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 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2299 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2300 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2302 if ((user->handle_info->opserv_level < target->opserv_level)
2303 || ((user->handle_info->opserv_level == target->opserv_level)
2304 && (user->handle_info->opserv_level < 1000))) {
2305 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2308 if ((user->handle_info->opserv_level < new_level)
2309 || ((user->handle_info->opserv_level == new_level)
2310 && (user->handle_info->opserv_level < 1000))) {
2311 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2314 if (user->handle_info == target) {
2315 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2318 if (target->opserv_level == new_level)
2320 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2321 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2322 target->opserv_level = new_level;
2326 static OPTION_FUNC(opt_level)
2331 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2335 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2336 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2340 static OPTION_FUNC(opt_epithet)
2343 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2347 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2348 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2351 if ((epithet[0] == '*') && !epithet[1])
2354 hi->epithet = strdup(epithet);
2358 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2360 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2364 static NICKSERV_FUNC(cmd_reclaim)
2366 struct handle_info *hi;
2367 struct nick_info *ni;
2368 struct userNode *victim;
2370 NICKSERV_MIN_PARMS(2);
2371 hi = user->handle_info;
2372 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2374 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2377 if (ni->owner != user->handle_info) {
2378 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2381 victim = GetUserH(ni->nick);
2383 reply("MSG_NICK_UNKNOWN", ni->nick);
2386 if (victim == user) {
2387 reply("NSMSG_NICK_USER_YOU");
2390 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2391 switch (nickserv_conf.reclaim_action) {
2392 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2393 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2394 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2395 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2400 static NICKSERV_FUNC(cmd_unregnick)
2403 struct handle_info *hi;
2404 struct nick_info *ni;
2406 hi = user->handle_info;
2407 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2408 ni = dict_find(nickserv_nick_dict, nick, NULL);
2410 reply("NSMSG_UNKNOWN_NICK", nick);
2413 if (hi != ni->owner) {
2414 reply("NSMSG_NOT_YOUR_NICK", nick);
2417 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2422 static NICKSERV_FUNC(cmd_ounregnick)
2424 struct nick_info *ni;
2426 NICKSERV_MIN_PARMS(2);
2427 if (!(ni = get_nick_info(argv[1]))) {
2428 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2431 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2432 reply("MSG_USER_OUTRANKED", ni->nick);
2435 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2440 static NICKSERV_FUNC(cmd_unregister)
2442 struct handle_info *hi;
2445 NICKSERV_MIN_PARMS(2);
2446 hi = user->handle_info;
2449 if (checkpass(passwd, hi->passwd)) {
2450 nickserv_unregister_handle(hi, user);
2453 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2454 reply("NSMSG_PASSWORD_INVALID");
2459 static NICKSERV_FUNC(cmd_ounregister)
2461 struct handle_info *hi;
2463 NICKSERV_MIN_PARMS(2);
2464 if (!(hi = get_victim_oper(user, argv[1])))
2466 nickserv_unregister_handle(hi, user);
2470 static NICKSERV_FUNC(cmd_status)
2472 if (nickserv_conf.disable_nicks) {
2473 reply("NSMSG_GLOBAL_STATS_NONICK",
2474 dict_size(nickserv_handle_dict));
2476 if (user->handle_info) {
2478 struct nick_info *ni;
2479 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2480 reply("NSMSG_HANDLE_STATS", cnt);
2482 reply("NSMSG_HANDLE_NONE");
2484 reply("NSMSG_GLOBAL_STATS",
2485 dict_size(nickserv_handle_dict),
2486 dict_size(nickserv_nick_dict));
2491 static NICKSERV_FUNC(cmd_ghost)
2493 struct userNode *target;
2494 char reason[MAXLEN];
2496 NICKSERV_MIN_PARMS(2);
2497 if (!(target = GetUserH(argv[1]))) {
2498 reply("MSG_NICK_UNKNOWN", argv[1]);
2501 if (target == user) {
2502 reply("NSMSG_CANNOT_GHOST_SELF");
2505 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2506 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2509 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2510 DelUser(target, nickserv, 1, reason);
2511 reply("NSMSG_GHOST_KILLED", argv[1]);
2515 static NICKSERV_FUNC(cmd_vacation)
2517 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2518 reply("NSMSG_ON_VACATION");
2523 nickserv_saxdb_write(struct saxdb_context *ctx) {
2525 struct handle_info *hi;
2528 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2530 #ifdef WITH_PROTOCOL_BAHAMUT
2533 saxdb_start_record(ctx, iter_key(it), 0);
2534 if (hi->announcements != '?') {
2535 flags[0] = hi->announcements;
2537 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2540 struct handle_cookie *cookie = hi->cookie;
2543 switch (cookie->type) {
2544 case ACTIVATION: type = KEY_ACTIVATION; break;
2545 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2546 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2547 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2548 default: type = NULL; break;
2551 saxdb_start_record(ctx, KEY_COOKIE, 0);
2552 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2553 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2555 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2556 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2557 saxdb_end_record(ctx);
2561 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2563 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2567 for (ii=flen=0; handle_flags[ii]; ++ii)
2568 if (hi->flags & (1 << ii))
2569 flags[flen++] = handle_flags[ii];
2571 saxdb_write_string(ctx, KEY_FLAGS, flags);
2573 #ifdef WITH_PROTOCOL_BAHAMUT
2574 saxdb_write_int(ctx, KEY_ID, hi->id);
2577 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2578 if (hi->last_quit_host[0])
2579 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2580 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2581 if (hi->masks->used)
2582 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2584 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2586 struct string_list *slist;
2587 struct nick_info *ni;
2589 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2590 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2591 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2595 if (hi->opserv_level)
2596 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2597 if (hi->language && (hi->language != lang_C))
2598 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2599 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2600 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2601 if (hi->screen_width)
2602 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2603 if (hi->table_width)
2604 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2605 flags[0] = hi->userlist_style;
2607 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2608 saxdb_end_record(ctx);
2613 static handle_merge_func_t *handle_merge_func_list;
2614 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2617 reg_handle_merge_func(handle_merge_func_t func)
2619 if (handle_merge_func_used == handle_merge_func_size) {
2620 if (handle_merge_func_size) {
2621 handle_merge_func_size <<= 1;
2622 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2624 handle_merge_func_size = 8;
2625 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2628 handle_merge_func_list[handle_merge_func_used++] = func;
2631 static NICKSERV_FUNC(cmd_merge)
2633 struct handle_info *hi_from, *hi_to;
2634 struct userNode *last_user;
2635 struct userData *cList, *cListNext;
2636 unsigned int ii, jj, n;
2637 char buffer[MAXLEN];
2639 NICKSERV_MIN_PARMS(3);
2641 if (!(hi_from = get_victim_oper(user, argv[1])))
2643 if (!(hi_to = get_victim_oper(user, argv[2])))
2645 if (hi_to == hi_from) {
2646 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2650 for (n=0; n<handle_merge_func_used; n++)
2651 handle_merge_func_list[n](user, hi_to, hi_from);
2653 /* Append "from" handle's nicks to "to" handle's nick list. */
2655 struct nick_info *last_ni;
2656 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2657 last_ni->next = hi_from->nicks;
2659 while (hi_from->nicks) {
2660 hi_from->nicks->owner = hi_to;
2661 hi_from->nicks = hi_from->nicks->next;
2664 /* Merge the hostmasks. */
2665 for (ii=0; ii<hi_from->masks->used; ii++) {
2666 char *mask = hi_from->masks->list[ii];
2667 for (jj=0; jj<hi_to->masks->used; jj++)
2668 if (match_ircglobs(hi_to->masks->list[jj], mask))
2670 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2671 string_list_append(hi_to->masks, strdup(mask));
2674 /* Merge the lists of authed users. */
2676 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2677 last_user->next_authed = hi_from->users;
2679 hi_to->users = hi_from->users;
2681 /* Repoint the old "from" handle's users. */
2682 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2683 last_user->handle_info = hi_to;
2685 hi_from->users = NULL;
2687 /* Merge channel userlists. */
2688 for (cList=hi_from->channels; cList; cList=cListNext) {
2689 struct userData *cList2;
2690 cListNext = cList->u_next;
2691 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2692 if (cList->channel == cList2->channel)
2694 log_module(NS_LOG, LOG_DEBUG, "Merging %s->%s@%s: before %p->%p->%-p, %p->%p->%p",
2695 hi_from->handle, hi_to->handle, cList->channel->channel->name,
2696 cList->u_prev, cList, cList->u_next,
2697 (cList2?cList2->u_prev:0), cList2, (cList2?cList2->u_next:0));
2698 if (cList2 && (cList2->access >= cList->access)) {
2699 /* keep cList2 in hi_to; remove cList from hi_from */
2700 log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList);
2701 del_channel_user(cList, 1);
2704 /* remove the lower-ranking cList2 from hi_to */
2705 log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList2);
2706 del_channel_user(cList2, 1);
2708 /* cList needs to be moved from hi_from to hi_to */
2709 cList->handle = hi_to;
2710 /* Remove from linked list for hi_from */
2711 assert(!cList->u_prev);
2712 hi_from->channels = cList->u_next;
2714 cList->u_next->u_prev = cList->u_prev;
2715 /* Add to linked list for hi_to */
2716 cList->u_prev = NULL;
2717 cList->u_next = hi_to->channels;
2718 if (hi_to->channels)
2719 hi_to->channels->u_prev = cList;
2720 hi_to->channels = cList;
2721 log_module(NS_LOG, LOG_DEBUG, "Now %p->%p->%p",
2722 cList->u_prev, cList, cList->u_next);
2726 /* Do they get an OpServ level promotion? */
2727 if (hi_from->opserv_level > hi_to->opserv_level)
2728 hi_to->opserv_level = hi_from->opserv_level;
2730 /* What about last seen time? */
2731 if (hi_from->lastseen > hi_to->lastseen)
2732 hi_to->lastseen = hi_from->lastseen;
2734 /* Notify of success. */
2735 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2736 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2737 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2739 /* Unregister the "from" handle. */
2740 nickserv_unregister_handle(hi_from, NULL);
2745 struct nickserv_discrim {
2746 unsigned int limit, min_level, max_level;
2747 unsigned long flags_on, flags_off;
2748 time_t min_registered, max_registered;
2750 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2751 const char *nickmask;
2752 const char *hostmask;
2753 const char *handlemask;
2754 const char *emailmask;
2757 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2759 struct discrim_apply_info {
2760 struct nickserv_discrim *discrim;
2761 discrim_search_func func;
2762 struct userNode *source;
2763 unsigned int matched;
2766 static struct nickserv_discrim *
2767 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2770 struct nickserv_discrim *discrim;
2772 discrim = malloc(sizeof(*discrim));
2773 memset(discrim, 0, sizeof(*discrim));
2774 discrim->min_level = 0;
2775 discrim->max_level = ~0;
2776 discrim->limit = 50;
2777 discrim->min_registered = 0;
2778 discrim->max_registered = INT_MAX;
2779 discrim->lastseen = now;
2781 for (i=0; i<argc; i++) {
2782 if (i == argc - 1) {
2783 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2786 if (!irccasecmp(argv[i], "limit")) {
2787 discrim->limit = atoi(argv[++i]);
2788 } else if (!irccasecmp(argv[i], "flags")) {
2789 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2790 } else if (!irccasecmp(argv[i], "registered")) {
2791 const char *cmp = argv[++i];
2792 if (cmp[0] == '<') {
2793 if (cmp[1] == '=') {
2794 discrim->min_registered = now - ParseInterval(cmp+2);
2796 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2798 } else if (cmp[0] == '=') {
2799 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2800 } else if (cmp[0] == '>') {
2801 if (cmp[1] == '=') {
2802 discrim->max_registered = now - ParseInterval(cmp+2);
2804 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2807 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2809 } else if (!irccasecmp(argv[i], "seen")) {
2810 discrim->lastseen = now - ParseInterval(argv[++i]);
2811 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2812 discrim->nickmask = argv[++i];
2813 } else if (!irccasecmp(argv[i], "hostmask")) {
2815 if (!irccasecmp(argv[i], "exact")) {
2816 if (i == argc - 1) {
2817 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2820 discrim->hostmask_type = EXACT;
2821 } else if (!irccasecmp(argv[i], "subset")) {
2822 if (i == argc - 1) {
2823 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2826 discrim->hostmask_type = SUBSET;
2827 } else if (!irccasecmp(argv[i], "superset")) {
2828 if (i == argc - 1) {
2829 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2832 discrim->hostmask_type = SUPERSET;
2833 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2834 if (i == argc - 1) {
2835 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2838 discrim->hostmask_type = LASTQUIT;
2841 discrim->hostmask_type = SUPERSET;
2843 discrim->hostmask = argv[++i];
2844 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2845 if (!irccasecmp(argv[++i], "*")) {
2846 discrim->handlemask = 0;
2848 discrim->handlemask = argv[i];
2850 } else if (!irccasecmp(argv[i], "email")) {
2851 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2852 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2854 } else if (!irccasecmp(argv[++i], "*")) {
2855 discrim->emailmask = 0;
2857 discrim->emailmask = argv[i];
2859 } else if (!irccasecmp(argv[i], "access")) {
2860 const char *cmp = argv[++i];
2861 if (cmp[0] == '<') {
2862 if (discrim->min_level == 0) discrim->min_level = 1;
2863 if (cmp[1] == '=') {
2864 discrim->max_level = strtoul(cmp+2, NULL, 0);
2866 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
2868 } else if (cmp[0] == '=') {
2869 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
2870 } else if (cmp[0] == '>') {
2871 if (cmp[1] == '=') {
2872 discrim->min_level = strtoul(cmp+2, NULL, 0);
2874 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
2877 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2880 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
2891 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
2893 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
2894 || (discrim->flags_off & hi->flags)
2895 || (discrim->min_registered > hi->registered)
2896 || (discrim->max_registered < hi->registered)
2897 || (discrim->lastseen < (hi->users?now:hi->lastseen))
2898 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
2899 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
2900 || (discrim->min_level > hi->opserv_level)
2901 || (discrim->max_level < hi->opserv_level)) {
2904 if (discrim->hostmask) {
2906 for (i=0; i<hi->masks->used; i++) {
2907 const char *mask = hi->masks->list[i];
2908 if ((discrim->hostmask_type == SUBSET)
2909 && (match_ircglobs(discrim->hostmask, mask))) break;
2910 else if ((discrim->hostmask_type == EXACT)
2911 && !irccasecmp(discrim->hostmask, mask)) break;
2912 else if ((discrim->hostmask_type == SUPERSET)
2913 && (match_ircglobs(mask, discrim->hostmask))) break;
2914 else if ((discrim->hostmask_type == LASTQUIT)
2915 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
2917 if (i==hi->masks->used) return 0;
2919 if (discrim->nickmask) {
2920 struct nick_info *nick = hi->nicks;
2922 if (match_ircglob(nick->nick, discrim->nickmask)) break;
2925 if (!nick) return 0;
2931 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
2933 dict_iterator_t it, next;
2934 unsigned int matched;
2936 for (it = dict_first(nickserv_handle_dict), matched = 0;
2937 it && (matched < discrim->limit);
2939 next = iter_next(it);
2940 if (nickserv_discrim_match(discrim, iter_data(it))) {
2941 dsf(source, iter_data(it));
2949 search_print_func(struct userNode *source, struct handle_info *match)
2951 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
2955 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
2960 search_unregister_func (struct userNode *source, struct handle_info *match)
2962 if (oper_has_access(source, nickserv, match->opserv_level, 0))
2963 nickserv_unregister_handle(match, source);
2967 nickserv_sort_accounts_by_access(const void *a, const void *b)
2969 const struct handle_info *hi_a = *(const struct handle_info**)a;
2970 const struct handle_info *hi_b = *(const struct handle_info**)b;
2971 if (hi_a->opserv_level != hi_b->opserv_level)
2972 return hi_b->opserv_level - hi_a->opserv_level;
2973 return irccasecmp(hi_a->handle, hi_b->handle);
2977 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
2979 struct handle_info_list hil;
2980 struct helpfile_table tbl;
2985 memset(&hil, 0, sizeof(hil));
2986 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2987 struct handle_info *hi = iter_data(it);
2988 if (hi->opserv_level)
2989 handle_info_list_append(&hil, hi);
2991 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
2992 tbl.length = hil.used + 1;
2994 tbl.flags = TABLE_NO_FREE;
2995 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
2996 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
2999 for (ii = 0; ii < hil.used; ) {
3000 ary = malloc(tbl.width * sizeof(ary[0]));
3001 ary[0] = hil.list[ii]->handle;
3002 ary[1] = strtab(hil.list[ii]->opserv_level);
3003 tbl.contents[++ii] = ary;
3005 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3006 reply("MSG_MATCH_COUNT", hil.used);
3007 for (ii = 0; ii < hil.used; )
3008 free(tbl.contents[++ii]);
3013 static NICKSERV_FUNC(cmd_search)
3015 struct nickserv_discrim *discrim;
3016 discrim_search_func action;
3017 struct svccmd *subcmd;
3018 unsigned int matches;
3021 NICKSERV_MIN_PARMS(3);
3022 sprintf(buf, "search %s", argv[1]);
3023 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3024 if (!irccasecmp(argv[1], "print"))
3025 action = search_print_func;
3026 else if (!irccasecmp(argv[1], "count"))
3027 action = search_count_func;
3028 else if (!irccasecmp(argv[1], "unregister"))
3029 action = search_unregister_func;
3031 reply("NSMSG_INVALID_ACTION", argv[1]);
3035 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3038 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3042 if (action == search_print_func)
3043 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3044 else if (action == search_count_func)
3045 discrim->limit = INT_MAX;
3047 matches = nickserv_discrim_search(discrim, action, user);
3050 reply("MSG_MATCH_COUNT", matches);
3052 reply("MSG_NO_MATCHES");
3058 static MODCMD_FUNC(cmd_checkpass)
3060 struct handle_info *hi;
3062 NICKSERV_MIN_PARMS(3);
3063 if (!(hi = get_handle_info(argv[1]))) {
3064 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3067 if (checkpass(argv[2], hi->passwd))
3068 reply("CHECKPASS_YES");
3070 reply("CHECKPASS_NO");
3076 nickserv_db_read_handle(const char *handle, dict_t obj)
3079 struct string_list *masks, *slist;
3080 struct handle_info *hi;
3081 struct userNode *authed_users;
3082 unsigned long int id;
3086 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3087 id = str ? strtoul(str, NULL, 0) : 0;
3088 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3090 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3093 if ((hi = get_handle_info(handle))) {
3094 authed_users = hi->users;
3096 dict_remove(nickserv_handle_dict, hi->handle);
3098 authed_users = NULL;
3100 hi = register_handle(handle, str, id);
3102 hi->users = authed_users;
3103 while (authed_users) {
3104 authed_users->handle_info = hi;
3105 authed_users = authed_users->next_authed;
3108 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3109 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3110 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3111 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3112 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3113 hi->language = language_find(str ? str : "C");
3114 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3115 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3116 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3117 if (str) hi->infoline = strdup(str);
3118 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3119 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3120 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3121 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3122 /* We want to read the nicks even if disable_nicks is set. This is so
3123 * that we don't lose the nick data entirely. */
3124 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3126 for (ii=0; ii<slist->used; ii++)
3127 register_nick(slist->list[ii], hi);
3129 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3131 for (ii=0; str[ii]; ii++)
3132 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3134 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3135 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3136 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3137 hi->announcements = str ? str[0] : '?';
3138 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3139 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3140 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3141 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3142 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3143 if (!str) str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3144 if (str) safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3145 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3146 if (str) nickserv_set_email_addr(hi, str);
3147 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3148 if (str) hi->epithet = strdup(str);
3149 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3151 const char *data, *type, *expires, *cookie_str;
3152 struct handle_cookie *cookie;
3154 cookie = calloc(1, sizeof(*cookie));
3155 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3156 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3157 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3158 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3159 if (!type || !expires || !cookie_str) {
3160 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3163 if (!irccasecmp(type, KEY_ACTIVATION))
3164 cookie->type = ACTIVATION;
3165 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3166 cookie->type = PASSWORD_CHANGE;
3167 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3168 cookie->type = EMAIL_CHANGE;
3169 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3170 cookie->type = ALLOWAUTH;
3172 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3175 cookie->expires = strtoul(expires, NULL, 0);
3176 if (cookie->expires < now)
3179 cookie->data = strdup(data);
3180 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3184 nickserv_bake_cookie(cookie);
3186 nickserv_free_cookie(cookie);
3191 nickserv_saxdb_read(dict_t db) {
3193 struct record_data *rd;
3195 for (it=dict_first(db); it; it=iter_next(it)) {
3197 nickserv_db_read_handle(iter_key(it), rd->d.object);
3202 static NICKSERV_FUNC(cmd_mergedb)
3204 struct timeval start, stop;
3207 NICKSERV_MIN_PARMS(2);
3208 gettimeofday(&start, NULL);
3209 if (!(db = parse_database(argv[1]))) {
3210 reply("NSMSG_DB_UNREADABLE", argv[1]);
3213 nickserv_saxdb_read(db);
3215 gettimeofday(&stop, NULL);
3216 stop.tv_sec -= start.tv_sec;
3217 stop.tv_usec -= start.tv_usec;
3218 if (stop.tv_usec < 0) {
3220 stop.tv_usec += 1000000;
3222 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3227 expire_handles(UNUSED_ARG(void *data))
3229 dict_iterator_t it, next;
3231 struct handle_info *hi;
3233 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3234 next = iter_next(it);
3236 if ((hi->opserv_level > 0)
3238 || HANDLE_FLAGGED(hi, FROZEN)
3239 || HANDLE_FLAGGED(hi, NODELETE)) {
3242 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3243 if ((now - hi->lastseen) > expiry) {
3244 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3245 nickserv_unregister_handle(hi, NULL);
3249 if (nickserv_conf.handle_expire_frequency)
3250 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3254 nickserv_load_dict(const char *fname)
3258 if (!(file = fopen(fname, "r"))) {
3259 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3262 while (!feof(file)) {
3263 fgets(line, sizeof(line), file);
3266 if (line[strlen(line)-1] == '\n')
3267 line[strlen(line)-1] = 0;
3268 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3271 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3274 static enum reclaim_action
3275 reclaim_action_from_string(const char *str) {
3277 return RECLAIM_NONE;
3278 else if (!irccasecmp(str, "warn"))
3279 return RECLAIM_WARN;
3280 else if (!irccasecmp(str, "svsnick"))
3281 return RECLAIM_SVSNICK;
3282 else if (!irccasecmp(str, "kill"))
3283 return RECLAIM_KILL;
3285 return RECLAIM_NONE;
3289 nickserv_conf_read(void)
3291 dict_t conf_node, child;
3295 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3296 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3299 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3300 if (!str) str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3301 if (nickserv_conf.valid_handle_regex_set) regfree(&nickserv_conf.valid_handle_regex);
3303 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3304 nickserv_conf.valid_handle_regex_set = !err;
3305 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3307 nickserv_conf.valid_handle_regex_set = 0;
3309 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3310 if (nickserv_conf.valid_nick_regex_set) regfree(&nickserv_conf.valid_nick_regex);
3312 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3313 nickserv_conf.valid_nick_regex_set = !err;
3314 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3316 nickserv_conf.valid_nick_regex_set = 0;
3318 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3319 if (!str) str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3320 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3321 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3322 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3323 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3324 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3325 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3326 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3327 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3328 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3329 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3330 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3331 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3332 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3333 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3334 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3335 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3336 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3337 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3338 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3339 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3340 if (!str) str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3341 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3342 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3343 if (!str) str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3344 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3345 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3346 if (!str) str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3347 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3348 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3349 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3350 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3351 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3352 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3353 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3354 if (!nickserv_conf.disable_nicks) {
3355 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3356 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3357 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3358 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3359 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3360 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3361 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3362 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3364 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3365 for (it=dict_first(child); it; it=iter_next(it)) {
3366 const char *key = iter_key(it), *value;
3370 if (!strncasecmp(key, "uc_", 3))
3371 flag = toupper(key[3]);
3372 else if (!strncasecmp(key, "lc_", 3))
3373 flag = tolower(key[3]);
3377 if ((pos = handle_inverse_flags[flag])) {
3378 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3379 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3382 if (nickserv_conf.weak_password_dict)
3383 dict_delete(nickserv_conf.weak_password_dict);
3384 nickserv_conf.weak_password_dict = dict_new();
3385 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3386 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3387 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3388 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3390 nickserv_load_dict(str);
3391 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3392 if (nickserv && str)
3393 NickChange(nickserv, str, 0);
3394 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3395 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3396 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3397 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3398 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3399 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3400 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3401 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3402 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3403 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3404 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3405 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3406 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3407 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3408 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3409 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3410 str = conf_get_data("server/network", RECDB_QSTRING);
3411 nickserv_conf.network_name = str ? str : "some IRC network";
3412 if (!nickserv_conf.auth_policer_params) {
3413 nickserv_conf.auth_policer_params = policer_params_new();
3414 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3415 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3417 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3418 for (it=dict_first(child); it; it=iter_next(it))
3419 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3423 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3424 char newnick[NICKLEN+1];
3433 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3435 case RECLAIM_SVSNICK:
3437 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3438 } while (GetUserH(newnick));
3439 irc_svsnick(nickserv, user, newnick);
3442 irc_kill(nickserv, user, "NSMSG_RECLAIM_KILL");
3448 nickserv_reclaim_p(void *data) {
3449 struct userNode *user = data;
3450 struct nick_info *ni = get_nick_info(user->nick);
3452 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3456 check_user_nick(struct userNode *user) {
3457 struct nick_info *ni;
3458 user->modes &= ~FLAGS_REGNICK;
3459 if (!(ni = get_nick_info(user->nick)))
3461 if (user->handle_info == ni->owner) {
3462 user->modes |= FLAGS_REGNICK;
3466 if (nickserv_conf.warn_nick_owned)
3467 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3468 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3470 if (nickserv_conf.auto_reclaim_delay)
3471 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3473 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3478 handle_new_user(struct userNode *user)
3480 return check_user_nick(user);
3484 handle_account(struct userNode *user, const char *stamp)
3486 struct handle_info *hi;
3488 #ifdef WITH_PROTOCOL_P10
3489 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3491 hi = dict_find(nickserv_id_dict, stamp, NULL);
3495 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3498 set_user_handle_info(user, hi, 0);
3500 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3505 handle_nick_change(struct userNode *user, const char *old_nick)
3507 struct handle_info *hi;
3509 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3510 dict_remove(nickserv_allow_auth_dict, old_nick);
3511 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3513 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3514 check_user_nick(user);
3518 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3520 dict_remove(nickserv_allow_auth_dict, user->nick);
3521 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3522 set_user_handle_info(user, NULL, 0);
3525 static struct modcmd *
3526 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3528 if (min_level > 0) {
3530 sprintf(buf, "%u", min_level);
3531 if (must_be_qualified) {
3532 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3534 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3536 } else if (min_level == 0) {
3537 if (must_be_qualified) {
3538 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3540 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3543 if (must_be_qualified) {
3544 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3546 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3552 nickserv_db_cleanup(void)
3554 unreg_del_user_func(nickserv_remove_user);
3555 userList_clean(&curr_helpers);
3556 policer_params_delete(nickserv_conf.auth_policer_params);
3557 dict_delete(nickserv_handle_dict);
3558 dict_delete(nickserv_nick_dict);
3559 dict_delete(nickserv_opt_dict);
3560 dict_delete(nickserv_allow_auth_dict);
3561 dict_delete(nickserv_email_dict);
3562 dict_delete(nickserv_id_dict);
3563 dict_delete(nickserv_conf.weak_password_dict);
3564 free(auth_func_list);
3565 free(unreg_func_list);
3567 free(allowauth_func_list);
3568 free(handle_merge_func_list);
3569 free(failpw_func_list);
3570 if (nickserv_conf.valid_handle_regex_set)
3571 regfree(&nickserv_conf.valid_handle_regex);
3572 if (nickserv_conf.valid_nick_regex_set)
3573 regfree(&nickserv_conf.valid_nick_regex);
3577 init_nickserv(const char *nick)
3580 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3581 reg_new_user_func(handle_new_user);
3582 reg_nick_change_func(handle_nick_change);
3583 reg_del_user_func(nickserv_remove_user);
3584 reg_account_func(handle_account);
3586 /* set up handle_inverse_flags */
3587 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3588 for (i=0; handle_flags[i]; i++) {
3589 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3590 flag_access_levels[i] = 0;
3593 conf_register_reload(nickserv_conf_read);
3594 nickserv_opt_dict = dict_new();
3595 nickserv_email_dict = dict_new();
3596 dict_set_free_keys(nickserv_email_dict, free);
3597 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3599 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3600 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3601 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3602 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3603 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3604 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3605 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3606 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3607 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3608 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3609 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3610 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3611 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3612 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3613 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3614 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3615 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3616 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3617 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3618 if (!nickserv_conf.disable_nicks) {
3619 /* nick management commands */
3620 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3621 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3622 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3623 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3624 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3625 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3627 if (nickserv_conf.email_enabled) {
3628 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3629 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3630 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3631 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3632 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3634 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3635 /* miscellaneous commands */
3636 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3637 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3638 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3639 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3640 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3642 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3643 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3644 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3645 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3646 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3647 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3648 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3649 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3650 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3651 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3652 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3653 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3654 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3655 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3656 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3658 nickserv_handle_dict = dict_new();
3659 dict_set_free_keys(nickserv_handle_dict, free);
3660 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3662 nickserv_id_dict = dict_new();
3663 dict_set_free_keys(nickserv_id_dict, free);
3665 nickserv_nick_dict = dict_new();
3666 dict_set_free_data(nickserv_nick_dict, free_nick_info);
3668 nickserv_allow_auth_dict = dict_new();
3670 userList_init(&curr_helpers);
3673 nickserv = AddService(nick, "Nick Services");
3674 nickserv_service = service_register(nickserv, 0);
3676 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3677 reg_exit_func(nickserv_db_cleanup);
3678 if(nickserv_conf.handle_expire_frequency)
3679 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3680 message_register_table(msgtab);