1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2004 srvx Development Team
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version. Important limitations are
8 * listed in the COPYING file that accompanies this software.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, email srvx-maintainers@srvx.net.
23 #include "opserv.h" /* for gag_create(), opserv_bad_channel() */
32 #define NICKSERV_CONF_NAME "services/nickserv"
34 #define KEY_DISABLE_NICKS "disable_nicks"
35 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
36 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
37 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
38 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
39 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
40 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
41 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
42 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
43 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
44 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
45 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
46 #define KEY_MODOPER_LEVEL "modoper_level"
47 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
48 #define KEY_FLAG_LEVELS "flag_levels"
49 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
50 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
51 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
52 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
53 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
54 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
55 #define KEY_DICT_FILE "dict_file"
56 #define KEY_NICK "nick"
57 #define KEY_LANGUAGE "language"
58 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
59 #define KEY_AUTOGAG_DURATION "autogag_duration"
60 #define KEY_AUTH_POLICER "auth_policer"
61 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
62 #define KEY_EMAIL_ENABLED "email_enabled"
63 #define KEY_EMAIL_REQUIRED "email_required"
64 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
65 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
66 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
69 #define KEY_PASSWD "passwd"
70 #define KEY_NICKS "nicks"
71 #define KEY_MASKS "masks"
72 #define KEY_OPSERV_LEVEL "opserv_level"
73 #define KEY_FLAGS "flags"
74 #define KEY_REGISTER_ON "register"
75 #define KEY_LAST_SEEN "lastseen"
76 #define KEY_INFO "info"
77 #define KEY_USERLIST_STYLE "user_style"
78 #define KEY_SCREEN_WIDTH "screen_width"
79 #define KEY_LAST_AUTHED_HOST "last_authed_host"
80 #define KEY_LAST_QUIT_HOST "last_quit_host"
81 #define KEY_EMAIL_ADDR "email_addr"
82 #define KEY_COOKIE "cookie"
83 #define KEY_COOKIE_DATA "data"
84 #define KEY_COOKIE_TYPE "type"
85 #define KEY_COOKIE_EXPIRES "expires"
86 #define KEY_ACTIVATION "activation"
87 #define KEY_PASSWORD_CHANGE "password change"
88 #define KEY_EMAIL_CHANGE "email change"
89 #define KEY_ALLOWAUTH "allowauth"
90 #define KEY_EPITHET "epithet"
91 #define KEY_TABLE_WIDTH "table_width"
92 #define KEY_ANNOUNCEMENTS "announcements"
93 #define KEY_MAXLOGINS "maxlogins"
95 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
97 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
98 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
99 typedef OPTION_FUNC(option_func_t);
101 DEFINE_LIST(handle_info_list, struct handle_info*);
103 #define NICKSERV_MIN_PARMS(N) do { \
105 reply("MSG_MISSING_PARAMS", argv[0]); \
106 svccmd_send_help(user, nickserv, cmd); \
110 struct userNode *nickserv;
111 struct userList curr_helpers;
112 const char *handle_flags = HANDLE_FLAGS;
114 static struct module *nickserv_module;
115 static struct service *nickserv_service;
116 static struct log_type *NS_LOG;
117 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
118 static dict_t nickserv_id_dict; /* contains struct handle_info* */
119 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
120 static dict_t nickserv_opt_dict; /* contains option_func_t* */
121 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
122 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
123 static char handle_inverse_flags[256];
124 static unsigned int flag_access_levels[32];
125 static const struct message_entry msgtab[] = {
126 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
127 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
128 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
129 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
130 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
131 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
132 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
133 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
134 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
135 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
136 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
137 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
138 { "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." },
139 { "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." },
140 { "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." },
141 { "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." },
142 { "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." },
143 { "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." },
144 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
145 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
146 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
147 { "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." },
148 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
149 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
150 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
151 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
152 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
153 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
154 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
155 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
156 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
157 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
158 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
159 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
160 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
161 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
162 { "NSMSG_USE_AUTHCOOKIE", "Your hostmask is not valid for account $b%s$b. Please use the $bauthcookie$b command to grant yourself access. (/msg $S authcookie %s)" },
163 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
164 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
165 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
166 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
167 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
168 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
169 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
170 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
171 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
172 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
173 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
174 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
175 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
176 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
177 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
178 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
179 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
180 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
181 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
182 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
183 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
184 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
185 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
186 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
187 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
188 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
189 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
190 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
191 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
192 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
193 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
194 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
195 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
196 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
197 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
198 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
199 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
200 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
201 { "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)." },
202 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
203 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
204 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
205 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
206 { "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." },
207 { "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." },
208 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
209 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
210 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
211 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
212 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
213 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
214 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
215 { "NSMSG_PASS_SUCCESS", "Password changed." },
216 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
217 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
218 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
219 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
220 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
221 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
222 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
223 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
224 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
225 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
226 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
227 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
228 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
229 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
230 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
231 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
232 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
233 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
234 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
235 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
236 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
237 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
238 { "NSMSG_NO_ACCESS", "Access denied." },
239 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
240 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
241 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
242 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
243 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
244 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
245 { "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." },
246 { "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." },
247 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
248 { "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." },
249 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
250 { "NSMSG_SEARCH_MATCH", "Match: %s" },
251 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
252 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
253 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
254 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
255 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
256 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
257 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
258 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
259 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
260 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
261 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
262 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
263 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an announcements value." },
264 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
265 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
266 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
267 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
268 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
269 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
270 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
271 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
272 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
273 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
274 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
275 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
276 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
277 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
278 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
279 { "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." },
280 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
281 { "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." },
282 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
283 { "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." },
284 { "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." },
285 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
286 { "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 %1$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." },
287 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
288 { "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 %1$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." },
289 { "CHECKPASS_YES", "Yes." },
290 { "CHECKPASS_NO", "No." },
294 enum reclaim_action {
300 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
301 static void nickserv_reclaim_p(void *data);
304 unsigned int disable_nicks : 1;
305 unsigned int valid_handle_regex_set : 1;
306 unsigned int valid_nick_regex_set : 1;
307 unsigned int autogag_enabled : 1;
308 unsigned int email_enabled : 1;
309 unsigned int email_required : 1;
310 unsigned int default_hostmask : 1;
311 unsigned int warn_nick_owned : 1;
312 unsigned int warn_clone_auth : 1;
313 unsigned long nicks_per_handle;
314 unsigned long password_min_length;
315 unsigned long password_min_digits;
316 unsigned long password_min_upper;
317 unsigned long password_min_lower;
318 unsigned long db_backup_frequency;
319 unsigned long handle_expire_frequency;
320 unsigned long autogag_duration;
321 unsigned long email_visible_level;
322 unsigned long cookie_timeout;
323 unsigned long handle_expire_delay;
324 unsigned long nochan_handle_expire_delay;
325 unsigned long modoper_level;
326 unsigned long set_epithet_level;
327 unsigned long handles_per_email;
328 unsigned long email_search_level;
329 const char *network_name;
330 const char *titlehost_suffix;
331 regex_t valid_handle_regex;
332 regex_t valid_nick_regex;
333 dict_t weak_password_dict;
334 struct policer_params *auth_policer_params;
335 enum reclaim_action reclaim_action;
336 enum reclaim_action auto_reclaim_action;
337 unsigned long auto_reclaim_delay;
338 unsigned char default_maxlogins;
339 unsigned char hard_maxlogins;
342 /* We have 2^32 unique account IDs to use. */
343 unsigned long int highest_id = 0;
346 canonicalize_hostmask(char *mask)
348 char *out = mask, *temp;
349 if ((temp = strchr(mask, '!'))) {
351 while (*temp) *out++ = *temp++;
357 static struct handle_info *
358 register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
360 struct handle_info *hi;
362 #ifdef WITH_PROTOCOL_BAHAMUT
363 char id_base64[IDLEN + 1];
366 /* Assign a unique account ID to the account; note that 0 is
367 an invalid account ID. 1 is therefore the first account ID. */
369 id = 1 + highest_id++;
371 /* Note: highest_id is and must always be the highest ID. */
372 if(id > highest_id) {
376 inttobase64(id_base64, id, IDLEN);
378 /* Make sure an account with the same ID doesn't exist. If a
379 duplicate is found, log some details and assign a new one.
380 This should be impossible, but it never hurts to expect it. */
381 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
382 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
388 hi = calloc(1, sizeof(*hi));
389 hi->userlist_style = HI_DEFAULT_STYLE;
390 hi->announcements = '?';
391 hi->handle = strdup(handle);
392 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
394 dict_insert(nickserv_handle_dict, hi->handle, hi);
396 #ifdef WITH_PROTOCOL_BAHAMUT
398 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
405 register_nick(const char *nick, struct handle_info *owner)
407 struct nick_info *ni;
408 ni = malloc(sizeof(struct nick_info));
409 safestrncpy(ni->nick, nick, sizeof(ni->nick));
411 ni->next = owner->nicks;
413 dict_insert(nickserv_nick_dict, ni->nick, ni);
417 free_nick_info(void *vni)
419 struct nick_info *ni = vni;
424 delete_nick(struct nick_info *ni)
426 struct nick_info *last, *next;
427 struct userNode *user;
428 /* Check to see if we should mark a user as unregistered. */
429 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
430 user->modes &= ~FLAGS_REGNICK;
433 /* Remove ni from the nick_info linked list. */
434 if (ni == ni->owner->nicks) {
435 ni->owner->nicks = ni->next;
437 last = ni->owner->nicks;
443 last->next = next->next;
445 dict_remove(nickserv_nick_dict, ni->nick);
448 static unreg_func_t *unreg_func_list;
449 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
452 reg_unreg_func(unreg_func_t func)
454 if (unreg_func_used == unreg_func_size) {
455 if (unreg_func_size) {
456 unreg_func_size <<= 1;
457 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
460 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
463 unreg_func_list[unreg_func_used++] = func;
467 nickserv_free_cookie(void *data)
469 struct handle_cookie *cookie = data;
470 if (cookie->hi) cookie->hi->cookie = NULL;
471 if (cookie->data) free(cookie->data);
476 free_handle_info(void *vhi)
478 struct handle_info *hi = vhi;
480 #ifdef WITH_PROTOCOL_BAHAMUT
483 inttobase64(id, hi->id, IDLEN);
484 dict_remove(nickserv_id_dict, id);
487 free_string_list(hi->masks);
491 delete_nick(hi->nicks);
495 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
496 nickserv_free_cookie(hi->cookie);
498 if (hi->email_addr) {
499 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
500 handle_info_list_remove(hil, hi);
502 dict_remove(nickserv_email_dict, hi->email_addr);
507 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
510 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
514 for (n=0; n<unreg_func_used; n++)
515 unreg_func_list[n](notify, hi);
517 set_user_handle_info(hi->users, NULL, 0);
519 if (nickserv_conf.disable_nicks)
520 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
522 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
524 dict_remove(nickserv_handle_dict, hi->handle);
528 get_handle_info(const char *handle)
530 return dict_find(nickserv_handle_dict, handle, 0);
534 get_nick_info(const char *nick)
536 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
540 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
545 for (nn=0; nn<channel->members.used; ++nn) {
546 mn = channel->members.list[nn];
547 if ((mn->user != except) && (mn->user->handle_info == handle))
554 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
555 if (!user->handle_info) {
557 send_message(user, bot, "MSG_AUTHENTICATE");
561 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
563 send_message(user, bot, "NSMSG_NO_ACCESS");
567 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
569 send_message(user, bot, "MSG_OPER_SUSPENDED");
573 if (user->handle_info->opserv_level < min_level) {
575 send_message(user, bot, "NSMSG_NO_ACCESS");
583 is_valid_handle(const char *handle)
585 struct userNode *user;
586 /* cant register a juped nick/service nick as handle, to prevent confusion */
587 user = GetUserH(handle);
588 if (user && IsLocal(user))
590 /* check against maximum length */
591 if (strlen(handle) > NICKSERV_HANDLE_LEN)
593 /* for consistency, only allow account names that could be nicks */
594 if (!is_valid_nick(handle))
596 /* disallow account names that look like bad words */
597 if (opserv_bad_channel(handle))
599 /* test either regex or containing all valid chars */
600 if (nickserv_conf.valid_handle_regex_set) {
601 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
604 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
605 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
609 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
614 is_registerable_nick(const char *nick)
616 /* make sure it could be used as an account name */
617 if (!is_valid_handle(nick))
620 if (strlen(nick) > NICKLEN)
622 /* test either regex or as valid handle */
623 if (nickserv_conf.valid_nick_regex_set) {
624 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
627 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
628 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
636 is_valid_email_addr(const char *email)
638 return strchr(email, '@') != NULL;
642 visible_email_addr(struct userNode *user, struct handle_info *hi)
644 if (hi->email_addr) {
645 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
646 return hi->email_addr;
656 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
658 struct handle_info *hi;
659 struct userNode *target;
663 if (!(hi = get_handle_info(++name))) {
664 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
669 if (!(target = GetUserH(name))) {
670 send_message(user, service, "MSG_NICK_UNKNOWN", name);
673 if (IsLocal(target)) {
674 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
677 if (!(hi = target->handle_info)) {
678 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
686 oper_outranks(struct userNode *user, struct handle_info *hi) {
687 if (user->handle_info->opserv_level > hi->opserv_level)
689 if (user->handle_info->opserv_level == hi->opserv_level) {
690 if ((user->handle_info->opserv_level == 1000)
691 || (user->handle_info == hi)
692 || ((user->handle_info->opserv_level == 0)
693 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
694 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
698 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
702 static struct handle_info *
703 get_victim_oper(struct userNode *user, const char *target)
705 struct handle_info *hi;
706 if (!(hi = smart_get_handle_info(nickserv, user, target)))
708 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
709 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
712 return oper_outranks(user, hi) ? hi : NULL;
716 valid_user_for(struct userNode *user, struct handle_info *hi)
720 /* If no hostmasks on the account, allow it. */
721 if (!hi->masks->used)
723 /* If any hostmask matches, allow it. */
724 for (ii=0; ii<hi->masks->used; ii++)
725 if (user_matches_glob(user, hi->masks->list[ii], 0))
727 /* If they are allowauthed to this account, allow it (removing the aa). */
728 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
729 dict_remove(nickserv_allow_auth_dict, user->nick);
732 /* The user is not allowed to use this account. */
737 is_secure_password(const char *handle, const char *pass, struct userNode *user)
740 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
742 if (len < nickserv_conf.password_min_length) {
744 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
747 if (!irccasecmp(pass, handle)) {
749 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
752 dict_find(nickserv_conf.weak_password_dict, pass, &i);
755 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
758 for (i=0; i<len; i++) {
759 if (isdigit(pass[i]))
761 if (isupper(pass[i]))
763 if (islower(pass[i]))
766 if ((cnt_lower < nickserv_conf.password_min_lower)
767 || (cnt_upper < nickserv_conf.password_min_upper)
768 || (cnt_digits < nickserv_conf.password_min_digits)) {
770 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
776 static auth_func_t *auth_func_list;
777 static unsigned int auth_func_size = 0, auth_func_used = 0;
780 reg_auth_func(auth_func_t func)
782 if (auth_func_used == auth_func_size) {
783 if (auth_func_size) {
784 auth_func_size <<= 1;
785 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
788 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
791 auth_func_list[auth_func_used++] = func;
794 static handle_rename_func_t *rf_list;
795 static unsigned int rf_list_size, rf_list_used;
798 reg_handle_rename_func(handle_rename_func_t func)
800 if (rf_list_used == rf_list_size) {
803 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
806 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
809 rf_list[rf_list_used++] = func;
813 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
816 struct handle_info *old_info;
818 /* This can happen if somebody uses COOKIE while authed, or if
819 * they re-auth to their current handle (which is silly, but users
821 if (user->handle_info == hi)
824 if (user->handle_info) {
825 struct userNode *other;
828 userList_remove(&curr_helpers, user);
830 /* remove from next_authed linked list */
831 if (user->handle_info->users == user) {
832 user->handle_info->users = user->next_authed;
834 for (other = user->handle_info->users;
835 other->next_authed != user;
836 other = other->next_authed) ;
837 other->next_authed = user->next_authed;
839 /* if nobody left on old handle, and they're not an oper, remove !god */
840 if (!user->handle_info->users && !user->handle_info->opserv_level)
841 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
842 /* record them as being last seen at this time */
843 user->handle_info->lastseen = now;
844 /* and record their hostmask */
845 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
847 old_info = user->handle_info;
848 user->handle_info = hi;
849 if (hi && !hi->users && !hi->opserv_level)
850 HANDLE_CLEAR_FLAG(hi, HELPING);
851 for (n=0; n<auth_func_used; n++)
852 auth_func_list[n](user, old_info);
854 struct nick_info *ni;
856 HANDLE_CLEAR_FLAG(hi, FROZEN);
857 if (nickserv_conf.warn_clone_auth) {
858 struct userNode *other;
859 for (other = hi->users; other; other = other->next_authed)
860 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
862 user->next_authed = hi->users;
866 userList_append(&curr_helpers, user);
869 #ifdef WITH_PROTOCOL_BAHAMUT
870 /* Stamp users with their account ID. */
872 inttobase64(id, hi->id, IDLEN);
873 #elif WITH_PROTOCOL_P10
874 /* Stamp users with their account name. */
875 char *id = hi->handle;
877 const char *id = "???";
879 if (!nickserv_conf.disable_nicks) {
880 struct nick_info *ni;
881 for (ni = hi->nicks; ni; ni = ni->next) {
882 if (!irccasecmp(user->nick, ni->nick)) {
883 user->modes |= FLAGS_REGNICK;
891 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
892 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
894 /* We cannot clear the user's account ID, unfortunately. */
895 user->next_authed = NULL;
899 static struct handle_info*
900 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
902 struct handle_info *hi;
903 struct nick_info *ni;
904 char crypted[MD5_CRYPT_LENGTH];
906 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
907 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
911 if (!is_secure_password(handle, passwd, user))
914 cryptpass(passwd, crypted);
915 hi = register_handle(handle, crypted, 0);
916 hi->masks = alloc_string_list(1);
918 hi->registered = now;
920 hi->flags = HI_DEFAULT_FLAGS;
921 if (settee && !no_auth)
922 set_user_handle_info(settee, hi, 1);
925 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
926 else if (nickserv_conf.disable_nicks)
927 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
928 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
929 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
931 register_nick(user->nick, hi);
932 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
934 if (settee && (user != settee))
935 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
940 nickserv_bake_cookie(struct handle_cookie *cookie)
942 cookie->hi->cookie = cookie;
943 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
947 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
949 struct handle_cookie *cookie;
950 char subject[128], body[4096], *misc;
951 const char *netname, *fmt;
955 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
959 cookie = calloc(1, sizeof(*cookie));
962 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
963 cookie->expires = now + nickserv_conf.cookie_timeout;
964 inttobase64(cookie->cookie, rand(), 5);
965 inttobase64(cookie->cookie+5, rand(), 5);
967 netname = nickserv_conf.network_name;
970 switch (cookie->type) {
972 hi->passwd[0] = 0; /* invalidate password */
973 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
974 fmt = user_find_message(user, "NSEMAIL_ACTIVATION_SUBJECT");
975 snprintf(subject, sizeof(subject), fmt, netname);
976 fmt = user_find_message(user, "NSEMAIL_ACTIVATION_BODY");
977 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
980 case PASSWORD_CHANGE:
981 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
982 fmt = user_find_message(user, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
983 snprintf(subject, sizeof(subject), fmt, netname);
984 fmt = user_find_message(user, "NSEMAIL_PASSWORD_CHANGE_BODY");
985 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
988 misc = hi->email_addr;
989 hi->email_addr = cookie->data;
991 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
992 fmt = user_find_message(user, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
993 snprintf(subject, sizeof(subject), fmt, netname);
994 fmt = user_find_message(user, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
995 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
996 sendmail(nickserv, hi, subject, body, 1);
997 fmt = user_find_message(user, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
998 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1000 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1001 fmt = user_find_message(user, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1002 snprintf(subject, sizeof(subject), fmt, netname);
1003 fmt = user_find_message(user, "NSEMAIL_EMAIL_VERIFY_BODY");
1004 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1005 sendmail(nickserv, hi, subject, body, 1);
1008 hi->email_addr = misc;
1011 fmt = user_find_message(user, "NSEMAIL_ALLOWAUTH_SUBJECT");
1012 snprintf(subject, sizeof(subject), fmt, netname);
1013 fmt = user_find_message(user, "NSEMAIL_ALLOWAUTH_BODY");
1014 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1017 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1021 sendmail(nickserv, hi, subject, body, first_time);
1022 nickserv_bake_cookie(cookie);
1026 nickserv_eat_cookie(struct handle_cookie *cookie)
1028 cookie->hi->cookie = NULL;
1029 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1030 nickserv_free_cookie(cookie);
1034 nickserv_free_email_addr(void *data)
1036 handle_info_list_clean(data);
1041 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1043 struct handle_info_list *hil;
1044 /* Remove from old handle_info_list ... */
1045 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1046 handle_info_list_remove(hil, hi);
1047 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1048 hi->email_addr = NULL;
1050 /* Add to the new list.. */
1051 if (new_email_addr) {
1052 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1053 hil = calloc(1, sizeof(*hil));
1054 hil->tag = strdup(new_email_addr);
1055 handle_info_list_init(hil);
1056 dict_insert(nickserv_email_dict, hil->tag, hil);
1058 handle_info_list_append(hil, hi);
1059 hi->email_addr = hil->tag;
1063 static NICKSERV_FUNC(cmd_register)
1065 struct handle_info *hi;
1066 const char *email_addr, *password;
1069 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1070 /* Require the first handle registered to belong to someone +o. */
1071 reply("NSMSG_REQUIRE_OPER");
1075 if (user->handle_info) {
1076 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1080 if (IsStamped(user)) {
1081 /* Unauthenticated users might still have been stamped
1082 previously and could therefore have a hidden host;
1083 do not allow them to register a new account. */
1084 reply("NSMSG_STAMPED_REGISTER");
1088 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1090 if (!is_valid_handle(argv[1])) {
1091 reply("NSMSG_BAD_HANDLE", argv[1]);
1095 if ((argc >= 4) && nickserv_conf.email_enabled) {
1096 struct handle_info_list *hil;
1099 /* Remember email address. */
1100 email_addr = argv[3];
1102 /* Check that the email address looks valid.. */
1103 if (!is_valid_email_addr(email_addr)) {
1104 reply("NSMSG_BAD_EMAIL_ADDR");
1108 /* .. and that we are allowed to send to it. */
1109 if ((str = sendmail_prohibited_address(email_addr))) {
1110 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1114 /* If we do email verify, make sure we don't spam the address. */
1115 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1117 for (nn=0; nn<hil->used; nn++) {
1118 if (hil->list[nn]->cookie) {
1119 reply("NSMSG_EMAIL_UNACTIVATED");
1123 if (hil->used >= nickserv_conf.handles_per_email) {
1124 reply("NSMSG_EMAIL_OVERUSED");
1137 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1139 /* Add any masks they should get. */
1140 if (nickserv_conf.default_hostmask) {
1141 string_list_append(hi->masks, strdup("*@*"));
1143 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1144 if (user->ip.s_addr && user->hostname[strspn(user->hostname, "0123456789.")])
1145 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1148 /* If they're the first to register, give them level 1000. */
1149 if (dict_size(nickserv_handle_dict) == 1) {
1150 hi->opserv_level = 1000;
1151 reply("NSMSG_ROOT_HANDLE", argv[1]);
1154 /* Set their email address. */
1156 nickserv_set_email_addr(hi, email_addr);
1158 /* If they need to do email verification, tell them. */
1160 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1165 static NICKSERV_FUNC(cmd_oregister)
1168 struct userNode *settee;
1169 struct handle_info *hi;
1171 NICKSERV_MIN_PARMS(4);
1173 if (!is_valid_handle(argv[1])) {
1174 reply("NSMSG_BAD_HANDLE", argv[1]);
1178 if (strchr(argv[3], '@')) {
1179 mask = canonicalize_hostmask(strdup(argv[3]));
1181 settee = GetUserH(argv[4]);
1183 reply("MSG_NICK_UNKNOWN", argv[4]);
1190 } else if ((settee = GetUserH(argv[3]))) {
1191 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1193 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1196 if (settee && settee->handle_info) {
1197 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1201 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1205 string_list_append(hi->masks, mask);
1209 static NICKSERV_FUNC(cmd_handleinfo)
1212 unsigned int i, pos=0, herelen;
1213 struct userNode *target, *next_un;
1214 struct handle_info *hi;
1215 const char *nsmsg_none;
1218 if (!(hi = user->handle_info)) {
1219 reply("NSMSG_MUST_AUTH");
1222 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1226 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1227 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1228 #ifdef WITH_PROTOCOL_BAHAMUT
1229 reply("NSMSG_HANDLEINFO_ID", hi->id);
1231 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1234 intervalString(buff, now - hi->lastseen);
1235 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1237 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1240 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1241 if (HANDLE_FLAGGED(hi, FROZEN))
1242 reply("NSMSG_HANDLEINFO_VACATION");
1244 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1245 struct do_not_register *dnr;
1246 if ((dnr = chanserv_is_dnr(NULL, hi)))
1247 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1248 if (!oper_outranks(user, hi))
1250 } else if (hi != user->handle_info)
1253 if (nickserv_conf.email_enabled)
1254 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1258 switch (hi->cookie->type) {
1259 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1260 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1261 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1262 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1263 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1269 unsigned long flen = 1;
1270 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1272 for (i=0, flen=1; handle_flags[i]; i++)
1273 if (hi->flags & 1 << i)
1274 flags[flen++] = handle_flags[i];
1276 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1278 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1281 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1282 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1283 || (hi->opserv_level > 0)) {
1284 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1287 if (hi->last_quit_host[0])
1288 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1290 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1292 if (nickserv_conf.disable_nicks) {
1293 /* nicks disabled; don't show anything about registered nicks */
1294 } else if (hi->nicks) {
1295 struct nick_info *ni, *next_ni;
1296 for (ni = hi->nicks; ni; ni = next_ni) {
1297 herelen = strlen(ni->nick);
1298 if (pos + herelen + 1 > ArrayLength(buff)) {
1300 goto print_nicks_buff;
1304 memcpy(buff+pos, ni->nick, herelen);
1305 pos += herelen; buff[pos++] = ' ';
1309 reply("NSMSG_HANDLEINFO_NICKS", buff);
1314 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1317 if (hi->masks->used) {
1318 for (i=0; i < hi->masks->used; i++) {
1319 herelen = strlen(hi->masks->list[i]);
1320 if (pos + herelen + 1 > ArrayLength(buff)) {
1322 goto print_mask_buff;
1324 memcpy(buff+pos, hi->masks->list[i], herelen);
1325 pos += herelen; buff[pos++] = ' ';
1326 if (i+1 == hi->masks->used) {
1329 reply("NSMSG_HANDLEINFO_MASKS", buff);
1334 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1338 struct userData *channel, *next;
1341 for (channel = hi->channels; channel; channel = next) {
1342 next = channel->u_next;
1343 name = channel->channel->channel->name;
1344 herelen = strlen(name);
1345 if (pos + herelen + 7 > ArrayLength(buff)) {
1347 goto print_chans_buff;
1349 if (IsUserSuspended(channel))
1351 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1355 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1360 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1363 for (target = hi->users; target; target = next_un) {
1364 herelen = strlen(target->nick);
1365 if (pos + herelen + 1 > ArrayLength(buff)) {
1367 goto print_cnick_buff;
1369 next_un = target->next_authed;
1371 memcpy(buff+pos, target->nick, herelen);
1372 pos += herelen; buff[pos++] = ' ';
1376 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1384 static NICKSERV_FUNC(cmd_userinfo)
1386 struct userNode *target;
1388 NICKSERV_MIN_PARMS(2);
1389 if (!(target = GetUserH(argv[1]))) {
1390 reply("MSG_NICK_UNKNOWN", argv[1]);
1393 if (target->handle_info)
1394 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1396 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1400 static NICKSERV_FUNC(cmd_nickinfo)
1402 struct nick_info *ni;
1404 NICKSERV_MIN_PARMS(2);
1405 if (!(ni = get_nick_info(argv[1]))) {
1406 reply("MSG_NICK_UNKNOWN", argv[1]);
1409 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1413 static NICKSERV_FUNC(cmd_rename_handle)
1415 struct handle_info *hi;
1416 char msgbuf[MAXLEN], *old_handle;
1419 NICKSERV_MIN_PARMS(3);
1420 if (!(hi = get_victim_oper(user, argv[1])))
1422 if (!is_valid_handle(argv[2])) {
1423 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1426 if (get_handle_info(argv[2])) {
1427 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1431 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1432 hi->handle = strdup(argv[2]);
1433 dict_insert(nickserv_handle_dict, hi->handle, hi);
1434 for (nn=0; nn<rf_list_used; nn++)
1435 rf_list[nn](hi, old_handle);
1436 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1437 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1438 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1443 static failpw_func_t *failpw_func_list;
1444 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1447 reg_failpw_func(failpw_func_t func)
1449 if (failpw_func_used == failpw_func_size) {
1450 if (failpw_func_size) {
1451 failpw_func_size <<= 1;
1452 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1454 failpw_func_size = 8;
1455 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1458 failpw_func_list[failpw_func_used++] = func;
1461 static NICKSERV_FUNC(cmd_auth)
1463 int pw_arg, used, maxlogins;
1464 struct handle_info *hi;
1466 struct userNode *other;
1468 if (user->handle_info) {
1469 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1472 if (IsStamped(user)) {
1473 /* Unauthenticated users might still have been stamped
1474 previously and could therefore have a hidden host;
1475 do not allow them to authenticate. */
1476 reply("NSMSG_STAMPED_AUTH");
1480 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1482 } else if (argc == 2) {
1483 if (nickserv_conf.disable_nicks) {
1484 if (!(hi = get_handle_info(user->nick))) {
1485 reply("NSMSG_HANDLE_NOT_FOUND");
1489 /* try to look up their handle from their nick */
1490 struct nick_info *ni;
1491 ni = get_nick_info(user->nick);
1493 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1500 reply("MSG_MISSING_PARAMS", argv[0]);
1501 svccmd_send_help(user, nickserv, cmd);
1505 reply("NSMSG_HANDLE_NOT_FOUND");
1508 passwd = argv[pw_arg];
1509 if (!valid_user_for(user, hi)) {
1510 if (hi->email_addr && nickserv_conf.email_enabled)
1511 reply("NSMSG_USE_AUTHCOOKIE", hi->handle, hi->handle);
1513 reply("NSMSG_HOSTMASK_INVALID", hi->handle);
1514 argv[pw_arg] = "BADMASK";
1517 if (!checkpass(passwd, hi->passwd)) {
1519 reply("NSMSG_PASSWORD_INVALID");
1520 argv[pw_arg] = "BADPASS";
1521 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1522 if (nickserv_conf.autogag_enabled) {
1523 if (!user->auth_policer.params) {
1524 user->auth_policer.last_req = now;
1525 user->auth_policer.params = nickserv_conf.auth_policer_params;
1527 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1529 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1530 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1531 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1533 argv[pw_arg] = "GAGGED";
1538 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1539 reply("NSMSG_HANDLE_SUSPENDED");
1540 argv[pw_arg] = "SUSPENDED";
1543 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1544 for (used = 0, other = hi->users; other; other = other->next_authed) {
1545 if (++used >= maxlogins) {
1546 reply("NSMSG_MAX_LOGINS", maxlogins);
1547 argv[pw_arg] = "MAXLOGINS";
1552 if (nickserv_conf.email_required && !hi->email_addr)
1553 reply("NSMSG_PLEASE_SET_EMAIL");
1554 if (!is_secure_password(hi->handle, passwd, NULL))
1555 reply("NSMSG_WEAK_PASSWORD");
1556 if (hi->passwd[0] != '$')
1557 cryptpass(passwd, hi->passwd);
1559 reply("NSMSG_AUTH_SUCCESS");
1560 argv[pw_arg] = "****";
1561 set_user_handle_info(user, hi, 1);
1565 static allowauth_func_t *allowauth_func_list;
1566 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1569 reg_allowauth_func(allowauth_func_t func)
1571 if (allowauth_func_used == allowauth_func_size) {
1572 if (allowauth_func_size) {
1573 allowauth_func_size <<= 1;
1574 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1576 allowauth_func_size = 8;
1577 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1580 allowauth_func_list[allowauth_func_used++] = func;
1583 static NICKSERV_FUNC(cmd_allowauth)
1585 struct userNode *target;
1586 struct handle_info *hi;
1589 NICKSERV_MIN_PARMS(2);
1590 if (!(target = GetUserH(argv[1]))) {
1591 reply("MSG_NICK_UNKNOWN", argv[1]);
1594 if (target->handle_info) {
1595 reply("NSMSG_USER_PREV_AUTH", target->nick);
1598 if (IsStamped(target)) {
1599 /* Unauthenticated users might still have been stamped
1600 previously and could therefore have a hidden host;
1601 do not allow them to authenticate to an account. */
1602 send_message(target, nickserv, "NSMSG_USER_PREV_STAMP", target->nick);
1607 else if (!(hi = get_handle_info(argv[2]))) {
1608 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1612 if (hi->opserv_level > user->handle_info->opserv_level) {
1613 reply("MSG_USER_OUTRANKED", hi->handle);
1616 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1617 || (hi->opserv_level > 0))
1618 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1619 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1622 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1623 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1624 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1625 if (nickserv_conf.email_enabled)
1626 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1628 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1629 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1631 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1633 for (n=0; n<allowauth_func_used; n++)
1634 allowauth_func_list[n](user, target, hi);
1638 static NICKSERV_FUNC(cmd_authcookie)
1640 struct handle_info *hi;
1642 NICKSERV_MIN_PARMS(2);
1643 if (user->handle_info) {
1644 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1647 if (IsStamped(user)) {
1648 /* Unauthenticated users might still have been stamped
1649 previously and could therefore have a hidden host;
1650 do not allow them to authenticate to an account. */
1651 reply("NSMSG_STAMPED_AUTHCOOKIE");
1654 if (!(hi = get_handle_info(argv[1]))) {
1655 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1658 if (!hi->email_addr) {
1659 reply("MSG_SET_EMAIL_ADDR");
1662 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1663 reply("NSMSG_USE_COOKIE_AUTH");
1667 static NICKSERV_FUNC(cmd_delcookie)
1669 struct handle_info *hi;
1671 hi = user->handle_info;
1673 reply("NSMSG_NO_COOKIE");
1676 switch (hi->cookie->type) {
1679 reply("NSMSG_MUST_TIME_OUT");
1682 nickserv_eat_cookie(hi->cookie);
1683 reply("NSMSG_ATE_COOKIE");
1689 static NICKSERV_FUNC(cmd_resetpass)
1691 struct handle_info *hi;
1692 char crypted[MD5_CRYPT_LENGTH];
1694 NICKSERV_MIN_PARMS(3);
1695 if (user->handle_info) {
1696 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1699 if (IsStamped(user)) {
1700 /* Unauthenticated users might still have been stamped
1701 previously and could therefore have a hidden host;
1702 do not allow them to activate an account. */
1703 reply("NSMSG_STAMPED_RESETPASS");
1706 if (!(hi = get_handle_info(argv[1]))) {
1707 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1710 if (!hi->email_addr) {
1711 reply("MSG_SET_EMAIL_ADDR");
1714 cryptpass(argv[2], crypted);
1716 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1720 static NICKSERV_FUNC(cmd_cookie)
1722 struct handle_info *hi;
1725 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1728 NICKSERV_MIN_PARMS(3);
1729 if (!(hi = get_handle_info(argv[1]))) {
1730 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1736 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1737 reply("NSMSG_HANDLE_SUSPENDED");
1742 reply("NSMSG_NO_COOKIE");
1746 /* Check validity of operation before comparing cookie to
1747 * prohibit guessing by authed users. */
1748 if (user->handle_info
1749 && (hi->cookie->type != EMAIL_CHANGE)
1750 && (hi->cookie->type != PASSWORD_CHANGE)) {
1751 reply("NSMSG_CANNOT_COOKIE");
1755 if (strcmp(cookie, hi->cookie->cookie)) {
1756 reply("NSMSG_BAD_COOKIE");
1760 switch (hi->cookie->type) {
1762 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1763 set_user_handle_info(user, hi, 1);
1764 reply("NSMSG_HANDLE_ACTIVATED");
1766 case PASSWORD_CHANGE:
1767 set_user_handle_info(user, hi, 1);
1768 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1769 reply("NSMSG_PASSWORD_CHANGED");
1772 nickserv_set_email_addr(hi, hi->cookie->data);
1773 reply("NSMSG_EMAIL_CHANGED");
1776 set_user_handle_info(user, hi, 1);
1777 reply("NSMSG_AUTH_SUCCESS");
1780 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1781 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1785 nickserv_eat_cookie(hi->cookie);
1790 static NICKSERV_FUNC(cmd_oregnick) {
1792 struct handle_info *target;
1793 struct nick_info *ni;
1795 NICKSERV_MIN_PARMS(3);
1796 if (!(target = modcmd_get_handle_info(user, argv[1])))
1799 if (!is_registerable_nick(nick)) {
1800 reply("NSMSG_BAD_NICK", nick);
1803 ni = dict_find(nickserv_nick_dict, nick, NULL);
1805 reply("NSMSG_NICK_EXISTS", nick);
1808 register_nick(nick, target);
1809 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1813 static NICKSERV_FUNC(cmd_regnick) {
1815 struct nick_info *ni;
1817 if (!is_registerable_nick(user->nick)) {
1818 reply("NSMSG_BAD_NICK", user->nick);
1821 /* count their nicks, see if it's too many */
1822 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1823 if (n >= nickserv_conf.nicks_per_handle) {
1824 reply("NSMSG_TOO_MANY_NICKS");
1827 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1829 reply("NSMSG_NICK_EXISTS", user->nick);
1832 register_nick(user->nick, user->handle_info);
1833 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1837 static NICKSERV_FUNC(cmd_pass)
1839 struct handle_info *hi;
1840 const char *old_pass, *new_pass;
1842 NICKSERV_MIN_PARMS(3);
1843 hi = user->handle_info;
1847 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1848 if (!checkpass(old_pass, hi->passwd)) {
1849 argv[1] = "BADPASS";
1850 reply("NSMSG_PASSWORD_INVALID");
1853 cryptpass(new_pass, hi->passwd);
1855 reply("NSMSG_PASS_SUCCESS");
1860 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1863 char *new_mask = canonicalize_hostmask(strdup(mask));
1864 for (i=0; i<hi->masks->used; i++) {
1865 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1866 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1871 string_list_append(hi->masks, new_mask);
1872 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
1876 static NICKSERV_FUNC(cmd_addmask)
1879 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1880 int res = nickserv_addmask(user, user->handle_info, mask);
1884 if (!is_gline(argv[1])) {
1885 reply("NSMSG_MASK_INVALID", argv[1]);
1888 return nickserv_addmask(user, user->handle_info, argv[1]);
1892 static NICKSERV_FUNC(cmd_oaddmask)
1894 struct handle_info *hi;
1896 NICKSERV_MIN_PARMS(3);
1897 if (!(hi = get_victim_oper(user, argv[1])))
1899 return nickserv_addmask(user, hi, argv[2]);
1903 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
1906 for (i=0; i<hi->masks->used; i++) {
1907 if (!strcmp(del_mask, hi->masks->list[i])) {
1908 char *old_mask = hi->masks->list[i];
1909 if (hi->masks->used == 1) {
1910 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
1913 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
1914 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
1919 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
1923 static NICKSERV_FUNC(cmd_delmask)
1925 NICKSERV_MIN_PARMS(2);
1926 return nickserv_delmask(user, user->handle_info, argv[1]);
1929 static NICKSERV_FUNC(cmd_odelmask)
1931 struct handle_info *hi;
1932 NICKSERV_MIN_PARMS(3);
1933 if (!(hi = get_victim_oper(user, argv[1])))
1935 return nickserv_delmask(user, hi, argv[2]);
1939 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
1940 unsigned int nn, add = 1, pos;
1941 unsigned long added, removed, flag;
1943 for (added=removed=nn=0; str[nn]; nn++) {
1945 case '+': add = 1; break;
1946 case '-': add = 0; break;
1948 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
1949 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
1952 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
1953 /* cheesy avoidance of looking up the flag name.. */
1954 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
1957 flag = 1 << (pos - 1);
1959 added |= flag, removed &= ~flag;
1961 removed |= flag, added &= ~flag;
1966 *premoved = removed;
1971 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
1973 unsigned long before, after, added, removed;
1974 struct userNode *uNode;
1976 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
1977 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
1979 hi->flags = (hi->flags | added) & ~removed;
1980 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
1982 /* Strip helping flag if they're only a support helper and not
1983 * currently in #support. */
1984 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
1985 struct channelList *schannels;
1987 schannels = chanserv_support_channels();
1988 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
1989 for (ii = 0; ii < schannels->used; ++ii)
1990 if (GetUserMode(schannels->list[ii], uNode))
1992 if (ii < schannels->used)
1996 HANDLE_CLEAR_FLAG(hi, HELPING);
1999 if (after && !before) {
2000 /* Add user to current helper list. */
2001 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2002 userList_append(&curr_helpers, uNode);
2003 } else if (!after && before) {
2004 /* Remove user from current helper list. */
2005 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2006 userList_remove(&curr_helpers, uNode);
2013 set_list(struct userNode *user, struct handle_info *hi, int override)
2017 char *set_display[] = {
2018 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2019 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS"
2022 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2024 /* Do this so options are presented in a consistent order. */
2025 for (i = 0; i < ArrayLength(set_display); ++i)
2026 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2027 opt(user, hi, override, 0, NULL);
2030 static NICKSERV_FUNC(cmd_set)
2032 struct handle_info *hi;
2035 hi = user->handle_info;
2037 set_list(user, hi, 0);
2040 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2041 reply("NSMSG_INVALID_OPTION", argv[1]);
2044 return opt(user, hi, 0, argc-1, argv+1);
2047 static NICKSERV_FUNC(cmd_oset)
2049 struct handle_info *hi;
2052 NICKSERV_MIN_PARMS(2);
2054 if (!(hi = get_victim_oper(user, argv[1])))
2058 set_list(user, hi, 0);
2062 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2063 reply("NSMSG_INVALID_OPTION", argv[2]);
2067 return opt(user, hi, 1, argc-2, argv+2);
2070 static OPTION_FUNC(opt_info)
2074 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2076 hi->infoline = NULL;
2078 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2082 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2083 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2087 static OPTION_FUNC(opt_width)
2090 unsigned int new_width = strtoul(argv[1], NULL, 0);
2091 hi->screen_width = new_width;
2094 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2095 hi->screen_width = MIN_LINE_SIZE;
2096 else if (hi->screen_width > MAX_LINE_SIZE)
2097 hi->screen_width = MAX_LINE_SIZE;
2099 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2103 static OPTION_FUNC(opt_tablewidth)
2106 unsigned int new_width = strtoul(argv[1], NULL, 0);
2107 hi->table_width = new_width;
2110 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2111 hi->table_width = MIN_LINE_SIZE;
2112 else if (hi->screen_width > MAX_LINE_SIZE)
2113 hi->table_width = MAX_LINE_SIZE;
2115 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2119 static OPTION_FUNC(opt_color)
2122 if (enabled_string(argv[1]))
2123 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2124 else if (disabled_string(argv[1]))
2125 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2127 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2132 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2136 static OPTION_FUNC(opt_privmsg)
2139 if (enabled_string(argv[1]))
2140 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2141 else if (disabled_string(argv[1]))
2142 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2144 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2149 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2153 static OPTION_FUNC(opt_style)
2158 if (!irccasecmp(argv[1], "Zoot"))
2159 hi->userlist_style = HI_STYLE_ZOOT;
2160 else if (!irccasecmp(argv[1], "def"))
2161 hi->userlist_style = HI_STYLE_DEF;
2164 switch (hi->userlist_style) {
2173 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2177 static OPTION_FUNC(opt_announcements)
2182 if (enabled_string(argv[1]))
2183 hi->announcements = 'y';
2184 else if (disabled_string(argv[1]))
2185 hi->announcements = 'n';
2186 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2187 hi->announcements = '?';
2189 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2194 switch (hi->announcements) {
2195 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2196 case 'n': choice = user_find_message(user, "MSG_ON"); break;
2197 case '?': choice = "default"; break;
2198 default: choice = "unknown"; break;
2200 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2204 static OPTION_FUNC(opt_password)
2207 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2212 cryptpass(argv[1], hi->passwd);
2214 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2218 static OPTION_FUNC(opt_flags)
2221 unsigned int ii, flen;
2224 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2229 nickserv_apply_flags(user, hi, argv[1]);
2231 for (ii = flen = 0; handle_flags[ii]; ii++)
2232 if (hi->flags & (1 << ii))
2233 flags[flen++] = handle_flags[ii];
2236 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2238 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2242 static OPTION_FUNC(opt_email)
2246 if (!is_valid_email_addr(argv[1])) {
2247 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2250 if ((str = sendmail_prohibited_address(argv[1]))) {
2251 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2254 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2255 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2257 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2259 nickserv_set_email_addr(hi, argv[1]);
2261 nickserv_eat_cookie(hi->cookie);
2262 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2265 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2269 static OPTION_FUNC(opt_maxlogins)
2273 maxlogins = strtoul(argv[1], NULL, 0);
2274 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2275 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2278 hi->maxlogins = maxlogins;
2280 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2281 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2285 static OPTION_FUNC(opt_language)
2287 struct language *lang;
2289 lang = language_find(argv[1]);
2290 if (irccasecmp(lang->name, argv[1]))
2291 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2292 hi->language = lang;
2294 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2299 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2300 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2302 if ((user->handle_info->opserv_level < target->opserv_level)
2303 || ((user->handle_info->opserv_level == target->opserv_level)
2304 && (user->handle_info->opserv_level < 1000))) {
2305 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2308 if ((user->handle_info->opserv_level < new_level)
2309 || ((user->handle_info->opserv_level == new_level)
2310 && (user->handle_info->opserv_level < 1000))) {
2311 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2314 if (user->handle_info == target) {
2315 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2318 if (target->opserv_level == new_level)
2320 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2321 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2322 target->opserv_level = new_level;
2326 static OPTION_FUNC(opt_level)
2331 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2335 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2336 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2340 static OPTION_FUNC(opt_epithet)
2343 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2347 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2348 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2351 if ((epithet[0] == '*') && !epithet[1])
2354 hi->epithet = strdup(epithet);
2358 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2360 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2364 static NICKSERV_FUNC(cmd_reclaim)
2366 struct handle_info *hi;
2367 struct nick_info *ni;
2368 struct userNode *victim;
2370 NICKSERV_MIN_PARMS(2);
2371 hi = user->handle_info;
2372 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2374 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2377 if (ni->owner != user->handle_info) {
2378 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2381 victim = GetUserH(ni->nick);
2383 reply("MSG_NICK_UNKNOWN", ni->nick);
2386 if (victim == user) {
2387 reply("NSMSG_NICK_USER_YOU");
2390 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2391 switch (nickserv_conf.reclaim_action) {
2392 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2393 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2394 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2395 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2400 static NICKSERV_FUNC(cmd_unregnick)
2403 struct handle_info *hi;
2404 struct nick_info *ni;
2406 hi = user->handle_info;
2407 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2408 ni = dict_find(nickserv_nick_dict, nick, NULL);
2410 reply("NSMSG_UNKNOWN_NICK", nick);
2413 if (hi != ni->owner) {
2414 reply("NSMSG_NOT_YOUR_NICK", nick);
2417 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2422 static NICKSERV_FUNC(cmd_ounregnick)
2424 struct nick_info *ni;
2426 NICKSERV_MIN_PARMS(2);
2427 if (!(ni = get_nick_info(argv[1]))) {
2428 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2431 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2432 reply("MSG_USER_OUTRANKED", ni->nick);
2435 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2440 static NICKSERV_FUNC(cmd_unregister)
2442 struct handle_info *hi;
2445 NICKSERV_MIN_PARMS(2);
2446 hi = user->handle_info;
2449 if (checkpass(passwd, hi->passwd)) {
2450 nickserv_unregister_handle(hi, user);
2453 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2454 reply("NSMSG_PASSWORD_INVALID");
2459 static NICKSERV_FUNC(cmd_ounregister)
2461 struct handle_info *hi;
2463 NICKSERV_MIN_PARMS(2);
2464 if (!(hi = get_victim_oper(user, argv[1])))
2466 nickserv_unregister_handle(hi, user);
2470 static NICKSERV_FUNC(cmd_status)
2472 if (nickserv_conf.disable_nicks) {
2473 reply("NSMSG_GLOBAL_STATS_NONICK",
2474 dict_size(nickserv_handle_dict));
2476 if (user->handle_info) {
2478 struct nick_info *ni;
2479 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2480 reply("NSMSG_HANDLE_STATS", cnt);
2482 reply("NSMSG_HANDLE_NONE");
2484 reply("NSMSG_GLOBAL_STATS",
2485 dict_size(nickserv_handle_dict),
2486 dict_size(nickserv_nick_dict));
2491 static NICKSERV_FUNC(cmd_ghost)
2493 struct userNode *target;
2494 char reason[MAXLEN];
2496 NICKSERV_MIN_PARMS(2);
2497 if (!(target = GetUserH(argv[1]))) {
2498 reply("MSG_NICK_UNKNOWN", argv[1]);
2501 if (target == user) {
2502 reply("NSMSG_CANNOT_GHOST_SELF");
2505 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2506 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2509 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2510 DelUser(target, nickserv, 1, reason);
2511 reply("NSMSG_GHOST_KILLED", argv[1]);
2515 static NICKSERV_FUNC(cmd_vacation)
2517 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2518 reply("NSMSG_ON_VACATION");
2523 nickserv_saxdb_write(struct saxdb_context *ctx) {
2525 struct handle_info *hi;
2528 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2530 #ifdef WITH_PROTOCOL_BAHAMUT
2533 saxdb_start_record(ctx, iter_key(it), 0);
2534 if (hi->announcements != '?') {
2535 flags[0] = hi->announcements;
2537 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2540 struct handle_cookie *cookie = hi->cookie;
2543 switch (cookie->type) {
2544 case ACTIVATION: type = KEY_ACTIVATION; break;
2545 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2546 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2547 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2548 default: type = NULL; break;
2551 saxdb_start_record(ctx, KEY_COOKIE, 0);
2552 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2553 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2555 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2556 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2557 saxdb_end_record(ctx);
2561 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2563 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2567 for (ii=flen=0; handle_flags[ii]; ++ii)
2568 if (hi->flags & (1 << ii))
2569 flags[flen++] = handle_flags[ii];
2571 saxdb_write_string(ctx, KEY_FLAGS, flags);
2573 #ifdef WITH_PROTOCOL_BAHAMUT
2574 saxdb_write_int(ctx, KEY_ID, hi->id);
2577 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2578 if (hi->last_quit_host[0])
2579 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2580 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2581 if (hi->masks->used)
2582 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2584 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2586 struct string_list *slist;
2587 struct nick_info *ni;
2589 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2590 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2591 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2595 if (hi->opserv_level)
2596 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2597 if (hi->language && (hi->language != lang_C))
2598 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2599 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2600 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2601 if (hi->screen_width)
2602 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2603 if (hi->table_width)
2604 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2605 flags[0] = hi->userlist_style;
2607 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2608 saxdb_end_record(ctx);
2613 static handle_merge_func_t *handle_merge_func_list;
2614 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2617 reg_handle_merge_func(handle_merge_func_t func)
2619 if (handle_merge_func_used == handle_merge_func_size) {
2620 if (handle_merge_func_size) {
2621 handle_merge_func_size <<= 1;
2622 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2624 handle_merge_func_size = 8;
2625 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2628 handle_merge_func_list[handle_merge_func_used++] = func;
2631 static NICKSERV_FUNC(cmd_merge)
2633 struct handle_info *hi_from, *hi_to;
2634 struct userNode *last_user;
2635 struct userData *cList, *cListNext;
2636 unsigned int ii, jj, n;
2637 char buffer[MAXLEN];
2639 NICKSERV_MIN_PARMS(3);
2641 if (!(hi_from = get_victim_oper(user, argv[1])))
2643 if (!(hi_to = get_victim_oper(user, argv[2])))
2645 if (hi_to == hi_from) {
2646 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2650 for (n=0; n<handle_merge_func_used; n++)
2651 handle_merge_func_list[n](user, hi_to, hi_from);
2653 /* Append "from" handle's nicks to "to" handle's nick list. */
2655 struct nick_info *last_ni;
2656 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2657 last_ni->next = hi_from->nicks;
2659 while (hi_from->nicks) {
2660 hi_from->nicks->owner = hi_to;
2661 hi_from->nicks = hi_from->nicks->next;
2664 /* Merge the hostmasks. */
2665 for (ii=0; ii<hi_from->masks->used; ii++) {
2666 char *mask = hi_from->masks->list[ii];
2667 for (jj=0; jj<hi_to->masks->used; jj++)
2668 if (match_ircglobs(hi_to->masks->list[jj], mask))
2670 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2671 string_list_append(hi_to->masks, strdup(mask));
2674 /* Merge the lists of authed users. */
2676 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2677 last_user->next_authed = hi_from->users;
2679 hi_to->users = hi_from->users;
2681 /* Repoint the old "from" handle's users. */
2682 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2683 last_user->handle_info = hi_to;
2685 hi_from->users = NULL;
2687 /* Merge channel userlists. */
2688 for (cList=hi_from->channels; cList; cList=cListNext) {
2689 struct userData *cList2;
2690 cListNext = cList->u_next;
2691 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2692 if (cList->channel == cList2->channel)
2694 log_module(NS_LOG, LOG_DEBUG, "Merging %s->%s@%s: before %p->%p->%-p, %p->%p->%p",
2695 hi_from->handle, hi_to->handle, cList->channel->channel->name,
2696 cList->u_prev, cList, cList->u_next,
2697 (cList2?cList2->u_prev:0), cList2, (cList2?cList2->u_next:0));
2698 if (cList2 && (cList2->access >= cList->access)) {
2699 /* keep cList2 in hi_to; remove cList from hi_from */
2700 log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList);
2701 del_channel_user(cList, 1);
2704 /* remove the lower-ranking cList2 from hi_to */
2705 log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList2);
2706 del_channel_user(cList2, 1);
2708 /* cList needs to be moved from hi_from to hi_to */
2709 cList->handle = hi_to;
2710 /* Remove from linked list for hi_from */
2711 assert(!cList->u_prev);
2712 hi_from->channels = cList->u_next;
2714 cList->u_next->u_prev = cList->u_prev;
2715 /* Add to linked list for hi_to */
2716 cList->u_prev = NULL;
2717 cList->u_next = hi_to->channels;
2718 if (hi_to->channels)
2719 hi_to->channels->u_prev = cList;
2720 hi_to->channels = cList;
2721 log_module(NS_LOG, LOG_DEBUG, "Now %p->%p->%p",
2722 cList->u_prev, cList, cList->u_next);
2726 /* Do they get an OpServ level promotion? */
2727 if (hi_from->opserv_level > hi_to->opserv_level)
2728 hi_to->opserv_level = hi_from->opserv_level;
2730 /* What about last seen time? */
2731 if (hi_from->lastseen > hi_to->lastseen)
2732 hi_to->lastseen = hi_from->lastseen;
2734 /* Notify of success. */
2735 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2736 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2737 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2739 /* Unregister the "from" handle. */
2740 nickserv_unregister_handle(hi_from, NULL);
2745 struct nickserv_discrim {
2746 unsigned int limit, min_level, max_level;
2747 unsigned long flags_on, flags_off;
2748 time_t min_registered, max_registered;
2750 enum { SUBSET, EXACT, SUPERSET } hostmask_type;
2751 const char *nickmask;
2752 const char *hostmask;
2753 const char *handlemask;
2754 const char *emailmask;
2757 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2759 struct discrim_apply_info {
2760 struct nickserv_discrim *discrim;
2761 discrim_search_func func;
2762 struct userNode *source;
2763 unsigned int matched;
2766 static struct nickserv_discrim *
2767 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2770 struct nickserv_discrim *discrim;
2772 discrim = malloc(sizeof(*discrim));
2773 memset(discrim, 0, sizeof(*discrim));
2774 discrim->min_level = 0;
2775 discrim->max_level = ~0;
2776 discrim->limit = 50;
2777 discrim->min_registered = 0;
2778 discrim->max_registered = INT_MAX;
2779 discrim->lastseen = now;
2781 for (i=0; i<argc; i++) {
2782 if (i == argc - 1) {
2783 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2786 if (!irccasecmp(argv[i], "limit")) {
2787 discrim->limit = atoi(argv[++i]);
2788 } else if (!irccasecmp(argv[i], "flags")) {
2789 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2790 } else if (!irccasecmp(argv[i], "registered")) {
2791 const char *cmp = argv[++i];
2792 if (cmp[0] == '<') {
2793 if (cmp[1] == '=') {
2794 discrim->min_registered = now - ParseInterval(cmp+2);
2796 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2798 } else if (cmp[0] == '=') {
2799 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2800 } else if (cmp[0] == '>') {
2801 if (cmp[1] == '=') {
2802 discrim->max_registered = now - ParseInterval(cmp+2);
2804 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2807 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2809 } else if (!irccasecmp(argv[i], "seen")) {
2810 discrim->lastseen = now - ParseInterval(argv[++i]);
2811 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2812 discrim->nickmask = argv[++i];
2813 } else if (!irccasecmp(argv[i], "hostmask")) {
2815 if (!irccasecmp(argv[i], "exact")) {
2816 if (i == argc - 1) {
2817 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2820 discrim->hostmask_type = EXACT;
2821 } else if (!irccasecmp(argv[i], "subset")) {
2822 if (i == argc - 1) {
2823 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2826 discrim->hostmask_type = SUBSET;
2827 } else if (!irccasecmp(argv[i], "superset")) {
2828 if (i == argc - 1) {
2829 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2832 discrim->hostmask_type = SUPERSET;
2835 discrim->hostmask_type = SUPERSET;
2837 discrim->hostmask = argv[++i];
2838 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2839 if (!irccasecmp(argv[++i], "*")) {
2840 discrim->handlemask = 0;
2842 discrim->handlemask = argv[i];
2844 } else if (!irccasecmp(argv[i], "email")) {
2845 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2846 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2848 } else if (!irccasecmp(argv[++i], "*")) {
2849 discrim->emailmask = 0;
2851 discrim->emailmask = argv[i];
2853 } else if (!irccasecmp(argv[i], "access")) {
2854 const char *cmp = argv[++i];
2855 if (cmp[0] == '<') {
2856 if (discrim->min_level == 0) discrim->min_level = 1;
2857 if (cmp[1] == '=') {
2858 discrim->max_level = strtoul(cmp+2, NULL, 0);
2860 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
2862 } else if (cmp[0] == '=') {
2863 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
2864 } else if (cmp[0] == '>') {
2865 if (cmp[1] == '=') {
2866 discrim->min_level = strtoul(cmp+2, NULL, 0);
2868 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
2871 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2874 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
2885 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
2887 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
2888 || (discrim->flags_off & hi->flags)
2889 || (discrim->min_registered > hi->registered)
2890 || (discrim->max_registered < hi->registered)
2891 || (discrim->lastseen < (hi->users?now:hi->lastseen))
2892 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
2893 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
2894 || (discrim->min_level > hi->opserv_level)
2895 || (discrim->max_level < hi->opserv_level)) {
2898 if (discrim->hostmask) {
2900 for (i=0; i<hi->masks->used; i++) {
2901 const char *mask = hi->masks->list[i];
2902 if ((discrim->hostmask_type == SUBSET)
2903 && (match_ircglobs(discrim->hostmask, mask))) break;
2904 else if ((discrim->hostmask_type == EXACT)
2905 && !irccasecmp(discrim->hostmask, mask)) break;
2906 else if ((discrim->hostmask_type == SUPERSET)
2907 && (match_ircglobs(mask, discrim->hostmask))) break;
2909 if (i==hi->masks->used) return 0;
2911 if (discrim->nickmask) {
2912 struct nick_info *nick = hi->nicks;
2914 if (match_ircglob(nick->nick, discrim->nickmask)) break;
2917 if (!nick) return 0;
2923 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
2925 dict_iterator_t it, next;
2926 unsigned int matched;
2928 for (it = dict_first(nickserv_handle_dict), matched = 0;
2929 it && (matched < discrim->limit);
2931 next = iter_next(it);
2932 if (nickserv_discrim_match(discrim, iter_data(it))) {
2933 dsf(source, iter_data(it));
2941 search_print_func(struct userNode *source, struct handle_info *match)
2943 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
2947 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
2952 search_unregister_func (struct userNode *source, struct handle_info *match)
2954 if (oper_has_access(source, nickserv, match->opserv_level, 0))
2955 nickserv_unregister_handle(match, source);
2959 nickserv_sort_accounts_by_access(const void *a, const void *b)
2961 const struct handle_info *hi_a = *(const struct handle_info**)a;
2962 const struct handle_info *hi_b = *(const struct handle_info**)b;
2963 if (hi_a->opserv_level != hi_b->opserv_level)
2964 return hi_b->opserv_level - hi_a->opserv_level;
2965 return irccasecmp(hi_a->handle, hi_b->handle);
2969 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
2971 struct handle_info_list hil;
2972 struct helpfile_table tbl;
2977 memset(&hil, 0, sizeof(hil));
2978 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2979 struct handle_info *hi = iter_data(it);
2980 if (hi->opserv_level)
2981 handle_info_list_append(&hil, hi);
2983 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
2984 tbl.length = hil.used + 1;
2986 tbl.flags = TABLE_NO_FREE;
2987 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
2988 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
2991 for (ii = 0; ii < hil.used; ) {
2992 ary = malloc(tbl.width * sizeof(ary[0]));
2993 ary[0] = hil.list[ii]->handle;
2994 ary[1] = strtab(hil.list[ii]->opserv_level);
2995 tbl.contents[++ii] = ary;
2997 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
2998 reply("MSG_MATCH_COUNT", hil.used);
2999 for (ii = 0; ii < hil.used; )
3000 free(tbl.contents[++ii]);
3005 static NICKSERV_FUNC(cmd_search)
3007 struct nickserv_discrim *discrim;
3008 discrim_search_func action;
3009 struct svccmd *subcmd;
3010 unsigned int matches;
3013 NICKSERV_MIN_PARMS(3);
3014 sprintf(buf, "search %s", argv[1]);
3015 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3016 if (!irccasecmp(argv[1], "print"))
3017 action = search_print_func;
3018 else if (!irccasecmp(argv[1], "count"))
3019 action = search_count_func;
3020 else if (!irccasecmp(argv[1], "unregister"))
3021 action = search_unregister_func;
3023 reply("NSMSG_INVALID_ACTION", argv[1]);
3027 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3030 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3034 if (action == search_print_func)
3035 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3036 else if (action == search_count_func)
3037 discrim->limit = INT_MAX;
3039 matches = nickserv_discrim_search(discrim, action, user);
3042 reply("MSG_MATCH_COUNT", matches);
3044 reply("MSG_NO_MATCHES");
3050 static MODCMD_FUNC(cmd_checkpass)
3052 struct handle_info *hi;
3054 NICKSERV_MIN_PARMS(3);
3055 if (!(hi = get_handle_info(argv[1]))) {
3056 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3059 if (checkpass(argv[2], hi->passwd))
3060 reply("CHECKPASS_YES");
3062 reply("CHECKPASS_NO");
3068 nickserv_db_read_handle(const char *handle, dict_t obj)
3071 struct string_list *masks, *slist;
3072 struct handle_info *hi;
3073 struct userNode *authed_users;
3074 unsigned long int id;
3078 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3079 id = str ? strtoul(str, NULL, 0) : 0;
3080 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3082 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3085 if ((hi = get_handle_info(handle))) {
3086 authed_users = hi->users;
3088 dict_remove(nickserv_handle_dict, hi->handle);
3090 authed_users = NULL;
3092 hi = register_handle(handle, str, id);
3094 hi->users = authed_users;
3095 while (authed_users) {
3096 authed_users->handle_info = hi;
3097 authed_users = authed_users->next_authed;
3100 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3101 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3102 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3103 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3104 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3105 hi->language = language_find(str ? str : "C");
3106 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3107 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3108 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3109 if (str) hi->infoline = strdup(str);
3110 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3111 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3112 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3113 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3114 /* We want to read the nicks even if disable_nicks is set. This is so
3115 * that we don't lose the nick data entirely. */
3116 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3118 for (ii=0; ii<slist->used; ii++)
3119 register_nick(slist->list[ii], hi);
3121 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3123 for (ii=0; str[ii]; ii++)
3124 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3126 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3127 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3128 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3129 hi->announcements = str ? str[0] : '?';
3130 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3131 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3132 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3133 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3134 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3135 if (!str) str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3136 if (str) safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3137 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3138 if (str) nickserv_set_email_addr(hi, str);
3139 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3140 if (str) hi->epithet = strdup(str);
3141 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3143 const char *data, *type, *expires, *cookie_str;
3144 struct handle_cookie *cookie;
3146 cookie = calloc(1, sizeof(*cookie));
3147 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3148 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3149 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3150 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3151 if (!type || !expires || !cookie_str) {
3152 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3155 if (!irccasecmp(type, KEY_ACTIVATION))
3156 cookie->type = ACTIVATION;
3157 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3158 cookie->type = PASSWORD_CHANGE;
3159 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3160 cookie->type = EMAIL_CHANGE;
3161 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3162 cookie->type = ALLOWAUTH;
3164 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3167 cookie->expires = strtoul(expires, NULL, 0);
3168 if (cookie->expires < now)
3171 cookie->data = strdup(data);
3172 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3176 nickserv_bake_cookie(cookie);
3178 nickserv_free_cookie(cookie);
3183 nickserv_saxdb_read(dict_t db) {
3185 struct record_data *rd;
3187 for (it=dict_first(db); it; it=iter_next(it)) {
3189 nickserv_db_read_handle(iter_key(it), rd->d.object);
3194 static NICKSERV_FUNC(cmd_mergedb)
3196 struct timeval start, stop;
3199 NICKSERV_MIN_PARMS(2);
3200 gettimeofday(&start, NULL);
3201 if (!(db = parse_database(argv[1]))) {
3202 reply("NSMSG_DB_UNREADABLE", argv[1]);
3205 nickserv_saxdb_read(db);
3207 gettimeofday(&stop, NULL);
3208 stop.tv_sec -= start.tv_sec;
3209 stop.tv_usec -= start.tv_usec;
3210 if (stop.tv_usec < 0) {
3212 stop.tv_usec += 1000000;
3214 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3219 expire_handles(UNUSED_ARG(void *data))
3221 dict_iterator_t it, next;
3223 struct handle_info *hi;
3225 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3226 next = iter_next(it);
3228 if ((hi->opserv_level > 0)
3230 || HANDLE_FLAGGED(hi, FROZEN)
3231 || HANDLE_FLAGGED(hi, NODELETE)) {
3234 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3235 if ((now - hi->lastseen) > expiry) {
3236 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3237 nickserv_unregister_handle(hi, NULL);
3241 if (nickserv_conf.handle_expire_frequency)
3242 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3246 nickserv_load_dict(const char *fname)
3250 if (!(file = fopen(fname, "r"))) {
3251 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3254 while (!feof(file)) {
3255 fgets(line, sizeof(line), file);
3258 if (line[strlen(line)-1] == '\n')
3259 line[strlen(line)-1] = 0;
3260 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3263 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3266 static enum reclaim_action
3267 reclaim_action_from_string(const char *str) {
3269 return RECLAIM_NONE;
3270 else if (!irccasecmp(str, "warn"))
3271 return RECLAIM_WARN;
3272 else if (!irccasecmp(str, "svsnick"))
3273 return RECLAIM_SVSNICK;
3274 else if (!irccasecmp(str, "kill"))
3275 return RECLAIM_KILL;
3277 return RECLAIM_NONE;
3281 nickserv_conf_read(void)
3283 dict_t conf_node, child;
3287 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3288 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3291 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3292 if (!str) str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3293 if (nickserv_conf.valid_handle_regex_set) regfree(&nickserv_conf.valid_handle_regex);
3295 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3296 nickserv_conf.valid_handle_regex_set = !err;
3297 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3299 nickserv_conf.valid_handle_regex_set = 0;
3301 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3302 if (nickserv_conf.valid_nick_regex_set) regfree(&nickserv_conf.valid_nick_regex);
3304 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3305 nickserv_conf.valid_nick_regex_set = !err;
3306 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3308 nickserv_conf.valid_nick_regex_set = 0;
3310 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3311 if (!str) str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3312 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3313 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3314 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3315 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3316 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3317 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3318 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3319 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3320 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3321 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3322 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3323 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3324 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3325 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3326 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3327 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3328 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3329 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3330 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3331 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3332 if (!str) str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3333 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3334 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3335 if (!str) str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3336 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3337 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3338 if (!str) str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3339 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3340 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3341 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3342 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3343 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3344 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3345 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3346 if (!nickserv_conf.disable_nicks) {
3347 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3348 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3349 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3350 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3351 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3352 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3353 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3354 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3356 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3357 for (it=dict_first(child); it; it=iter_next(it)) {
3358 const char *key = iter_key(it), *value;
3362 if (!strncasecmp(key, "uc_", 3))
3363 flag = toupper(key[3]);
3364 else if (!strncasecmp(key, "lc_", 3))
3365 flag = tolower(key[3]);
3369 if ((pos = handle_inverse_flags[flag])) {
3370 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3371 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3374 if (nickserv_conf.weak_password_dict)
3375 dict_delete(nickserv_conf.weak_password_dict);
3376 nickserv_conf.weak_password_dict = dict_new();
3377 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3378 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3379 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3380 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3382 nickserv_load_dict(str);
3383 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3385 NickChange(nickserv, str, 0);
3386 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3387 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3388 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3389 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3390 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3391 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3392 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3393 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3394 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3395 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3396 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3397 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3398 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3399 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3400 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3401 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3402 str = conf_get_data("server/network", RECDB_QSTRING);
3403 nickserv_conf.network_name = str ? str : "some IRC network";
3404 if (!nickserv_conf.auth_policer_params) {
3405 nickserv_conf.auth_policer_params = policer_params_new();
3406 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3407 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3409 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3410 for (it=dict_first(child); it; it=iter_next(it))
3411 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3415 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3416 char newnick[NICKLEN+1];
3425 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3427 case RECLAIM_SVSNICK:
3429 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3430 } while (GetUserH(newnick));
3431 irc_svsnick(nickserv, user, newnick);
3434 irc_kill(nickserv, user, "NSMSG_RECLAIM_KILL");
3440 nickserv_reclaim_p(void *data) {
3441 struct userNode *user = data;
3442 struct nick_info *ni = get_nick_info(user->nick);
3444 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3448 check_user_nick(struct userNode *user) {
3449 struct nick_info *ni;
3450 user->modes &= ~FLAGS_REGNICK;
3451 if (!(ni = get_nick_info(user->nick)))
3453 if (user->handle_info == ni->owner) {
3454 user->modes |= FLAGS_REGNICK;
3458 if (nickserv_conf.warn_nick_owned)
3459 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3460 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3462 if (nickserv_conf.auto_reclaim_delay)
3463 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3465 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3470 handle_new_user(struct userNode *user)
3472 return check_user_nick(user);
3476 handle_account(struct userNode *user, const char *stamp)
3478 struct handle_info *hi;
3480 #ifdef WITH_PROTOCOL_P10
3481 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3483 hi = dict_find(nickserv_id_dict, stamp, NULL);
3487 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3490 set_user_handle_info(user, hi, 0);
3492 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3497 handle_nick_change(struct userNode *user, const char *old_nick)
3499 struct handle_info *hi;
3501 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3502 dict_remove(nickserv_allow_auth_dict, old_nick);
3503 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3505 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3506 check_user_nick(user);
3510 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3512 dict_remove(nickserv_allow_auth_dict, user->nick);
3513 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3514 set_user_handle_info(user, NULL, 0);
3517 static struct modcmd *
3518 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3520 if (min_level > 0) {
3522 sprintf(buf, "%u", min_level);
3523 if (must_be_qualified) {
3524 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3526 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3528 } else if (min_level == 0) {
3529 if (must_be_qualified) {
3530 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3532 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3535 if (must_be_qualified) {
3536 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3538 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3544 nickserv_db_cleanup(void)
3546 unreg_del_user_func(nickserv_remove_user);
3547 userList_clean(&curr_helpers);
3548 policer_params_delete(nickserv_conf.auth_policer_params);
3549 dict_delete(nickserv_handle_dict);
3550 dict_delete(nickserv_nick_dict);
3551 dict_delete(nickserv_opt_dict);
3552 dict_delete(nickserv_allow_auth_dict);
3553 dict_delete(nickserv_email_dict);
3554 dict_delete(nickserv_id_dict);
3555 dict_delete(nickserv_conf.weak_password_dict);
3556 free(auth_func_list);
3557 free(unreg_func_list);
3559 free(allowauth_func_list);
3560 free(handle_merge_func_list);
3561 free(failpw_func_list);
3562 if (nickserv_conf.valid_handle_regex_set)
3563 regfree(&nickserv_conf.valid_handle_regex);
3564 if (nickserv_conf.valid_nick_regex_set)
3565 regfree(&nickserv_conf.valid_nick_regex);
3569 init_nickserv(const char *nick)
3572 nickserv = AddService(nick, "Nick Services");
3573 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3574 reg_new_user_func(handle_new_user);
3575 reg_nick_change_func(handle_nick_change);
3576 reg_del_user_func(nickserv_remove_user);
3577 reg_account_func(handle_account);
3579 /* set up handle_inverse_flags */
3580 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3581 for (i=0; handle_flags[i]; i++) {
3582 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3583 flag_access_levels[i] = 0;
3586 conf_register_reload(nickserv_conf_read);
3587 nickserv_opt_dict = dict_new();
3588 nickserv_email_dict = dict_new();
3589 dict_set_free_keys(nickserv_email_dict, free);
3590 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3592 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3593 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3594 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3595 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3596 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3597 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3598 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3599 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3600 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3601 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3602 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3603 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3604 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3605 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3606 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3607 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3608 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3609 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3610 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3611 if (!nickserv_conf.disable_nicks) {
3612 /* nick management commands */
3613 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3614 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3615 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3616 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3617 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3618 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3620 if (nickserv_conf.email_enabled) {
3621 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3622 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3623 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3624 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3625 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3627 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3628 /* miscellaneous commands */
3629 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3630 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3631 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3632 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3633 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3635 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3636 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3637 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3638 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3639 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3640 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3641 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3642 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3643 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3644 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3645 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3646 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3647 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3648 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3649 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3651 nickserv_handle_dict = dict_new();
3652 dict_set_free_keys(nickserv_handle_dict, free);
3653 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3655 nickserv_id_dict = dict_new();
3656 dict_set_free_keys(nickserv_id_dict, free);
3658 nickserv_nick_dict = dict_new();
3659 dict_set_free_data(nickserv_nick_dict, free_nick_info);
3661 nickserv_allow_auth_dict = dict_new();
3663 userList_init(&curr_helpers);
3665 nickserv_service = service_register(nickserv, 0);
3666 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3667 reg_exit_func(nickserv_db_cleanup);
3668 if(nickserv_conf.handle_expire_frequency)
3669 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3670 message_register_table(msgtab);