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_OFF"); 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 lang = hi->language ? hi->language : lang_C;
2295 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", lang->name);
2300 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2301 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2303 if ((user->handle_info->opserv_level < target->opserv_level)
2304 || ((user->handle_info->opserv_level == target->opserv_level)
2305 && (user->handle_info->opserv_level < 1000))) {
2306 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2309 if ((user->handle_info->opserv_level < new_level)
2310 || ((user->handle_info->opserv_level == new_level)
2311 && (user->handle_info->opserv_level < 1000))) {
2312 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2315 if (user->handle_info == target) {
2316 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2319 if (target->opserv_level == new_level)
2321 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2322 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2323 target->opserv_level = new_level;
2327 static OPTION_FUNC(opt_level)
2332 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2336 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2337 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2341 static OPTION_FUNC(opt_epithet)
2344 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2348 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2349 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2352 if ((epithet[0] == '*') && !epithet[1])
2355 hi->epithet = strdup(epithet);
2359 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2361 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2365 static NICKSERV_FUNC(cmd_reclaim)
2367 struct handle_info *hi;
2368 struct nick_info *ni;
2369 struct userNode *victim;
2371 NICKSERV_MIN_PARMS(2);
2372 hi = user->handle_info;
2373 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2375 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2378 if (ni->owner != user->handle_info) {
2379 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2382 victim = GetUserH(ni->nick);
2384 reply("MSG_NICK_UNKNOWN", ni->nick);
2387 if (victim == user) {
2388 reply("NSMSG_NICK_USER_YOU");
2391 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2392 switch (nickserv_conf.reclaim_action) {
2393 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2394 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2395 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2396 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2401 static NICKSERV_FUNC(cmd_unregnick)
2404 struct handle_info *hi;
2405 struct nick_info *ni;
2407 hi = user->handle_info;
2408 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2409 ni = dict_find(nickserv_nick_dict, nick, NULL);
2411 reply("NSMSG_UNKNOWN_NICK", nick);
2414 if (hi != ni->owner) {
2415 reply("NSMSG_NOT_YOUR_NICK", nick);
2418 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2423 static NICKSERV_FUNC(cmd_ounregnick)
2425 struct nick_info *ni;
2427 NICKSERV_MIN_PARMS(2);
2428 if (!(ni = get_nick_info(argv[1]))) {
2429 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2432 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2433 reply("MSG_USER_OUTRANKED", ni->nick);
2436 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2441 static NICKSERV_FUNC(cmd_unregister)
2443 struct handle_info *hi;
2446 NICKSERV_MIN_PARMS(2);
2447 hi = user->handle_info;
2450 if (checkpass(passwd, hi->passwd)) {
2451 nickserv_unregister_handle(hi, user);
2454 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2455 reply("NSMSG_PASSWORD_INVALID");
2460 static NICKSERV_FUNC(cmd_ounregister)
2462 struct handle_info *hi;
2464 NICKSERV_MIN_PARMS(2);
2465 if (!(hi = get_victim_oper(user, argv[1])))
2467 nickserv_unregister_handle(hi, user);
2471 static NICKSERV_FUNC(cmd_status)
2473 if (nickserv_conf.disable_nicks) {
2474 reply("NSMSG_GLOBAL_STATS_NONICK",
2475 dict_size(nickserv_handle_dict));
2477 if (user->handle_info) {
2479 struct nick_info *ni;
2480 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2481 reply("NSMSG_HANDLE_STATS", cnt);
2483 reply("NSMSG_HANDLE_NONE");
2485 reply("NSMSG_GLOBAL_STATS",
2486 dict_size(nickserv_handle_dict),
2487 dict_size(nickserv_nick_dict));
2492 static NICKSERV_FUNC(cmd_ghost)
2494 struct userNode *target;
2495 char reason[MAXLEN];
2497 NICKSERV_MIN_PARMS(2);
2498 if (!(target = GetUserH(argv[1]))) {
2499 reply("MSG_NICK_UNKNOWN", argv[1]);
2502 if (target == user) {
2503 reply("NSMSG_CANNOT_GHOST_SELF");
2506 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2507 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2510 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2511 DelUser(target, nickserv, 1, reason);
2512 reply("NSMSG_GHOST_KILLED", argv[1]);
2516 static NICKSERV_FUNC(cmd_vacation)
2518 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2519 reply("NSMSG_ON_VACATION");
2524 nickserv_saxdb_write(struct saxdb_context *ctx) {
2526 struct handle_info *hi;
2529 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2531 #ifdef WITH_PROTOCOL_BAHAMUT
2534 saxdb_start_record(ctx, iter_key(it), 0);
2535 if (hi->announcements != '?') {
2536 flags[0] = hi->announcements;
2538 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2541 struct handle_cookie *cookie = hi->cookie;
2544 switch (cookie->type) {
2545 case ACTIVATION: type = KEY_ACTIVATION; break;
2546 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2547 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2548 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2549 default: type = NULL; break;
2552 saxdb_start_record(ctx, KEY_COOKIE, 0);
2553 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2554 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2556 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2557 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2558 saxdb_end_record(ctx);
2562 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2564 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2568 for (ii=flen=0; handle_flags[ii]; ++ii)
2569 if (hi->flags & (1 << ii))
2570 flags[flen++] = handle_flags[ii];
2572 saxdb_write_string(ctx, KEY_FLAGS, flags);
2574 #ifdef WITH_PROTOCOL_BAHAMUT
2575 saxdb_write_int(ctx, KEY_ID, hi->id);
2578 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2579 if (hi->last_quit_host[0])
2580 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2581 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2582 if (hi->masks->used)
2583 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2585 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2587 struct string_list *slist;
2588 struct nick_info *ni;
2590 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2591 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2592 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2596 if (hi->opserv_level)
2597 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2598 if (hi->language && (hi->language != lang_C))
2599 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2600 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2601 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2602 if (hi->screen_width)
2603 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2604 if (hi->table_width)
2605 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2606 flags[0] = hi->userlist_style;
2608 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2609 saxdb_end_record(ctx);
2614 static handle_merge_func_t *handle_merge_func_list;
2615 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2618 reg_handle_merge_func(handle_merge_func_t func)
2620 if (handle_merge_func_used == handle_merge_func_size) {
2621 if (handle_merge_func_size) {
2622 handle_merge_func_size <<= 1;
2623 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2625 handle_merge_func_size = 8;
2626 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2629 handle_merge_func_list[handle_merge_func_used++] = func;
2632 static NICKSERV_FUNC(cmd_merge)
2634 struct handle_info *hi_from, *hi_to;
2635 struct userNode *last_user;
2636 struct userData *cList, *cListNext;
2637 unsigned int ii, jj, n;
2638 char buffer[MAXLEN];
2640 NICKSERV_MIN_PARMS(3);
2642 if (!(hi_from = get_victim_oper(user, argv[1])))
2644 if (!(hi_to = get_victim_oper(user, argv[2])))
2646 if (hi_to == hi_from) {
2647 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2651 for (n=0; n<handle_merge_func_used; n++)
2652 handle_merge_func_list[n](user, hi_to, hi_from);
2654 /* Append "from" handle's nicks to "to" handle's nick list. */
2656 struct nick_info *last_ni;
2657 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2658 last_ni->next = hi_from->nicks;
2660 while (hi_from->nicks) {
2661 hi_from->nicks->owner = hi_to;
2662 hi_from->nicks = hi_from->nicks->next;
2665 /* Merge the hostmasks. */
2666 for (ii=0; ii<hi_from->masks->used; ii++) {
2667 char *mask = hi_from->masks->list[ii];
2668 for (jj=0; jj<hi_to->masks->used; jj++)
2669 if (match_ircglobs(hi_to->masks->list[jj], mask))
2671 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2672 string_list_append(hi_to->masks, strdup(mask));
2675 /* Merge the lists of authed users. */
2677 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2678 last_user->next_authed = hi_from->users;
2680 hi_to->users = hi_from->users;
2682 /* Repoint the old "from" handle's users. */
2683 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2684 last_user->handle_info = hi_to;
2686 hi_from->users = NULL;
2688 /* Merge channel userlists. */
2689 for (cList=hi_from->channels; cList; cList=cListNext) {
2690 struct userData *cList2;
2691 cListNext = cList->u_next;
2692 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2693 if (cList->channel == cList2->channel)
2695 log_module(NS_LOG, LOG_DEBUG, "Merging %s->%s@%s: before %p->%p->%-p, %p->%p->%p",
2696 hi_from->handle, hi_to->handle, cList->channel->channel->name,
2697 cList->u_prev, cList, cList->u_next,
2698 (cList2?cList2->u_prev:0), cList2, (cList2?cList2->u_next:0));
2699 if (cList2 && (cList2->access >= cList->access)) {
2700 /* keep cList2 in hi_to; remove cList from hi_from */
2701 log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList);
2702 del_channel_user(cList, 1);
2705 /* remove the lower-ranking cList2 from hi_to */
2706 log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList2);
2707 del_channel_user(cList2, 1);
2709 /* cList needs to be moved from hi_from to hi_to */
2710 cList->handle = hi_to;
2711 /* Remove from linked list for hi_from */
2712 assert(!cList->u_prev);
2713 hi_from->channels = cList->u_next;
2715 cList->u_next->u_prev = cList->u_prev;
2716 /* Add to linked list for hi_to */
2717 cList->u_prev = NULL;
2718 cList->u_next = hi_to->channels;
2719 if (hi_to->channels)
2720 hi_to->channels->u_prev = cList;
2721 hi_to->channels = cList;
2722 log_module(NS_LOG, LOG_DEBUG, "Now %p->%p->%p",
2723 cList->u_prev, cList, cList->u_next);
2727 /* Do they get an OpServ level promotion? */
2728 if (hi_from->opserv_level > hi_to->opserv_level)
2729 hi_to->opserv_level = hi_from->opserv_level;
2731 /* What about last seen time? */
2732 if (hi_from->lastseen > hi_to->lastseen)
2733 hi_to->lastseen = hi_from->lastseen;
2735 /* Notify of success. */
2736 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2737 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2738 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2740 /* Unregister the "from" handle. */
2741 nickserv_unregister_handle(hi_from, NULL);
2746 struct nickserv_discrim {
2747 unsigned int limit, min_level, max_level;
2748 unsigned long flags_on, flags_off;
2749 time_t min_registered, max_registered;
2751 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2752 const char *nickmask;
2753 const char *hostmask;
2754 const char *handlemask;
2755 const char *emailmask;
2758 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2760 struct discrim_apply_info {
2761 struct nickserv_discrim *discrim;
2762 discrim_search_func func;
2763 struct userNode *source;
2764 unsigned int matched;
2767 static struct nickserv_discrim *
2768 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2771 struct nickserv_discrim *discrim;
2773 discrim = malloc(sizeof(*discrim));
2774 memset(discrim, 0, sizeof(*discrim));
2775 discrim->min_level = 0;
2776 discrim->max_level = ~0;
2777 discrim->limit = 50;
2778 discrim->min_registered = 0;
2779 discrim->max_registered = INT_MAX;
2780 discrim->lastseen = now;
2782 for (i=0; i<argc; i++) {
2783 if (i == argc - 1) {
2784 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2787 if (!irccasecmp(argv[i], "limit")) {
2788 discrim->limit = atoi(argv[++i]);
2789 } else if (!irccasecmp(argv[i], "flags")) {
2790 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2791 } else if (!irccasecmp(argv[i], "registered")) {
2792 const char *cmp = argv[++i];
2793 if (cmp[0] == '<') {
2794 if (cmp[1] == '=') {
2795 discrim->min_registered = now - ParseInterval(cmp+2);
2797 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2799 } else if (cmp[0] == '=') {
2800 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2801 } else if (cmp[0] == '>') {
2802 if (cmp[1] == '=') {
2803 discrim->max_registered = now - ParseInterval(cmp+2);
2805 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2808 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2810 } else if (!irccasecmp(argv[i], "seen")) {
2811 discrim->lastseen = now - ParseInterval(argv[++i]);
2812 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2813 discrim->nickmask = argv[++i];
2814 } else if (!irccasecmp(argv[i], "hostmask")) {
2816 if (!irccasecmp(argv[i], "exact")) {
2817 if (i == argc - 1) {
2818 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2821 discrim->hostmask_type = EXACT;
2822 } else if (!irccasecmp(argv[i], "subset")) {
2823 if (i == argc - 1) {
2824 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2827 discrim->hostmask_type = SUBSET;
2828 } else if (!irccasecmp(argv[i], "superset")) {
2829 if (i == argc - 1) {
2830 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2833 discrim->hostmask_type = SUPERSET;
2834 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2835 if (i == argc - 1) {
2836 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2839 discrim->hostmask_type = LASTQUIT;
2842 discrim->hostmask_type = SUPERSET;
2844 discrim->hostmask = argv[++i];
2845 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2846 if (!irccasecmp(argv[++i], "*")) {
2847 discrim->handlemask = 0;
2849 discrim->handlemask = argv[i];
2851 } else if (!irccasecmp(argv[i], "email")) {
2852 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2853 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2855 } else if (!irccasecmp(argv[++i], "*")) {
2856 discrim->emailmask = 0;
2858 discrim->emailmask = argv[i];
2860 } else if (!irccasecmp(argv[i], "access")) {
2861 const char *cmp = argv[++i];
2862 if (cmp[0] == '<') {
2863 if (discrim->min_level == 0) discrim->min_level = 1;
2864 if (cmp[1] == '=') {
2865 discrim->max_level = strtoul(cmp+2, NULL, 0);
2867 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
2869 } else if (cmp[0] == '=') {
2870 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
2871 } else if (cmp[0] == '>') {
2872 if (cmp[1] == '=') {
2873 discrim->min_level = strtoul(cmp+2, NULL, 0);
2875 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
2878 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2881 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
2892 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
2894 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
2895 || (discrim->flags_off & hi->flags)
2896 || (discrim->min_registered > hi->registered)
2897 || (discrim->max_registered < hi->registered)
2898 || (discrim->lastseen < (hi->users?now:hi->lastseen))
2899 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
2900 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
2901 || (discrim->min_level > hi->opserv_level)
2902 || (discrim->max_level < hi->opserv_level)) {
2905 if (discrim->hostmask) {
2907 for (i=0; i<hi->masks->used; i++) {
2908 const char *mask = hi->masks->list[i];
2909 if ((discrim->hostmask_type == SUBSET)
2910 && (match_ircglobs(discrim->hostmask, mask))) break;
2911 else if ((discrim->hostmask_type == EXACT)
2912 && !irccasecmp(discrim->hostmask, mask)) break;
2913 else if ((discrim->hostmask_type == SUPERSET)
2914 && (match_ircglobs(mask, discrim->hostmask))) break;
2915 else if ((discrim->hostmask_type == LASTQUIT)
2916 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
2918 if (i==hi->masks->used) return 0;
2920 if (discrim->nickmask) {
2921 struct nick_info *nick = hi->nicks;
2923 if (match_ircglob(nick->nick, discrim->nickmask)) break;
2926 if (!nick) return 0;
2932 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
2934 dict_iterator_t it, next;
2935 unsigned int matched;
2937 for (it = dict_first(nickserv_handle_dict), matched = 0;
2938 it && (matched < discrim->limit);
2940 next = iter_next(it);
2941 if (nickserv_discrim_match(discrim, iter_data(it))) {
2942 dsf(source, iter_data(it));
2950 search_print_func(struct userNode *source, struct handle_info *match)
2952 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
2956 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
2961 search_unregister_func (struct userNode *source, struct handle_info *match)
2963 if (oper_has_access(source, nickserv, match->opserv_level, 0))
2964 nickserv_unregister_handle(match, source);
2968 nickserv_sort_accounts_by_access(const void *a, const void *b)
2970 const struct handle_info *hi_a = *(const struct handle_info**)a;
2971 const struct handle_info *hi_b = *(const struct handle_info**)b;
2972 if (hi_a->opserv_level != hi_b->opserv_level)
2973 return hi_b->opserv_level - hi_a->opserv_level;
2974 return irccasecmp(hi_a->handle, hi_b->handle);
2978 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
2980 struct handle_info_list hil;
2981 struct helpfile_table tbl;
2986 memset(&hil, 0, sizeof(hil));
2987 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2988 struct handle_info *hi = iter_data(it);
2989 if (hi->opserv_level)
2990 handle_info_list_append(&hil, hi);
2992 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
2993 tbl.length = hil.used + 1;
2995 tbl.flags = TABLE_NO_FREE;
2996 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
2997 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3000 for (ii = 0; ii < hil.used; ) {
3001 ary = malloc(tbl.width * sizeof(ary[0]));
3002 ary[0] = hil.list[ii]->handle;
3003 ary[1] = strtab(hil.list[ii]->opserv_level);
3004 tbl.contents[++ii] = ary;
3006 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3007 reply("MSG_MATCH_COUNT", hil.used);
3008 for (ii = 0; ii < hil.used; )
3009 free(tbl.contents[++ii]);
3014 static NICKSERV_FUNC(cmd_search)
3016 struct nickserv_discrim *discrim;
3017 discrim_search_func action;
3018 struct svccmd *subcmd;
3019 unsigned int matches;
3022 NICKSERV_MIN_PARMS(3);
3023 sprintf(buf, "search %s", argv[1]);
3024 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3025 if (!irccasecmp(argv[1], "print"))
3026 action = search_print_func;
3027 else if (!irccasecmp(argv[1], "count"))
3028 action = search_count_func;
3029 else if (!irccasecmp(argv[1], "unregister"))
3030 action = search_unregister_func;
3032 reply("NSMSG_INVALID_ACTION", argv[1]);
3036 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3039 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3043 if (action == search_print_func)
3044 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3045 else if (action == search_count_func)
3046 discrim->limit = INT_MAX;
3048 matches = nickserv_discrim_search(discrim, action, user);
3051 reply("MSG_MATCH_COUNT", matches);
3053 reply("MSG_NO_MATCHES");
3059 static MODCMD_FUNC(cmd_checkpass)
3061 struct handle_info *hi;
3063 NICKSERV_MIN_PARMS(3);
3064 if (!(hi = get_handle_info(argv[1]))) {
3065 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3068 if (checkpass(argv[2], hi->passwd))
3069 reply("CHECKPASS_YES");
3071 reply("CHECKPASS_NO");
3077 nickserv_db_read_handle(const char *handle, dict_t obj)
3080 struct string_list *masks, *slist;
3081 struct handle_info *hi;
3082 struct userNode *authed_users;
3083 unsigned long int id;
3087 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3088 id = str ? strtoul(str, NULL, 0) : 0;
3089 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3091 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3094 if ((hi = get_handle_info(handle))) {
3095 authed_users = hi->users;
3097 dict_remove(nickserv_handle_dict, hi->handle);
3099 authed_users = NULL;
3101 hi = register_handle(handle, str, id);
3103 hi->users = authed_users;
3104 while (authed_users) {
3105 authed_users->handle_info = hi;
3106 authed_users = authed_users->next_authed;
3109 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3110 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3111 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3112 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3113 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3114 hi->language = language_find(str ? str : "C");
3115 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3116 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3117 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3118 if (str) hi->infoline = strdup(str);
3119 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3120 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3121 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3122 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3123 /* We want to read the nicks even if disable_nicks is set. This is so
3124 * that we don't lose the nick data entirely. */
3125 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3127 for (ii=0; ii<slist->used; ii++)
3128 register_nick(slist->list[ii], hi);
3130 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3132 for (ii=0; str[ii]; ii++)
3133 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3135 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3136 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3137 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3138 hi->announcements = str ? str[0] : '?';
3139 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3140 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3141 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3142 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3143 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3144 if (!str) str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3145 if (str) safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3146 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3147 if (str) nickserv_set_email_addr(hi, str);
3148 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3149 if (str) hi->epithet = strdup(str);
3150 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3152 const char *data, *type, *expires, *cookie_str;
3153 struct handle_cookie *cookie;
3155 cookie = calloc(1, sizeof(*cookie));
3156 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3157 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3158 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3159 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3160 if (!type || !expires || !cookie_str) {
3161 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3164 if (!irccasecmp(type, KEY_ACTIVATION))
3165 cookie->type = ACTIVATION;
3166 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3167 cookie->type = PASSWORD_CHANGE;
3168 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3169 cookie->type = EMAIL_CHANGE;
3170 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3171 cookie->type = ALLOWAUTH;
3173 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3176 cookie->expires = strtoul(expires, NULL, 0);
3177 if (cookie->expires < now)
3180 cookie->data = strdup(data);
3181 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3185 nickserv_bake_cookie(cookie);
3187 nickserv_free_cookie(cookie);
3192 nickserv_saxdb_read(dict_t db) {
3194 struct record_data *rd;
3196 for (it=dict_first(db); it; it=iter_next(it)) {
3198 nickserv_db_read_handle(iter_key(it), rd->d.object);
3203 static NICKSERV_FUNC(cmd_mergedb)
3205 struct timeval start, stop;
3208 NICKSERV_MIN_PARMS(2);
3209 gettimeofday(&start, NULL);
3210 if (!(db = parse_database(argv[1]))) {
3211 reply("NSMSG_DB_UNREADABLE", argv[1]);
3214 nickserv_saxdb_read(db);
3216 gettimeofday(&stop, NULL);
3217 stop.tv_sec -= start.tv_sec;
3218 stop.tv_usec -= start.tv_usec;
3219 if (stop.tv_usec < 0) {
3221 stop.tv_usec += 1000000;
3223 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3228 expire_handles(UNUSED_ARG(void *data))
3230 dict_iterator_t it, next;
3232 struct handle_info *hi;
3234 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3235 next = iter_next(it);
3237 if ((hi->opserv_level > 0)
3239 || HANDLE_FLAGGED(hi, FROZEN)
3240 || HANDLE_FLAGGED(hi, NODELETE)) {
3243 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3244 if ((now - hi->lastseen) > expiry) {
3245 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3246 nickserv_unregister_handle(hi, NULL);
3250 if (nickserv_conf.handle_expire_frequency)
3251 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3255 nickserv_load_dict(const char *fname)
3259 if (!(file = fopen(fname, "r"))) {
3260 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3263 while (!feof(file)) {
3264 fgets(line, sizeof(line), file);
3267 if (line[strlen(line)-1] == '\n')
3268 line[strlen(line)-1] = 0;
3269 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3272 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3275 static enum reclaim_action
3276 reclaim_action_from_string(const char *str) {
3278 return RECLAIM_NONE;
3279 else if (!irccasecmp(str, "warn"))
3280 return RECLAIM_WARN;
3281 else if (!irccasecmp(str, "svsnick"))
3282 return RECLAIM_SVSNICK;
3283 else if (!irccasecmp(str, "kill"))
3284 return RECLAIM_KILL;
3286 return RECLAIM_NONE;
3290 nickserv_conf_read(void)
3292 dict_t conf_node, child;
3296 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3297 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3300 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3301 if (!str) str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3302 if (nickserv_conf.valid_handle_regex_set) regfree(&nickserv_conf.valid_handle_regex);
3304 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3305 nickserv_conf.valid_handle_regex_set = !err;
3306 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3308 nickserv_conf.valid_handle_regex_set = 0;
3310 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3311 if (nickserv_conf.valid_nick_regex_set) regfree(&nickserv_conf.valid_nick_regex);
3313 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3314 nickserv_conf.valid_nick_regex_set = !err;
3315 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3317 nickserv_conf.valid_nick_regex_set = 0;
3319 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3320 if (!str) str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3321 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3322 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3323 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3324 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3325 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3326 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3327 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3328 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3329 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3330 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3331 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3332 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3333 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3334 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3335 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3336 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3337 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3338 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3339 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3340 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3341 if (!str) str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3342 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3343 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3344 if (!str) str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3345 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3346 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3347 if (!str) str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3348 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3349 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3350 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3351 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3352 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3353 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3354 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3355 if (!nickserv_conf.disable_nicks) {
3356 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3357 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3358 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3359 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3360 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3361 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3362 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3363 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3365 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3366 for (it=dict_first(child); it; it=iter_next(it)) {
3367 const char *key = iter_key(it), *value;
3371 if (!strncasecmp(key, "uc_", 3))
3372 flag = toupper(key[3]);
3373 else if (!strncasecmp(key, "lc_", 3))
3374 flag = tolower(key[3]);
3378 if ((pos = handle_inverse_flags[flag])) {
3379 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3380 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3383 if (nickserv_conf.weak_password_dict)
3384 dict_delete(nickserv_conf.weak_password_dict);
3385 nickserv_conf.weak_password_dict = dict_new();
3386 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3387 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3388 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3389 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3391 nickserv_load_dict(str);
3392 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3393 if (nickserv && str)
3394 NickChange(nickserv, str, 0);
3395 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3396 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3397 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3398 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3399 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3400 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3401 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3402 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3403 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3404 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3405 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3406 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3407 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3408 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3409 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3410 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3411 str = conf_get_data("server/network", RECDB_QSTRING);
3412 nickserv_conf.network_name = str ? str : "some IRC network";
3413 if (!nickserv_conf.auth_policer_params) {
3414 nickserv_conf.auth_policer_params = policer_params_new();
3415 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3416 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3418 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3419 for (it=dict_first(child); it; it=iter_next(it))
3420 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3424 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3425 char newnick[NICKLEN+1];
3434 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3436 case RECLAIM_SVSNICK:
3438 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3439 } while (GetUserH(newnick));
3440 irc_svsnick(nickserv, user, newnick);
3443 irc_kill(nickserv, user, "NSMSG_RECLAIM_KILL");
3449 nickserv_reclaim_p(void *data) {
3450 struct userNode *user = data;
3451 struct nick_info *ni = get_nick_info(user->nick);
3453 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3457 check_user_nick(struct userNode *user) {
3458 struct nick_info *ni;
3459 user->modes &= ~FLAGS_REGNICK;
3460 if (!(ni = get_nick_info(user->nick)))
3462 if (user->handle_info == ni->owner) {
3463 user->modes |= FLAGS_REGNICK;
3467 if (nickserv_conf.warn_nick_owned)
3468 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3469 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3471 if (nickserv_conf.auto_reclaim_delay)
3472 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3474 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3479 handle_new_user(struct userNode *user)
3481 return check_user_nick(user);
3485 handle_account(struct userNode *user, const char *stamp)
3487 struct handle_info *hi;
3489 #ifdef WITH_PROTOCOL_P10
3490 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3492 hi = dict_find(nickserv_id_dict, stamp, NULL);
3496 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3499 set_user_handle_info(user, hi, 0);
3501 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3506 handle_nick_change(struct userNode *user, const char *old_nick)
3508 struct handle_info *hi;
3510 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3511 dict_remove(nickserv_allow_auth_dict, old_nick);
3512 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3514 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3515 check_user_nick(user);
3519 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3521 dict_remove(nickserv_allow_auth_dict, user->nick);
3522 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3523 set_user_handle_info(user, NULL, 0);
3526 static struct modcmd *
3527 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3529 if (min_level > 0) {
3531 sprintf(buf, "%u", min_level);
3532 if (must_be_qualified) {
3533 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3535 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3537 } else if (min_level == 0) {
3538 if (must_be_qualified) {
3539 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3541 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3544 if (must_be_qualified) {
3545 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3547 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3553 nickserv_db_cleanup(void)
3555 unreg_del_user_func(nickserv_remove_user);
3556 userList_clean(&curr_helpers);
3557 policer_params_delete(nickserv_conf.auth_policer_params);
3558 dict_delete(nickserv_handle_dict);
3559 dict_delete(nickserv_nick_dict);
3560 dict_delete(nickserv_opt_dict);
3561 dict_delete(nickserv_allow_auth_dict);
3562 dict_delete(nickserv_email_dict);
3563 dict_delete(nickserv_id_dict);
3564 dict_delete(nickserv_conf.weak_password_dict);
3565 free(auth_func_list);
3566 free(unreg_func_list);
3568 free(allowauth_func_list);
3569 free(handle_merge_func_list);
3570 free(failpw_func_list);
3571 if (nickserv_conf.valid_handle_regex_set)
3572 regfree(&nickserv_conf.valid_handle_regex);
3573 if (nickserv_conf.valid_nick_regex_set)
3574 regfree(&nickserv_conf.valid_nick_regex);
3578 init_nickserv(const char *nick)
3581 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3582 reg_new_user_func(handle_new_user);
3583 reg_nick_change_func(handle_nick_change);
3584 reg_del_user_func(nickserv_remove_user);
3585 reg_account_func(handle_account);
3587 /* set up handle_inverse_flags */
3588 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3589 for (i=0; handle_flags[i]; i++) {
3590 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3591 flag_access_levels[i] = 0;
3594 conf_register_reload(nickserv_conf_read);
3595 nickserv_opt_dict = dict_new();
3596 nickserv_email_dict = dict_new();
3597 dict_set_free_keys(nickserv_email_dict, free);
3598 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3600 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3601 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3602 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3603 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3604 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3605 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3606 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3607 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3608 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3609 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3610 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3611 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3612 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3613 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3614 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3615 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3616 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3617 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3618 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3619 if (!nickserv_conf.disable_nicks) {
3620 /* nick management commands */
3621 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3622 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3623 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3624 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3625 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3626 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3628 if (nickserv_conf.email_enabled) {
3629 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3630 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3631 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3632 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3633 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3635 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3636 /* miscellaneous commands */
3637 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3638 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3639 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3640 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3641 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3643 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3644 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3645 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3646 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3647 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3648 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3649 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3650 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3651 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3652 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3653 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3654 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3655 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3656 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3657 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3659 nickserv_handle_dict = dict_new();
3660 dict_set_free_keys(nickserv_handle_dict, free);
3661 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3663 nickserv_id_dict = dict_new();
3664 dict_set_free_keys(nickserv_id_dict, free);
3666 nickserv_nick_dict = dict_new();
3667 dict_set_free_data(nickserv_nick_dict, free_nick_info);
3669 nickserv_allow_auth_dict = dict_new();
3671 userList_init(&curr_helpers);
3674 nickserv = AddService(nick, "Nick Services");
3675 nickserv_service = service_register(nickserv, 0);
3677 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3678 reg_exit_func(nickserv_db_cleanup);
3679 if(nickserv_conf.handle_expire_frequency)
3680 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3681 message_register_table(msgtab);