1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for gag_create(), opserv_bad_channel() */
34 #define NICKSERV_CONF_NAME "services/nickserv"
36 #define KEY_DISABLE_NICKS "disable_nicks"
37 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
38 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
39 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
40 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
41 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
42 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
43 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
44 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
45 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
46 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
47 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
48 #define KEY_MODOPER_LEVEL "modoper_level"
49 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
50 #define KEY_FLAG_LEVELS "flag_levels"
51 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
52 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
53 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
54 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
55 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
56 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
57 #define KEY_DICT_FILE "dict_file"
58 #define KEY_NICK "nick"
59 #define KEY_LANGUAGE "language"
60 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
61 #define KEY_AUTOGAG_DURATION "autogag_duration"
62 #define KEY_AUTH_POLICER "auth_policer"
63 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
64 #define KEY_EMAIL_ENABLED "email_enabled"
65 #define KEY_EMAIL_REQUIRED "email_required"
66 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
67 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
68 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
71 #define KEY_PASSWD "passwd"
72 #define KEY_NICKS "nicks"
73 #define KEY_MASKS "masks"
74 #define KEY_OPSERV_LEVEL "opserv_level"
75 #define KEY_FLAGS "flags"
76 #define KEY_REGISTER_ON "register"
77 #define KEY_LAST_SEEN "lastseen"
78 #define KEY_INFO "info"
79 #define KEY_USERLIST_STYLE "user_style"
80 #define KEY_SCREEN_WIDTH "screen_width"
81 #define KEY_LAST_AUTHED_HOST "last_authed_host"
82 #define KEY_LAST_QUIT_HOST "last_quit_host"
83 #define KEY_EMAIL_ADDR "email_addr"
84 #define KEY_COOKIE "cookie"
85 #define KEY_COOKIE_DATA "data"
86 #define KEY_COOKIE_TYPE "type"
87 #define KEY_COOKIE_EXPIRES "expires"
88 #define KEY_ACTIVATION "activation"
89 #define KEY_PASSWORD_CHANGE "password change"
90 #define KEY_EMAIL_CHANGE "email change"
91 #define KEY_ALLOWAUTH "allowauth"
92 #define KEY_EPITHET "epithet"
93 #define KEY_TABLE_WIDTH "table_width"
94 #define KEY_ANNOUNCEMENTS "announcements"
95 #define KEY_MAXLOGINS "maxlogins"
97 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
99 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
100 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
101 typedef OPTION_FUNC(option_func_t);
103 DEFINE_LIST(handle_info_list, struct handle_info*);
105 #define NICKSERV_MIN_PARMS(N) do { \
107 reply("MSG_MISSING_PARAMS", argv[0]); \
108 svccmd_send_help(user, nickserv, cmd); \
112 struct userNode *nickserv;
113 struct userList curr_helpers;
114 const char *handle_flags = HANDLE_FLAGS;
116 static struct module *nickserv_module;
117 static struct service *nickserv_service;
118 static struct log_type *NS_LOG;
119 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
120 static dict_t nickserv_id_dict; /* contains struct handle_info* */
121 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
122 static dict_t nickserv_opt_dict; /* contains option_func_t* */
123 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
124 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
125 static char handle_inverse_flags[256];
126 static unsigned int flag_access_levels[32];
127 static const struct message_entry msgtab[] = {
128 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
129 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
130 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
131 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
132 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
133 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
134 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
135 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
136 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
137 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
138 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
139 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
140 { "NSMSG_USE_COOKIE_REGISTER", "To activate your account, you must check your email for the \"cookie\" that has been mailed to it. When you have it, use the $bcookie$b command to complete registration." },
141 { "NSMSG_USE_COOKIE_RESETPASS", "A cookie has been mailed to your account's email address. You must check your email and use the $bcookie$b command to confirm the password change." },
142 { "NSMSG_USE_COOKIE_EMAIL_1", "A cookie has been mailed to the new address you requested. To finish setting your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
143 { "NSMSG_USE_COOKIE_EMAIL_2", "A cookie has been generated, and half mailed to each your old and new addresses. To finish changing your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
144 { "NSMSG_USE_COOKIE_AUTH", "A cookie has been generated and sent to your email address. Once you have checked your email and received the cookie, auth using the $bcookie$b command." },
145 { "NSMSG_COOKIE_LIVE", "Account $b%s$b already has a cookie active. Please either finish using that cookie, wait for it to expire, or auth to the account and use the $bdelcookie$b command." },
146 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
147 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
148 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
149 { "NSMSG_BAD_COOKIE", "That cookie is not the right one. Please make sure you are copying it EXACTLY from the email; it is case-sensitive, so $bABC$b is different from $babc$b." },
150 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
151 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
152 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
153 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
154 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
155 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
156 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
157 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
158 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
159 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
160 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
161 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
162 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
163 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
164 { "NSMSG_USE_AUTHCOOKIE", "Your hostmask is not valid for account $b%s$b. Please use the $bauthcookie$b command to grant yourself access. (/msg $S authcookie %s)" },
165 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
166 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
167 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
168 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
169 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
170 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
171 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
172 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
173 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
174 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
175 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
176 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
177 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
178 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
179 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
180 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
181 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
182 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
183 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
184 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
185 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
186 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
187 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
188 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
189 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
190 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
191 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
192 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
193 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
194 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
195 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
196 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
197 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
198 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
199 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
200 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
201 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
202 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
203 { "NSMSG_WEAK_PASSWORD", "WARNING: You are using a password that is considered weak (easy to guess). It is STRONGLY recommended you change it (now, if not sooner) by typing \"/msg $S@$s PASS oldpass newpass\" (with your current password and a new password)." },
204 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
205 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
206 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
207 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
208 { "NSMSG_AUTH_ALLOWED_MSG", "You may now authenticate to account $b%s$b by typing $b/msg $N@$s auth %s password$b (using your password). If you will be using this computer regularly, please type $b/msg $N addmask$b (AFTER you auth) to permanently add your hostmask." },
209 { "NSMSG_AUTH_ALLOWED_EMAIL", "You may also (after you auth) type $b/msg $N set email user@your.isp$b to set an email address. This will let you use the $bauthcookie$b command to be authenticated in the future." },
210 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
211 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
212 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
213 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
214 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
215 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
216 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
217 { "NSMSG_PASS_SUCCESS", "Password changed." },
218 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
219 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
220 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
221 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
222 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
223 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
224 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
225 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
226 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
227 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
228 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
229 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
230 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
231 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
232 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
233 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
234 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
235 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
236 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
237 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
238 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
239 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
240 { "NSMSG_NO_ACCESS", "Access denied." },
241 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
242 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
243 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
244 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
245 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
246 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
247 { "NSMSG_BAD_HANDLE", "Account $b%s$b not registered because it is in use by a network service, is too long, or contains invalid characters." },
248 { "NSMSG_BAD_NICK", "Nickname $b%s$b not registered because it is in use by a network service, is too long, or contains invalid characters." },
249 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
250 { "NSMSG_FAIL_RENAME", "Account $b%s$b not renamed to $b%s$b because it is in use by a network services, or contains invalid characters." },
251 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
252 { "NSMSG_SEARCH_MATCH", "Match: %s" },
253 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
254 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
255 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
256 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
257 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
258 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
259 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
260 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
261 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
262 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
263 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
264 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
265 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
266 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
267 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
268 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
269 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
270 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
271 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
272 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
273 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
274 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
275 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
276 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
277 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
278 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
279 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
280 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
281 { "NSEMAIL_ACTIVATION_BODY", "This email has been sent to verify that this email address belongs to the person who tried to register an account on %1$s. Your cookie is:\n %2$s\nTo verify your email address and complete the account registration, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
282 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
283 { "NSEMAIL_PASSWORD_CHANGE_BODY", "This email has been sent to verify that you wish to change the password on your account %5$s. Your cookie is %2$s.\nTo complete the password change, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request your password to be changed, you do not need to do anything. Please contact the %1$s staff if you have questions." },
284 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
285 { "NSEMAIL_EMAIL_CHANGE_BODY_NEW", "This email has been sent to verify that your email address belongs to the same person as account %5$s on %1$s. The SECOND HALF of your cookie is %2$.*6$s.\nTo verify your address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s ?????%2$.*6$s\n(Replace the ????? with the FIRST HALF of the cookie, as sent to your OLD email address.)\nIf you did NOT request this email address to be associated with this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
286 { "NSEMAIL_EMAIL_CHANGE_BODY_OLD", "This email has been sent to verify that you want to change your email for account %5$s on %1$s from this address to %7$s. The FIRST HALF of your cookie is %2$.*6$s\nTo verify your new address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$.*6$s?????\n(Replace the ????? with the SECOND HALF of the cookie, as sent to your NEW email address.)\nIf you did NOT request this change of email address, you do not need to do anything. Please contact the %1$s staff if you have questions." },
287 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
288 { "NSEMAIL_EMAIL_VERIFY_BODY", "This email has been sent to verify that this address belongs to the same person as %5$s on %1$s. Your cookie is %2$s.\nTo verify your address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request this email address to be associated with this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
289 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
290 { "NSEMAIL_ALLOWAUTH_BODY", "This email has been sent to let you authenticate (auth) to account %5$s on %1$s. Your cookie is %2$s.\nTo auth to that account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %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." },
291 { "CHECKPASS_YES", "Yes." },
292 { "CHECKPASS_NO", "No." },
296 enum reclaim_action {
302 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
303 static void nickserv_reclaim_p(void *data);
306 unsigned int disable_nicks : 1;
307 unsigned int valid_handle_regex_set : 1;
308 unsigned int valid_nick_regex_set : 1;
309 unsigned int autogag_enabled : 1;
310 unsigned int email_enabled : 1;
311 unsigned int email_required : 1;
312 unsigned int default_hostmask : 1;
313 unsigned int warn_nick_owned : 1;
314 unsigned int warn_clone_auth : 1;
315 unsigned long nicks_per_handle;
316 unsigned long password_min_length;
317 unsigned long password_min_digits;
318 unsigned long password_min_upper;
319 unsigned long password_min_lower;
320 unsigned long db_backup_frequency;
321 unsigned long handle_expire_frequency;
322 unsigned long autogag_duration;
323 unsigned long email_visible_level;
324 unsigned long cookie_timeout;
325 unsigned long handle_expire_delay;
326 unsigned long nochan_handle_expire_delay;
327 unsigned long modoper_level;
328 unsigned long set_epithet_level;
329 unsigned long handles_per_email;
330 unsigned long email_search_level;
331 const char *network_name;
332 const char *titlehost_suffix;
333 regex_t valid_handle_regex;
334 regex_t valid_nick_regex;
335 dict_t weak_password_dict;
336 struct policer_params *auth_policer_params;
337 enum reclaim_action reclaim_action;
338 enum reclaim_action auto_reclaim_action;
339 unsigned long auto_reclaim_delay;
340 unsigned char default_maxlogins;
341 unsigned char hard_maxlogins;
344 /* We have 2^32 unique account IDs to use. */
345 unsigned long int highest_id = 0;
348 canonicalize_hostmask(char *mask)
350 char *out = mask, *temp;
351 if ((temp = strchr(mask, '!'))) {
353 while (*temp) *out++ = *temp++;
359 static struct handle_info *
360 register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
362 struct handle_info *hi;
364 #ifdef WITH_PROTOCOL_BAHAMUT
365 char id_base64[IDLEN + 1];
368 /* Assign a unique account ID to the account; note that 0 is
369 an invalid account ID. 1 is therefore the first account ID. */
371 id = 1 + highest_id++;
373 /* Note: highest_id is and must always be the highest ID. */
374 if(id > highest_id) {
378 inttobase64(id_base64, id, IDLEN);
380 /* Make sure an account with the same ID doesn't exist. If a
381 duplicate is found, log some details and assign a new one.
382 This should be impossible, but it never hurts to expect it. */
383 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
384 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
390 hi = calloc(1, sizeof(*hi));
391 hi->userlist_style = HI_DEFAULT_STYLE;
392 hi->announcements = '?';
393 hi->handle = strdup(handle);
394 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
396 dict_insert(nickserv_handle_dict, hi->handle, hi);
398 #ifdef WITH_PROTOCOL_BAHAMUT
400 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
407 register_nick(const char *nick, struct handle_info *owner)
409 struct nick_info *ni;
410 ni = malloc(sizeof(struct nick_info));
411 safestrncpy(ni->nick, nick, sizeof(ni->nick));
413 ni->next = owner->nicks;
415 dict_insert(nickserv_nick_dict, ni->nick, ni);
419 free_nick_info(void *vni)
421 struct nick_info *ni = vni;
426 delete_nick(struct nick_info *ni)
428 struct nick_info *last, *next;
429 struct userNode *user;
430 /* Check to see if we should mark a user as unregistered. */
431 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
432 user->modes &= ~FLAGS_REGNICK;
435 /* Remove ni from the nick_info linked list. */
436 if (ni == ni->owner->nicks) {
437 ni->owner->nicks = ni->next;
439 last = ni->owner->nicks;
445 last->next = next->next;
447 dict_remove(nickserv_nick_dict, ni->nick);
450 static unreg_func_t *unreg_func_list;
451 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
454 reg_unreg_func(unreg_func_t func)
456 if (unreg_func_used == unreg_func_size) {
457 if (unreg_func_size) {
458 unreg_func_size <<= 1;
459 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
462 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
465 unreg_func_list[unreg_func_used++] = func;
469 nickserv_free_cookie(void *data)
471 struct handle_cookie *cookie = data;
472 if (cookie->hi) cookie->hi->cookie = NULL;
473 if (cookie->data) free(cookie->data);
478 free_handle_info(void *vhi)
480 struct handle_info *hi = vhi;
482 #ifdef WITH_PROTOCOL_BAHAMUT
485 inttobase64(id, hi->id, IDLEN);
486 dict_remove(nickserv_id_dict, id);
489 free_string_list(hi->masks);
493 delete_nick(hi->nicks);
497 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
498 nickserv_free_cookie(hi->cookie);
500 if (hi->email_addr) {
501 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
502 handle_info_list_remove(hil, hi);
504 dict_remove(nickserv_email_dict, hi->email_addr);
509 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
512 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
516 for (n=0; n<unreg_func_used; n++)
517 unreg_func_list[n](notify, hi);
519 set_user_handle_info(hi->users, NULL, 0);
521 if (nickserv_conf.disable_nicks)
522 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
524 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
526 dict_remove(nickserv_handle_dict, hi->handle);
530 get_handle_info(const char *handle)
532 return dict_find(nickserv_handle_dict, handle, 0);
536 get_nick_info(const char *nick)
538 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
542 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
547 for (nn=0; nn<channel->members.used; ++nn) {
548 mn = channel->members.list[nn];
549 if ((mn->user != except) && (mn->user->handle_info == handle))
556 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
557 if (!user->handle_info) {
559 send_message(user, bot, "MSG_AUTHENTICATE");
563 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
565 send_message(user, bot, "NSMSG_NO_ACCESS");
569 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
571 send_message(user, bot, "MSG_OPER_SUSPENDED");
575 if (user->handle_info->opserv_level < min_level) {
577 send_message(user, bot, "NSMSG_NO_ACCESS");
585 is_valid_handle(const char *handle)
587 struct userNode *user;
588 /* cant register a juped nick/service nick as handle, to prevent confusion */
589 user = GetUserH(handle);
590 if (user && IsLocal(user))
592 /* check against maximum length */
593 if (strlen(handle) > NICKSERV_HANDLE_LEN)
595 /* for consistency, only allow account names that could be nicks */
596 if (!is_valid_nick(handle))
598 /* disallow account names that look like bad words */
599 if (opserv_bad_channel(handle))
601 /* test either regex or containing all valid chars */
602 if (nickserv_conf.valid_handle_regex_set) {
603 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
606 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
607 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
611 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
616 is_registerable_nick(const char *nick)
618 /* make sure it could be used as an account name */
619 if (!is_valid_handle(nick))
622 if (strlen(nick) > NICKLEN)
624 /* test either regex or as valid handle */
625 if (nickserv_conf.valid_nick_regex_set) {
626 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
629 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
630 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
638 is_valid_email_addr(const char *email)
640 return strchr(email, '@') != NULL;
644 visible_email_addr(struct userNode *user, struct handle_info *hi)
646 if (hi->email_addr) {
647 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
648 return hi->email_addr;
658 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
660 struct handle_info *hi;
661 struct userNode *target;
665 if (!(hi = get_handle_info(++name))) {
666 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
671 if (!(target = GetUserH(name))) {
672 send_message(user, service, "MSG_NICK_UNKNOWN", name);
675 if (IsLocal(target)) {
676 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
679 if (!(hi = target->handle_info)) {
680 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
688 oper_outranks(struct userNode *user, struct handle_info *hi) {
689 if (user->handle_info->opserv_level > hi->opserv_level)
691 if (user->handle_info->opserv_level == hi->opserv_level) {
692 if ((user->handle_info->opserv_level == 1000)
693 || (user->handle_info == hi)
694 || ((user->handle_info->opserv_level == 0)
695 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
696 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
700 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
704 static struct handle_info *
705 get_victim_oper(struct userNode *user, const char *target)
707 struct handle_info *hi;
708 if (!(hi = smart_get_handle_info(nickserv, user, target)))
710 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
711 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
714 return oper_outranks(user, hi) ? hi : NULL;
718 valid_user_for(struct userNode *user, struct handle_info *hi)
722 /* If no hostmasks on the account, allow it. */
723 if (!hi->masks->used)
725 /* If any hostmask matches, allow it. */
726 for (ii=0; ii<hi->masks->used; ii++)
727 if (user_matches_glob(user, hi->masks->list[ii], 0))
729 /* If they are allowauthed to this account, allow it (removing the aa). */
730 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
731 dict_remove(nickserv_allow_auth_dict, user->nick);
734 /* The user is not allowed to use this account. */
739 is_secure_password(const char *handle, const char *pass, struct userNode *user)
742 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
744 if (len < nickserv_conf.password_min_length) {
746 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
749 if (!irccasecmp(pass, handle)) {
751 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
754 dict_find(nickserv_conf.weak_password_dict, pass, &i);
757 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
760 for (i=0; i<len; i++) {
761 if (isdigit(pass[i]))
763 if (isupper(pass[i]))
765 if (islower(pass[i]))
768 if ((cnt_lower < nickserv_conf.password_min_lower)
769 || (cnt_upper < nickserv_conf.password_min_upper)
770 || (cnt_digits < nickserv_conf.password_min_digits)) {
772 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
778 static auth_func_t *auth_func_list;
779 static unsigned int auth_func_size = 0, auth_func_used = 0;
782 reg_auth_func(auth_func_t func)
784 if (auth_func_used == auth_func_size) {
785 if (auth_func_size) {
786 auth_func_size <<= 1;
787 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
790 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
793 auth_func_list[auth_func_used++] = func;
796 static handle_rename_func_t *rf_list;
797 static unsigned int rf_list_size, rf_list_used;
800 reg_handle_rename_func(handle_rename_func_t func)
802 if (rf_list_used == rf_list_size) {
805 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
808 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
811 rf_list[rf_list_used++] = func;
815 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
818 struct handle_info *old_info;
820 /* This can happen if somebody uses COOKIE while authed, or if
821 * they re-auth to their current handle (which is silly, but users
823 if (user->handle_info == hi)
826 if (user->handle_info) {
827 struct userNode *other;
830 userList_remove(&curr_helpers, user);
832 /* remove from next_authed linked list */
833 if (user->handle_info->users == user) {
834 user->handle_info->users = user->next_authed;
836 for (other = user->handle_info->users;
837 other->next_authed != user;
838 other = other->next_authed) ;
839 other->next_authed = user->next_authed;
841 /* if nobody left on old handle, and they're not an oper, remove !god */
842 if (!user->handle_info->users && !user->handle_info->opserv_level)
843 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
844 /* record them as being last seen at this time */
845 user->handle_info->lastseen = now;
846 /* and record their hostmask */
847 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
849 old_info = user->handle_info;
850 user->handle_info = hi;
851 if (hi && !hi->users && !hi->opserv_level)
852 HANDLE_CLEAR_FLAG(hi, HELPING);
853 for (n=0; n<auth_func_used; n++)
854 auth_func_list[n](user, old_info);
856 struct nick_info *ni;
858 HANDLE_CLEAR_FLAG(hi, FROZEN);
859 if (nickserv_conf.warn_clone_auth) {
860 struct userNode *other;
861 for (other = hi->users; other; other = other->next_authed)
862 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
864 user->next_authed = hi->users;
868 userList_append(&curr_helpers, user);
871 #ifdef WITH_PROTOCOL_BAHAMUT
872 /* Stamp users with their account ID. */
874 inttobase64(id, hi->id, IDLEN);
875 #elif WITH_PROTOCOL_P10
876 /* Stamp users with their account name. */
877 char *id = hi->handle;
879 const char *id = "???";
881 if (!nickserv_conf.disable_nicks) {
882 struct nick_info *ni;
883 for (ni = hi->nicks; ni; ni = ni->next) {
884 if (!irccasecmp(user->nick, ni->nick)) {
885 user->modes |= FLAGS_REGNICK;
893 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
894 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
896 /* We cannot clear the user's account ID, unfortunately. */
897 user->next_authed = NULL;
901 static struct handle_info*
902 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
904 struct handle_info *hi;
905 struct nick_info *ni;
906 char crypted[MD5_CRYPT_LENGTH];
908 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
909 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
913 if (!is_secure_password(handle, passwd, user))
916 cryptpass(passwd, crypted);
917 hi = register_handle(handle, crypted, 0);
918 hi->masks = alloc_string_list(1);
920 hi->language = lang_C;
921 hi->registered = now;
923 hi->flags = HI_DEFAULT_FLAGS;
924 if (settee && !no_auth)
925 set_user_handle_info(settee, hi, 1);
928 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
929 else if (nickserv_conf.disable_nicks)
930 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
931 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
932 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
934 register_nick(user->nick, hi);
935 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
937 if (settee && (user != settee))
938 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
943 nickserv_bake_cookie(struct handle_cookie *cookie)
945 cookie->hi->cookie = cookie;
946 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
950 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
952 struct handle_cookie *cookie;
953 char subject[128], body[4096], *misc;
954 const char *netname, *fmt;
958 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
962 cookie = calloc(1, sizeof(*cookie));
965 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
966 cookie->expires = now + nickserv_conf.cookie_timeout;
967 inttobase64(cookie->cookie, rand(), 5);
968 inttobase64(cookie->cookie+5, rand(), 5);
970 netname = nickserv_conf.network_name;
973 switch (cookie->type) {
975 hi->passwd[0] = 0; /* invalidate password */
976 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
977 fmt = user_find_message(user, "NSEMAIL_ACTIVATION_SUBJECT");
978 snprintf(subject, sizeof(subject), fmt, netname);
979 fmt = user_find_message(user, "NSEMAIL_ACTIVATION_BODY");
980 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
983 case PASSWORD_CHANGE:
984 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
985 fmt = user_find_message(user, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
986 snprintf(subject, sizeof(subject), fmt, netname);
987 fmt = user_find_message(user, "NSEMAIL_PASSWORD_CHANGE_BODY");
988 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
991 misc = hi->email_addr;
992 hi->email_addr = cookie->data;
994 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
995 fmt = user_find_message(user, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
996 snprintf(subject, sizeof(subject), fmt, netname);
997 fmt = user_find_message(user, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
998 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
999 sendmail(nickserv, hi, subject, body, 1);
1000 fmt = user_find_message(user, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1001 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1003 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1004 fmt = user_find_message(user, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1005 snprintf(subject, sizeof(subject), fmt, netname);
1006 fmt = user_find_message(user, "NSEMAIL_EMAIL_VERIFY_BODY");
1007 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1008 sendmail(nickserv, hi, subject, body, 1);
1011 hi->email_addr = misc;
1014 fmt = user_find_message(user, "NSEMAIL_ALLOWAUTH_SUBJECT");
1015 snprintf(subject, sizeof(subject), fmt, netname);
1016 fmt = user_find_message(user, "NSEMAIL_ALLOWAUTH_BODY");
1017 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1018 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1021 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1025 sendmail(nickserv, hi, subject, body, first_time);
1026 nickserv_bake_cookie(cookie);
1030 nickserv_eat_cookie(struct handle_cookie *cookie)
1032 cookie->hi->cookie = NULL;
1033 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1034 nickserv_free_cookie(cookie);
1038 nickserv_free_email_addr(void *data)
1040 handle_info_list_clean(data);
1045 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1047 struct handle_info_list *hil;
1048 /* Remove from old handle_info_list ... */
1049 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1050 handle_info_list_remove(hil, hi);
1051 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1052 hi->email_addr = NULL;
1054 /* Add to the new list.. */
1055 if (new_email_addr) {
1056 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1057 hil = calloc(1, sizeof(*hil));
1058 hil->tag = strdup(new_email_addr);
1059 handle_info_list_init(hil);
1060 dict_insert(nickserv_email_dict, hil->tag, hil);
1062 handle_info_list_append(hil, hi);
1063 hi->email_addr = hil->tag;
1067 static NICKSERV_FUNC(cmd_register)
1069 struct handle_info *hi;
1070 const char *email_addr, *password;
1073 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1074 /* Require the first handle registered to belong to someone +o. */
1075 reply("NSMSG_REQUIRE_OPER");
1079 if (user->handle_info) {
1080 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1084 if (IsStamped(user)) {
1085 /* Unauthenticated users might still have been stamped
1086 previously and could therefore have a hidden host;
1087 do not allow them to register a new account. */
1088 reply("NSMSG_STAMPED_REGISTER");
1092 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1094 if (!is_valid_handle(argv[1])) {
1095 reply("NSMSG_BAD_HANDLE", argv[1]);
1099 if ((argc >= 4) && nickserv_conf.email_enabled) {
1100 struct handle_info_list *hil;
1103 /* Remember email address. */
1104 email_addr = argv[3];
1106 /* Check that the email address looks valid.. */
1107 if (!is_valid_email_addr(email_addr)) {
1108 reply("NSMSG_BAD_EMAIL_ADDR");
1112 /* .. and that we are allowed to send to it. */
1113 if ((str = sendmail_prohibited_address(email_addr))) {
1114 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1118 /* If we do email verify, make sure we don't spam the address. */
1119 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1121 for (nn=0; nn<hil->used; nn++) {
1122 if (hil->list[nn]->cookie) {
1123 reply("NSMSG_EMAIL_UNACTIVATED");
1127 if (hil->used >= nickserv_conf.handles_per_email) {
1128 reply("NSMSG_EMAIL_OVERUSED");
1141 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1143 /* Add any masks they should get. */
1144 if (nickserv_conf.default_hostmask) {
1145 string_list_append(hi->masks, strdup("*@*"));
1147 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1148 if (user->ip.s_addr && user->hostname[strspn(user->hostname, "0123456789.")])
1149 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1152 /* If they're the first to register, give them level 1000. */
1153 if (dict_size(nickserv_handle_dict) == 1) {
1154 hi->opserv_level = 1000;
1155 reply("NSMSG_ROOT_HANDLE", argv[1]);
1158 /* Set their email address. */
1160 nickserv_set_email_addr(hi, email_addr);
1162 /* If they need to do email verification, tell them. */
1164 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1169 static NICKSERV_FUNC(cmd_oregister)
1172 struct userNode *settee;
1173 struct handle_info *hi;
1175 NICKSERV_MIN_PARMS(4);
1177 if (!is_valid_handle(argv[1])) {
1178 reply("NSMSG_BAD_HANDLE", argv[1]);
1182 if (strchr(argv[3], '@')) {
1183 mask = canonicalize_hostmask(strdup(argv[3]));
1185 settee = GetUserH(argv[4]);
1187 reply("MSG_NICK_UNKNOWN", argv[4]);
1194 } else if ((settee = GetUserH(argv[3]))) {
1195 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1197 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1200 if (settee && settee->handle_info) {
1201 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1205 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1209 string_list_append(hi->masks, mask);
1213 static NICKSERV_FUNC(cmd_handleinfo)
1216 unsigned int i, pos=0, herelen;
1217 struct userNode *target, *next_un;
1218 struct handle_info *hi;
1219 const char *nsmsg_none;
1222 if (!(hi = user->handle_info)) {
1223 reply("NSMSG_MUST_AUTH");
1226 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1230 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1231 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1232 #ifdef WITH_PROTOCOL_BAHAMUT
1233 reply("NSMSG_HANDLEINFO_ID", hi->id);
1235 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1238 intervalString(buff, now - hi->lastseen);
1239 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1241 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1244 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1245 if (HANDLE_FLAGGED(hi, FROZEN))
1246 reply("NSMSG_HANDLEINFO_VACATION");
1248 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1249 struct do_not_register *dnr;
1250 if ((dnr = chanserv_is_dnr(NULL, hi)))
1251 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1252 if (!oper_outranks(user, hi))
1254 } else if (hi != user->handle_info)
1257 if (nickserv_conf.email_enabled)
1258 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1262 switch (hi->cookie->type) {
1263 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1264 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1265 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1266 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1267 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1273 unsigned long flen = 1;
1274 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1276 for (i=0, flen=1; handle_flags[i]; i++)
1277 if (hi->flags & 1 << i)
1278 flags[flen++] = handle_flags[i];
1280 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1282 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1285 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1286 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1287 || (hi->opserv_level > 0)) {
1288 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1291 if (hi->last_quit_host[0])
1292 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1294 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1296 if (nickserv_conf.disable_nicks) {
1297 /* nicks disabled; don't show anything about registered nicks */
1298 } else if (hi->nicks) {
1299 struct nick_info *ni, *next_ni;
1300 for (ni = hi->nicks; ni; ni = next_ni) {
1301 herelen = strlen(ni->nick);
1302 if (pos + herelen + 1 > ArrayLength(buff)) {
1304 goto print_nicks_buff;
1308 memcpy(buff+pos, ni->nick, herelen);
1309 pos += herelen; buff[pos++] = ' ';
1313 reply("NSMSG_HANDLEINFO_NICKS", buff);
1318 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1321 if (hi->masks->used) {
1322 for (i=0; i < hi->masks->used; i++) {
1323 herelen = strlen(hi->masks->list[i]);
1324 if (pos + herelen + 1 > ArrayLength(buff)) {
1326 goto print_mask_buff;
1328 memcpy(buff+pos, hi->masks->list[i], herelen);
1329 pos += herelen; buff[pos++] = ' ';
1330 if (i+1 == hi->masks->used) {
1333 reply("NSMSG_HANDLEINFO_MASKS", buff);
1338 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1342 struct userData *channel, *next;
1345 for (channel = hi->channels; channel; channel = next) {
1346 next = channel->u_next;
1347 name = channel->channel->channel->name;
1348 herelen = strlen(name);
1349 if (pos + herelen + 7 > ArrayLength(buff)) {
1351 goto print_chans_buff;
1353 if (IsUserSuspended(channel))
1355 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1359 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1364 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1367 for (target = hi->users; target; target = next_un) {
1368 herelen = strlen(target->nick);
1369 if (pos + herelen + 1 > ArrayLength(buff)) {
1371 goto print_cnick_buff;
1373 next_un = target->next_authed;
1375 memcpy(buff+pos, target->nick, herelen);
1376 pos += herelen; buff[pos++] = ' ';
1380 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1388 static NICKSERV_FUNC(cmd_userinfo)
1390 struct userNode *target;
1392 NICKSERV_MIN_PARMS(2);
1393 if (!(target = GetUserH(argv[1]))) {
1394 reply("MSG_NICK_UNKNOWN", argv[1]);
1397 if (target->handle_info)
1398 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1400 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1404 static NICKSERV_FUNC(cmd_nickinfo)
1406 struct nick_info *ni;
1408 NICKSERV_MIN_PARMS(2);
1409 if (!(ni = get_nick_info(argv[1]))) {
1410 reply("MSG_NICK_UNKNOWN", argv[1]);
1413 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1417 static NICKSERV_FUNC(cmd_rename_handle)
1419 struct handle_info *hi;
1420 char msgbuf[MAXLEN], *old_handle;
1423 NICKSERV_MIN_PARMS(3);
1424 if (!(hi = get_victim_oper(user, argv[1])))
1426 if (!is_valid_handle(argv[2])) {
1427 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1430 if (get_handle_info(argv[2])) {
1431 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1435 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1436 hi->handle = strdup(argv[2]);
1437 dict_insert(nickserv_handle_dict, hi->handle, hi);
1438 for (nn=0; nn<rf_list_used; nn++)
1439 rf_list[nn](hi, old_handle);
1440 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1441 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1442 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1447 static failpw_func_t *failpw_func_list;
1448 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1451 reg_failpw_func(failpw_func_t func)
1453 if (failpw_func_used == failpw_func_size) {
1454 if (failpw_func_size) {
1455 failpw_func_size <<= 1;
1456 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1458 failpw_func_size = 8;
1459 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1462 failpw_func_list[failpw_func_used++] = func;
1465 static NICKSERV_FUNC(cmd_auth)
1467 int pw_arg, used, maxlogins;
1468 struct handle_info *hi;
1470 struct userNode *other;
1472 if (user->handle_info) {
1473 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1476 if (IsStamped(user)) {
1477 /* Unauthenticated users might still have been stamped
1478 previously and could therefore have a hidden host;
1479 do not allow them to authenticate. */
1480 reply("NSMSG_STAMPED_AUTH");
1484 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1486 } else if (argc == 2) {
1487 if (nickserv_conf.disable_nicks) {
1488 if (!(hi = get_handle_info(user->nick))) {
1489 reply("NSMSG_HANDLE_NOT_FOUND");
1493 /* try to look up their handle from their nick */
1494 struct nick_info *ni;
1495 ni = get_nick_info(user->nick);
1497 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1504 reply("MSG_MISSING_PARAMS", argv[0]);
1505 svccmd_send_help(user, nickserv, cmd);
1509 reply("NSMSG_HANDLE_NOT_FOUND");
1512 passwd = argv[pw_arg];
1513 if (!valid_user_for(user, hi)) {
1514 if (hi->email_addr && nickserv_conf.email_enabled)
1515 reply("NSMSG_USE_AUTHCOOKIE", hi->handle, hi->handle);
1517 reply("NSMSG_HOSTMASK_INVALID", hi->handle);
1518 argv[pw_arg] = "BADMASK";
1521 if (!checkpass(passwd, hi->passwd)) {
1523 reply("NSMSG_PASSWORD_INVALID");
1524 argv[pw_arg] = "BADPASS";
1525 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1526 if (nickserv_conf.autogag_enabled) {
1527 if (!user->auth_policer.params) {
1528 user->auth_policer.last_req = now;
1529 user->auth_policer.params = nickserv_conf.auth_policer_params;
1531 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1533 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1534 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1535 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1537 argv[pw_arg] = "GAGGED";
1542 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1543 reply("NSMSG_HANDLE_SUSPENDED");
1544 argv[pw_arg] = "SUSPENDED";
1547 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1548 for (used = 0, other = hi->users; other; other = other->next_authed) {
1549 if (++used >= maxlogins) {
1550 reply("NSMSG_MAX_LOGINS", maxlogins);
1551 argv[pw_arg] = "MAXLOGINS";
1556 if (nickserv_conf.email_required && !hi->email_addr)
1557 reply("NSMSG_PLEASE_SET_EMAIL");
1558 if (!is_secure_password(hi->handle, passwd, NULL))
1559 reply("NSMSG_WEAK_PASSWORD");
1560 if (hi->passwd[0] != '$')
1561 cryptpass(passwd, hi->passwd);
1563 reply("NSMSG_AUTH_SUCCESS");
1564 argv[pw_arg] = "****";
1565 set_user_handle_info(user, hi, 1);
1569 static allowauth_func_t *allowauth_func_list;
1570 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1573 reg_allowauth_func(allowauth_func_t func)
1575 if (allowauth_func_used == allowauth_func_size) {
1576 if (allowauth_func_size) {
1577 allowauth_func_size <<= 1;
1578 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1580 allowauth_func_size = 8;
1581 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1584 allowauth_func_list[allowauth_func_used++] = func;
1587 static NICKSERV_FUNC(cmd_allowauth)
1589 struct userNode *target;
1590 struct handle_info *hi;
1593 NICKSERV_MIN_PARMS(2);
1594 if (!(target = GetUserH(argv[1]))) {
1595 reply("MSG_NICK_UNKNOWN", argv[1]);
1598 if (target->handle_info) {
1599 reply("NSMSG_USER_PREV_AUTH", target->nick);
1602 if (IsStamped(target)) {
1603 /* Unauthenticated users might still have been stamped
1604 previously and could therefore have a hidden host;
1605 do not allow them to authenticate to an account. */
1606 send_message(target, nickserv, "NSMSG_USER_PREV_STAMP", target->nick);
1611 else if (!(hi = get_handle_info(argv[2]))) {
1612 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1616 if (hi->opserv_level > user->handle_info->opserv_level) {
1617 reply("MSG_USER_OUTRANKED", hi->handle);
1620 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1621 || (hi->opserv_level > 0))
1622 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1623 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1626 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1627 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1628 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1629 if (nickserv_conf.email_enabled)
1630 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1632 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1633 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1635 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1637 for (n=0; n<allowauth_func_used; n++)
1638 allowauth_func_list[n](user, target, hi);
1642 static NICKSERV_FUNC(cmd_authcookie)
1644 struct handle_info *hi;
1646 NICKSERV_MIN_PARMS(2);
1647 if (user->handle_info) {
1648 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1651 if (IsStamped(user)) {
1652 /* Unauthenticated users might still have been stamped
1653 previously and could therefore have a hidden host;
1654 do not allow them to authenticate to an account. */
1655 reply("NSMSG_STAMPED_AUTHCOOKIE");
1658 if (!(hi = get_handle_info(argv[1]))) {
1659 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1662 if (!hi->email_addr) {
1663 reply("MSG_SET_EMAIL_ADDR");
1666 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1670 static NICKSERV_FUNC(cmd_delcookie)
1672 struct handle_info *hi;
1674 hi = user->handle_info;
1676 reply("NSMSG_NO_COOKIE");
1679 switch (hi->cookie->type) {
1682 reply("NSMSG_MUST_TIME_OUT");
1685 nickserv_eat_cookie(hi->cookie);
1686 reply("NSMSG_ATE_COOKIE");
1692 static NICKSERV_FUNC(cmd_resetpass)
1694 struct handle_info *hi;
1695 char crypted[MD5_CRYPT_LENGTH];
1697 NICKSERV_MIN_PARMS(3);
1698 if (user->handle_info) {
1699 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1702 if (IsStamped(user)) {
1703 /* Unauthenticated users might still have been stamped
1704 previously and could therefore have a hidden host;
1705 do not allow them to activate an account. */
1706 reply("NSMSG_STAMPED_RESETPASS");
1709 if (!(hi = get_handle_info(argv[1]))) {
1710 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1713 if (!hi->email_addr) {
1714 reply("MSG_SET_EMAIL_ADDR");
1717 cryptpass(argv[2], crypted);
1719 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1723 static NICKSERV_FUNC(cmd_cookie)
1725 struct handle_info *hi;
1728 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1731 NICKSERV_MIN_PARMS(3);
1732 if (!(hi = get_handle_info(argv[1]))) {
1733 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1739 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1740 reply("NSMSG_HANDLE_SUSPENDED");
1745 reply("NSMSG_NO_COOKIE");
1749 /* Check validity of operation before comparing cookie to
1750 * prohibit guessing by authed users. */
1751 if (user->handle_info
1752 && (hi->cookie->type != EMAIL_CHANGE)
1753 && (hi->cookie->type != PASSWORD_CHANGE)) {
1754 reply("NSMSG_CANNOT_COOKIE");
1758 if (strcmp(cookie, hi->cookie->cookie)) {
1759 reply("NSMSG_BAD_COOKIE");
1763 switch (hi->cookie->type) {
1765 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1766 set_user_handle_info(user, hi, 1);
1767 reply("NSMSG_HANDLE_ACTIVATED");
1769 case PASSWORD_CHANGE:
1770 set_user_handle_info(user, hi, 1);
1771 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1772 reply("NSMSG_PASSWORD_CHANGED");
1775 nickserv_set_email_addr(hi, hi->cookie->data);
1776 reply("NSMSG_EMAIL_CHANGED");
1779 set_user_handle_info(user, hi, 1);
1780 reply("NSMSG_AUTH_SUCCESS");
1783 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1784 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1788 nickserv_eat_cookie(hi->cookie);
1793 static NICKSERV_FUNC(cmd_oregnick) {
1795 struct handle_info *target;
1796 struct nick_info *ni;
1798 NICKSERV_MIN_PARMS(3);
1799 if (!(target = modcmd_get_handle_info(user, argv[1])))
1802 if (!is_registerable_nick(nick)) {
1803 reply("NSMSG_BAD_NICK", nick);
1806 ni = dict_find(nickserv_nick_dict, nick, NULL);
1808 reply("NSMSG_NICK_EXISTS", nick);
1811 register_nick(nick, target);
1812 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1816 static NICKSERV_FUNC(cmd_regnick) {
1818 struct nick_info *ni;
1820 if (!is_registerable_nick(user->nick)) {
1821 reply("NSMSG_BAD_NICK", user->nick);
1824 /* count their nicks, see if it's too many */
1825 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1826 if (n >= nickserv_conf.nicks_per_handle) {
1827 reply("NSMSG_TOO_MANY_NICKS");
1830 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1832 reply("NSMSG_NICK_EXISTS", user->nick);
1835 register_nick(user->nick, user->handle_info);
1836 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1840 static NICKSERV_FUNC(cmd_pass)
1842 struct handle_info *hi;
1843 const char *old_pass, *new_pass;
1845 NICKSERV_MIN_PARMS(3);
1846 hi = user->handle_info;
1850 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1851 if (!checkpass(old_pass, hi->passwd)) {
1852 argv[1] = "BADPASS";
1853 reply("NSMSG_PASSWORD_INVALID");
1856 cryptpass(new_pass, hi->passwd);
1858 reply("NSMSG_PASS_SUCCESS");
1863 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1866 char *new_mask = canonicalize_hostmask(strdup(mask));
1867 for (i=0; i<hi->masks->used; i++) {
1868 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1869 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1874 string_list_append(hi->masks, new_mask);
1875 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
1879 static NICKSERV_FUNC(cmd_addmask)
1882 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1883 int res = nickserv_addmask(user, user->handle_info, mask);
1887 if (!is_gline(argv[1])) {
1888 reply("NSMSG_MASK_INVALID", argv[1]);
1891 return nickserv_addmask(user, user->handle_info, argv[1]);
1895 static NICKSERV_FUNC(cmd_oaddmask)
1897 struct handle_info *hi;
1899 NICKSERV_MIN_PARMS(3);
1900 if (!(hi = get_victim_oper(user, argv[1])))
1902 return nickserv_addmask(user, hi, argv[2]);
1906 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
1909 for (i=0; i<hi->masks->used; i++) {
1910 if (!strcmp(del_mask, hi->masks->list[i])) {
1911 char *old_mask = hi->masks->list[i];
1912 if (hi->masks->used == 1) {
1913 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
1916 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
1917 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
1922 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
1926 static NICKSERV_FUNC(cmd_delmask)
1928 NICKSERV_MIN_PARMS(2);
1929 return nickserv_delmask(user, user->handle_info, argv[1]);
1932 static NICKSERV_FUNC(cmd_odelmask)
1934 struct handle_info *hi;
1935 NICKSERV_MIN_PARMS(3);
1936 if (!(hi = get_victim_oper(user, argv[1])))
1938 return nickserv_delmask(user, hi, argv[2]);
1942 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
1943 unsigned int nn, add = 1, pos;
1944 unsigned long added, removed, flag;
1946 for (added=removed=nn=0; str[nn]; nn++) {
1948 case '+': add = 1; break;
1949 case '-': add = 0; break;
1951 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
1952 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
1955 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
1956 /* cheesy avoidance of looking up the flag name.. */
1957 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
1960 flag = 1 << (pos - 1);
1962 added |= flag, removed &= ~flag;
1964 removed |= flag, added &= ~flag;
1969 *premoved = removed;
1974 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
1976 unsigned long before, after, added, removed;
1977 struct userNode *uNode;
1979 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
1980 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
1982 hi->flags = (hi->flags | added) & ~removed;
1983 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
1985 /* Strip helping flag if they're only a support helper and not
1986 * currently in #support. */
1987 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
1988 struct channelList *schannels;
1990 schannels = chanserv_support_channels();
1991 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
1992 for (ii = 0; ii < schannels->used; ++ii)
1993 if (GetUserMode(schannels->list[ii], uNode))
1995 if (ii < schannels->used)
1999 HANDLE_CLEAR_FLAG(hi, HELPING);
2002 if (after && !before) {
2003 /* Add user to current helper list. */
2004 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2005 userList_append(&curr_helpers, uNode);
2006 } else if (!after && before) {
2007 /* Remove user from current helper list. */
2008 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2009 userList_remove(&curr_helpers, uNode);
2016 set_list(struct userNode *user, struct handle_info *hi, int override)
2020 char *set_display[] = {
2021 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2022 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2025 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2027 /* Do this so options are presented in a consistent order. */
2028 for (i = 0; i < ArrayLength(set_display); ++i)
2029 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2030 opt(user, hi, override, 0, NULL);
2033 static NICKSERV_FUNC(cmd_set)
2035 struct handle_info *hi;
2038 hi = user->handle_info;
2040 set_list(user, hi, 0);
2043 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2044 reply("NSMSG_INVALID_OPTION", argv[1]);
2047 return opt(user, hi, 0, argc-1, argv+1);
2050 static NICKSERV_FUNC(cmd_oset)
2052 struct handle_info *hi;
2055 NICKSERV_MIN_PARMS(2);
2057 if (!(hi = get_victim_oper(user, argv[1])))
2061 set_list(user, hi, 0);
2065 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2066 reply("NSMSG_INVALID_OPTION", argv[2]);
2070 return opt(user, hi, 1, argc-2, argv+2);
2073 static OPTION_FUNC(opt_info)
2077 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2079 hi->infoline = NULL;
2081 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2085 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2086 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2090 static OPTION_FUNC(opt_width)
2093 unsigned int new_width = strtoul(argv[1], NULL, 0);
2094 hi->screen_width = new_width;
2097 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2098 hi->screen_width = MIN_LINE_SIZE;
2099 else if (hi->screen_width > MAX_LINE_SIZE)
2100 hi->screen_width = MAX_LINE_SIZE;
2102 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2106 static OPTION_FUNC(opt_tablewidth)
2109 unsigned int new_width = strtoul(argv[1], NULL, 0);
2110 hi->table_width = new_width;
2113 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2114 hi->table_width = MIN_LINE_SIZE;
2115 else if (hi->screen_width > MAX_LINE_SIZE)
2116 hi->table_width = MAX_LINE_SIZE;
2118 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2122 static OPTION_FUNC(opt_color)
2125 if (enabled_string(argv[1]))
2126 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2127 else if (disabled_string(argv[1]))
2128 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2130 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2135 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2139 static OPTION_FUNC(opt_privmsg)
2142 if (enabled_string(argv[1]))
2143 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2144 else if (disabled_string(argv[1]))
2145 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2147 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2152 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2156 static OPTION_FUNC(opt_style)
2161 if (!irccasecmp(argv[1], "Zoot"))
2162 hi->userlist_style = HI_STYLE_ZOOT;
2163 else if (!irccasecmp(argv[1], "def"))
2164 hi->userlist_style = HI_STYLE_DEF;
2167 switch (hi->userlist_style) {
2176 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2180 static OPTION_FUNC(opt_announcements)
2185 if (enabled_string(argv[1]))
2186 hi->announcements = 'y';
2187 else if (disabled_string(argv[1]))
2188 hi->announcements = 'n';
2189 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2190 hi->announcements = '?';
2192 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2197 switch (hi->announcements) {
2198 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2199 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2200 case '?': choice = "default"; break;
2201 default: choice = "unknown"; break;
2203 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2207 static OPTION_FUNC(opt_password)
2210 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2215 cryptpass(argv[1], hi->passwd);
2217 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2221 static OPTION_FUNC(opt_flags)
2224 unsigned int ii, flen;
2227 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2232 nickserv_apply_flags(user, hi, argv[1]);
2234 for (ii = flen = 0; handle_flags[ii]; ii++)
2235 if (hi->flags & (1 << ii))
2236 flags[flen++] = handle_flags[ii];
2239 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2241 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2245 static OPTION_FUNC(opt_email)
2249 if (!is_valid_email_addr(argv[1])) {
2250 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2253 if ((str = sendmail_prohibited_address(argv[1]))) {
2254 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2257 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2258 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2260 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2262 nickserv_set_email_addr(hi, argv[1]);
2264 nickserv_eat_cookie(hi->cookie);
2265 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2268 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2272 static OPTION_FUNC(opt_maxlogins)
2276 maxlogins = strtoul(argv[1], NULL, 0);
2277 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2278 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2281 hi->maxlogins = maxlogins;
2283 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2284 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2288 static OPTION_FUNC(opt_language)
2290 struct language *lang;
2292 lang = language_find(argv[1]);
2293 if (irccasecmp(lang->name, argv[1]))
2294 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2295 hi->language = lang;
2297 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2302 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2303 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2305 if ((user->handle_info->opserv_level < target->opserv_level)
2306 || ((user->handle_info->opserv_level == target->opserv_level)
2307 && (user->handle_info->opserv_level < 1000))) {
2308 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2311 if ((user->handle_info->opserv_level < new_level)
2312 || ((user->handle_info->opserv_level == new_level)
2313 && (user->handle_info->opserv_level < 1000))) {
2314 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2317 if (user->handle_info == target) {
2318 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2321 if (target->opserv_level == new_level)
2323 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2324 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2325 target->opserv_level = new_level;
2329 static OPTION_FUNC(opt_level)
2334 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2338 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2339 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2343 static OPTION_FUNC(opt_epithet)
2346 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2350 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2351 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2354 if ((epithet[0] == '*') && !epithet[1])
2357 hi->epithet = strdup(epithet);
2361 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2363 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2367 static NICKSERV_FUNC(cmd_reclaim)
2369 struct handle_info *hi;
2370 struct nick_info *ni;
2371 struct userNode *victim;
2373 NICKSERV_MIN_PARMS(2);
2374 hi = user->handle_info;
2375 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2377 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2380 if (ni->owner != user->handle_info) {
2381 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2384 victim = GetUserH(ni->nick);
2386 reply("MSG_NICK_UNKNOWN", ni->nick);
2389 if (victim == user) {
2390 reply("NSMSG_NICK_USER_YOU");
2393 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2394 switch (nickserv_conf.reclaim_action) {
2395 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2396 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2397 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2398 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2403 static NICKSERV_FUNC(cmd_unregnick)
2406 struct handle_info *hi;
2407 struct nick_info *ni;
2409 hi = user->handle_info;
2410 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2411 ni = dict_find(nickserv_nick_dict, nick, NULL);
2413 reply("NSMSG_UNKNOWN_NICK", nick);
2416 if (hi != ni->owner) {
2417 reply("NSMSG_NOT_YOUR_NICK", nick);
2420 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2425 static NICKSERV_FUNC(cmd_ounregnick)
2427 struct nick_info *ni;
2429 NICKSERV_MIN_PARMS(2);
2430 if (!(ni = get_nick_info(argv[1]))) {
2431 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2434 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2435 reply("MSG_USER_OUTRANKED", ni->nick);
2438 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2443 static NICKSERV_FUNC(cmd_unregister)
2445 struct handle_info *hi;
2448 NICKSERV_MIN_PARMS(2);
2449 hi = user->handle_info;
2452 if (checkpass(passwd, hi->passwd)) {
2453 nickserv_unregister_handle(hi, user);
2456 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2457 reply("NSMSG_PASSWORD_INVALID");
2462 static NICKSERV_FUNC(cmd_ounregister)
2464 struct handle_info *hi;
2466 NICKSERV_MIN_PARMS(2);
2467 if (!(hi = get_victim_oper(user, argv[1])))
2469 nickserv_unregister_handle(hi, user);
2473 static NICKSERV_FUNC(cmd_status)
2475 if (nickserv_conf.disable_nicks) {
2476 reply("NSMSG_GLOBAL_STATS_NONICK",
2477 dict_size(nickserv_handle_dict));
2479 if (user->handle_info) {
2481 struct nick_info *ni;
2482 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2483 reply("NSMSG_HANDLE_STATS", cnt);
2485 reply("NSMSG_HANDLE_NONE");
2487 reply("NSMSG_GLOBAL_STATS",
2488 dict_size(nickserv_handle_dict),
2489 dict_size(nickserv_nick_dict));
2494 static NICKSERV_FUNC(cmd_ghost)
2496 struct userNode *target;
2497 char reason[MAXLEN];
2499 NICKSERV_MIN_PARMS(2);
2500 if (!(target = GetUserH(argv[1]))) {
2501 reply("MSG_NICK_UNKNOWN", argv[1]);
2504 if (target == user) {
2505 reply("NSMSG_CANNOT_GHOST_SELF");
2508 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2509 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2512 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2513 DelUser(target, nickserv, 1, reason);
2514 reply("NSMSG_GHOST_KILLED", argv[1]);
2518 static NICKSERV_FUNC(cmd_vacation)
2520 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2521 reply("NSMSG_ON_VACATION");
2526 nickserv_saxdb_write(struct saxdb_context *ctx) {
2528 struct handle_info *hi;
2531 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2533 #ifdef WITH_PROTOCOL_BAHAMUT
2536 saxdb_start_record(ctx, iter_key(it), 0);
2537 if (hi->announcements != '?') {
2538 flags[0] = hi->announcements;
2540 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2543 struct handle_cookie *cookie = hi->cookie;
2546 switch (cookie->type) {
2547 case ACTIVATION: type = KEY_ACTIVATION; break;
2548 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2549 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2550 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2551 default: type = NULL; break;
2554 saxdb_start_record(ctx, KEY_COOKIE, 0);
2555 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2556 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2558 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2559 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2560 saxdb_end_record(ctx);
2564 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2566 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2570 for (ii=flen=0; handle_flags[ii]; ++ii)
2571 if (hi->flags & (1 << ii))
2572 flags[flen++] = handle_flags[ii];
2574 saxdb_write_string(ctx, KEY_FLAGS, flags);
2576 #ifdef WITH_PROTOCOL_BAHAMUT
2577 saxdb_write_int(ctx, KEY_ID, hi->id);
2580 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2581 if (hi->last_quit_host[0])
2582 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2583 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2584 if (hi->masks->used)
2585 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2587 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2589 struct string_list *slist;
2590 struct nick_info *ni;
2592 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2593 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2594 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2598 if (hi->opserv_level)
2599 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2600 if (hi->language != lang_C)
2601 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2602 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2603 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2604 if (hi->screen_width)
2605 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2606 if (hi->table_width)
2607 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2608 flags[0] = hi->userlist_style;
2610 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2611 saxdb_end_record(ctx);
2616 static handle_merge_func_t *handle_merge_func_list;
2617 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2620 reg_handle_merge_func(handle_merge_func_t func)
2622 if (handle_merge_func_used == handle_merge_func_size) {
2623 if (handle_merge_func_size) {
2624 handle_merge_func_size <<= 1;
2625 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2627 handle_merge_func_size = 8;
2628 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2631 handle_merge_func_list[handle_merge_func_used++] = func;
2634 static NICKSERV_FUNC(cmd_merge)
2636 struct handle_info *hi_from, *hi_to;
2637 struct userNode *last_user;
2638 struct userData *cList, *cListNext;
2639 unsigned int ii, jj, n;
2640 char buffer[MAXLEN];
2642 NICKSERV_MIN_PARMS(3);
2644 if (!(hi_from = get_victim_oper(user, argv[1])))
2646 if (!(hi_to = get_victim_oper(user, argv[2])))
2648 if (hi_to == hi_from) {
2649 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2653 for (n=0; n<handle_merge_func_used; n++)
2654 handle_merge_func_list[n](user, hi_to, hi_from);
2656 /* Append "from" handle's nicks to "to" handle's nick list. */
2658 struct nick_info *last_ni;
2659 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2660 last_ni->next = hi_from->nicks;
2662 while (hi_from->nicks) {
2663 hi_from->nicks->owner = hi_to;
2664 hi_from->nicks = hi_from->nicks->next;
2667 /* Merge the hostmasks. */
2668 for (ii=0; ii<hi_from->masks->used; ii++) {
2669 char *mask = hi_from->masks->list[ii];
2670 for (jj=0; jj<hi_to->masks->used; jj++)
2671 if (match_ircglobs(hi_to->masks->list[jj], mask))
2673 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2674 string_list_append(hi_to->masks, strdup(mask));
2677 /* Merge the lists of authed users. */
2679 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2680 last_user->next_authed = hi_from->users;
2682 hi_to->users = hi_from->users;
2684 /* Repoint the old "from" handle's users. */
2685 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2686 last_user->handle_info = hi_to;
2688 hi_from->users = NULL;
2690 /* Merge channel userlists. */
2691 for (cList=hi_from->channels; cList; cList=cListNext) {
2692 struct userData *cList2;
2693 cListNext = cList->u_next;
2694 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2695 if (cList->channel == cList2->channel)
2697 log_module(NS_LOG, LOG_DEBUG, "Merging %s->%s@%s: before %p->%p->%-p, %p->%p->%p",
2698 hi_from->handle, hi_to->handle, cList->channel->channel->name,
2699 cList->u_prev, cList, cList->u_next,
2700 (cList2?cList2->u_prev:0), cList2, (cList2?cList2->u_next:0));
2701 if (cList2 && (cList2->access >= cList->access)) {
2702 /* keep cList2 in hi_to; remove cList from hi_from */
2703 log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList);
2704 del_channel_user(cList, 1);
2707 /* remove the lower-ranking cList2 from hi_to */
2708 log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList2);
2709 del_channel_user(cList2, 1);
2711 /* cList needs to be moved from hi_from to hi_to */
2712 cList->handle = hi_to;
2713 /* Remove from linked list for hi_from */
2714 assert(!cList->u_prev);
2715 hi_from->channels = cList->u_next;
2717 cList->u_next->u_prev = cList->u_prev;
2718 /* Add to linked list for hi_to */
2719 cList->u_prev = NULL;
2720 cList->u_next = hi_to->channels;
2721 if (hi_to->channels)
2722 hi_to->channels->u_prev = cList;
2723 hi_to->channels = cList;
2724 log_module(NS_LOG, LOG_DEBUG, "Now %p->%p->%p",
2725 cList->u_prev, cList, cList->u_next);
2729 /* Do they get an OpServ level promotion? */
2730 if (hi_from->opserv_level > hi_to->opserv_level)
2731 hi_to->opserv_level = hi_from->opserv_level;
2733 /* What about last seen time? */
2734 if (hi_from->lastseen > hi_to->lastseen)
2735 hi_to->lastseen = hi_from->lastseen;
2737 /* Notify of success. */
2738 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2739 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2740 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2742 /* Unregister the "from" handle. */
2743 nickserv_unregister_handle(hi_from, NULL);
2748 struct nickserv_discrim {
2749 unsigned int limit, min_level, max_level;
2750 unsigned long flags_on, flags_off;
2751 time_t min_registered, max_registered;
2753 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2754 const char *nickmask;
2755 const char *hostmask;
2756 const char *handlemask;
2757 const char *emailmask;
2760 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2762 struct discrim_apply_info {
2763 struct nickserv_discrim *discrim;
2764 discrim_search_func func;
2765 struct userNode *source;
2766 unsigned int matched;
2769 static struct nickserv_discrim *
2770 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2773 struct nickserv_discrim *discrim;
2775 discrim = malloc(sizeof(*discrim));
2776 memset(discrim, 0, sizeof(*discrim));
2777 discrim->min_level = 0;
2778 discrim->max_level = ~0;
2779 discrim->limit = 50;
2780 discrim->min_registered = 0;
2781 discrim->max_registered = INT_MAX;
2782 discrim->lastseen = now;
2784 for (i=0; i<argc; i++) {
2785 if (i == argc - 1) {
2786 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2789 if (!irccasecmp(argv[i], "limit")) {
2790 discrim->limit = atoi(argv[++i]);
2791 } else if (!irccasecmp(argv[i], "flags")) {
2792 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2793 } else if (!irccasecmp(argv[i], "registered")) {
2794 const char *cmp = argv[++i];
2795 if (cmp[0] == '<') {
2796 if (cmp[1] == '=') {
2797 discrim->min_registered = now - ParseInterval(cmp+2);
2799 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2801 } else if (cmp[0] == '=') {
2802 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2803 } else if (cmp[0] == '>') {
2804 if (cmp[1] == '=') {
2805 discrim->max_registered = now - ParseInterval(cmp+2);
2807 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2810 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2812 } else if (!irccasecmp(argv[i], "seen")) {
2813 discrim->lastseen = now - ParseInterval(argv[++i]);
2814 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2815 discrim->nickmask = argv[++i];
2816 } else if (!irccasecmp(argv[i], "hostmask")) {
2818 if (!irccasecmp(argv[i], "exact")) {
2819 if (i == argc - 1) {
2820 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2823 discrim->hostmask_type = EXACT;
2824 } else if (!irccasecmp(argv[i], "subset")) {
2825 if (i == argc - 1) {
2826 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2829 discrim->hostmask_type = SUBSET;
2830 } else if (!irccasecmp(argv[i], "superset")) {
2831 if (i == argc - 1) {
2832 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2835 discrim->hostmask_type = SUPERSET;
2836 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2837 if (i == argc - 1) {
2838 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2841 discrim->hostmask_type = LASTQUIT;
2844 discrim->hostmask_type = SUPERSET;
2846 discrim->hostmask = argv[++i];
2847 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2848 if (!irccasecmp(argv[++i], "*")) {
2849 discrim->handlemask = 0;
2851 discrim->handlemask = argv[i];
2853 } else if (!irccasecmp(argv[i], "email")) {
2854 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2855 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2857 } else if (!irccasecmp(argv[++i], "*")) {
2858 discrim->emailmask = 0;
2860 discrim->emailmask = argv[i];
2862 } else if (!irccasecmp(argv[i], "access")) {
2863 const char *cmp = argv[++i];
2864 if (cmp[0] == '<') {
2865 if (discrim->min_level == 0) discrim->min_level = 1;
2866 if (cmp[1] == '=') {
2867 discrim->max_level = strtoul(cmp+2, NULL, 0);
2869 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
2871 } else if (cmp[0] == '=') {
2872 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
2873 } else if (cmp[0] == '>') {
2874 if (cmp[1] == '=') {
2875 discrim->min_level = strtoul(cmp+2, NULL, 0);
2877 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
2880 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2883 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
2894 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
2896 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
2897 || (discrim->flags_off & hi->flags)
2898 || (discrim->min_registered > hi->registered)
2899 || (discrim->max_registered < hi->registered)
2900 || (discrim->lastseen < (hi->users?now:hi->lastseen))
2901 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
2902 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
2903 || (discrim->min_level > hi->opserv_level)
2904 || (discrim->max_level < hi->opserv_level)) {
2907 if (discrim->hostmask) {
2909 for (i=0; i<hi->masks->used; i++) {
2910 const char *mask = hi->masks->list[i];
2911 if ((discrim->hostmask_type == SUBSET)
2912 && (match_ircglobs(discrim->hostmask, mask))) break;
2913 else if ((discrim->hostmask_type == EXACT)
2914 && !irccasecmp(discrim->hostmask, mask)) break;
2915 else if ((discrim->hostmask_type == SUPERSET)
2916 && (match_ircglobs(mask, discrim->hostmask))) break;
2917 else if ((discrim->hostmask_type == LASTQUIT)
2918 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
2920 if (i==hi->masks->used) return 0;
2922 if (discrim->nickmask) {
2923 struct nick_info *nick = hi->nicks;
2925 if (match_ircglob(nick->nick, discrim->nickmask)) break;
2928 if (!nick) return 0;
2934 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
2936 dict_iterator_t it, next;
2937 unsigned int matched;
2939 for (it = dict_first(nickserv_handle_dict), matched = 0;
2940 it && (matched < discrim->limit);
2942 next = iter_next(it);
2943 if (nickserv_discrim_match(discrim, iter_data(it))) {
2944 dsf(source, iter_data(it));
2952 search_print_func(struct userNode *source, struct handle_info *match)
2954 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
2958 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
2963 search_unregister_func (struct userNode *source, struct handle_info *match)
2965 if (oper_has_access(source, nickserv, match->opserv_level, 0))
2966 nickserv_unregister_handle(match, source);
2970 nickserv_sort_accounts_by_access(const void *a, const void *b)
2972 const struct handle_info *hi_a = *(const struct handle_info**)a;
2973 const struct handle_info *hi_b = *(const struct handle_info**)b;
2974 if (hi_a->opserv_level != hi_b->opserv_level)
2975 return hi_b->opserv_level - hi_a->opserv_level;
2976 return irccasecmp(hi_a->handle, hi_b->handle);
2980 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
2982 struct handle_info_list hil;
2983 struct helpfile_table tbl;
2988 memset(&hil, 0, sizeof(hil));
2989 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2990 struct handle_info *hi = iter_data(it);
2991 if (hi->opserv_level)
2992 handle_info_list_append(&hil, hi);
2994 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
2995 tbl.length = hil.used + 1;
2997 tbl.flags = TABLE_NO_FREE;
2998 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
2999 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3002 for (ii = 0; ii < hil.used; ) {
3003 ary = malloc(tbl.width * sizeof(ary[0]));
3004 ary[0] = hil.list[ii]->handle;
3005 ary[1] = strtab(hil.list[ii]->opserv_level);
3006 tbl.contents[++ii] = ary;
3008 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3009 reply("MSG_MATCH_COUNT", hil.used);
3010 for (ii = 0; ii < hil.used; )
3011 free(tbl.contents[++ii]);
3016 static NICKSERV_FUNC(cmd_search)
3018 struct nickserv_discrim *discrim;
3019 discrim_search_func action;
3020 struct svccmd *subcmd;
3021 unsigned int matches;
3024 NICKSERV_MIN_PARMS(3);
3025 sprintf(buf, "search %s", argv[1]);
3026 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3027 if (!irccasecmp(argv[1], "print"))
3028 action = search_print_func;
3029 else if (!irccasecmp(argv[1], "count"))
3030 action = search_count_func;
3031 else if (!irccasecmp(argv[1], "unregister"))
3032 action = search_unregister_func;
3034 reply("NSMSG_INVALID_ACTION", argv[1]);
3038 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3041 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3045 if (action == search_print_func)
3046 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3047 else if (action == search_count_func)
3048 discrim->limit = INT_MAX;
3050 matches = nickserv_discrim_search(discrim, action, user);
3053 reply("MSG_MATCH_COUNT", matches);
3055 reply("MSG_NO_MATCHES");
3061 static MODCMD_FUNC(cmd_checkpass)
3063 struct handle_info *hi;
3065 NICKSERV_MIN_PARMS(3);
3066 if (!(hi = get_handle_info(argv[1]))) {
3067 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3070 if (checkpass(argv[2], hi->passwd))
3071 reply("CHECKPASS_YES");
3073 reply("CHECKPASS_NO");
3079 nickserv_db_read_handle(const char *handle, dict_t obj)
3082 struct string_list *masks, *slist;
3083 struct handle_info *hi;
3084 struct userNode *authed_users;
3085 unsigned long int id;
3089 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3090 id = str ? strtoul(str, NULL, 0) : 0;
3091 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3093 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3096 if ((hi = get_handle_info(handle))) {
3097 authed_users = hi->users;
3099 dict_remove(nickserv_handle_dict, hi->handle);
3101 authed_users = NULL;
3103 hi = register_handle(handle, str, id);
3105 hi->users = authed_users;
3106 while (authed_users) {
3107 authed_users->handle_info = hi;
3108 authed_users = authed_users->next_authed;
3111 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3112 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3113 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3114 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3115 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3116 hi->language = language_find(str ? str : "C");
3117 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3118 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3119 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3121 hi->infoline = strdup(str);
3122 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3123 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3124 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3125 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3126 /* We want to read the nicks even if disable_nicks is set. This is so
3127 * that we don't lose the nick data entirely. */
3128 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3130 for (ii=0; ii<slist->used; ii++)
3131 register_nick(slist->list[ii], hi);
3133 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3135 for (ii=0; str[ii]; ii++)
3136 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3138 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3139 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3140 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3141 hi->announcements = str ? str[0] : '?';
3142 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3143 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3144 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3145 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3146 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3148 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3150 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3151 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3153 nickserv_set_email_addr(hi, str);
3154 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3156 hi->epithet = strdup(str);
3157 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3159 const char *data, *type, *expires, *cookie_str;
3160 struct handle_cookie *cookie;
3162 cookie = calloc(1, sizeof(*cookie));
3163 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3164 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3165 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3166 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3167 if (!type || !expires || !cookie_str) {
3168 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3171 if (!irccasecmp(type, KEY_ACTIVATION))
3172 cookie->type = ACTIVATION;
3173 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3174 cookie->type = PASSWORD_CHANGE;
3175 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3176 cookie->type = EMAIL_CHANGE;
3177 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3178 cookie->type = ALLOWAUTH;
3180 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3183 cookie->expires = strtoul(expires, NULL, 0);
3184 if (cookie->expires < now)
3187 cookie->data = strdup(data);
3188 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3192 nickserv_bake_cookie(cookie);
3194 nickserv_free_cookie(cookie);
3199 nickserv_saxdb_read(dict_t db) {
3201 struct record_data *rd;
3203 for (it=dict_first(db); it; it=iter_next(it)) {
3205 nickserv_db_read_handle(iter_key(it), rd->d.object);
3210 static NICKSERV_FUNC(cmd_mergedb)
3212 struct timeval start, stop;
3215 NICKSERV_MIN_PARMS(2);
3216 gettimeofday(&start, NULL);
3217 if (!(db = parse_database(argv[1]))) {
3218 reply("NSMSG_DB_UNREADABLE", argv[1]);
3221 nickserv_saxdb_read(db);
3223 gettimeofday(&stop, NULL);
3224 stop.tv_sec -= start.tv_sec;
3225 stop.tv_usec -= start.tv_usec;
3226 if (stop.tv_usec < 0) {
3228 stop.tv_usec += 1000000;
3230 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3235 expire_handles(UNUSED_ARG(void *data))
3237 dict_iterator_t it, next;
3239 struct handle_info *hi;
3241 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3242 next = iter_next(it);
3244 if ((hi->opserv_level > 0)
3246 || HANDLE_FLAGGED(hi, FROZEN)
3247 || HANDLE_FLAGGED(hi, NODELETE)) {
3250 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3251 if ((now - hi->lastseen) > expiry) {
3252 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3253 nickserv_unregister_handle(hi, NULL);
3257 if (nickserv_conf.handle_expire_frequency)
3258 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3262 nickserv_load_dict(const char *fname)
3266 if (!(file = fopen(fname, "r"))) {
3267 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3270 while (!feof(file)) {
3271 fgets(line, sizeof(line), file);
3274 if (line[strlen(line)-1] == '\n')
3275 line[strlen(line)-1] = 0;
3276 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3279 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3282 static enum reclaim_action
3283 reclaim_action_from_string(const char *str) {
3285 return RECLAIM_NONE;
3286 else if (!irccasecmp(str, "warn"))
3287 return RECLAIM_WARN;
3288 else if (!irccasecmp(str, "svsnick"))
3289 return RECLAIM_SVSNICK;
3290 else if (!irccasecmp(str, "kill"))
3291 return RECLAIM_KILL;
3293 return RECLAIM_NONE;
3297 nickserv_conf_read(void)
3299 dict_t conf_node, child;
3303 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3304 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3307 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3309 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3310 if (nickserv_conf.valid_handle_regex_set) regfree(&nickserv_conf.valid_handle_regex);
3312 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3313 nickserv_conf.valid_handle_regex_set = !err;
3314 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3316 nickserv_conf.valid_handle_regex_set = 0;
3318 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3319 if (nickserv_conf.valid_nick_regex_set) regfree(&nickserv_conf.valid_nick_regex);
3321 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3322 nickserv_conf.valid_nick_regex_set = !err;
3323 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3325 nickserv_conf.valid_nick_regex_set = 0;
3327 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3329 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3330 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3331 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3332 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3333 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3334 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3335 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3336 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3337 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3338 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3339 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3340 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3341 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3342 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3343 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3344 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3345 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3346 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3347 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3348 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3349 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3351 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3352 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3353 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3355 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3356 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3357 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3359 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3360 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3361 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3362 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3363 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3364 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3365 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3366 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3367 if (!nickserv_conf.disable_nicks) {
3368 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3369 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3370 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3371 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3372 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3373 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3374 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3375 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3377 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3378 for (it=dict_first(child); it; it=iter_next(it)) {
3379 const char *key = iter_key(it), *value;
3383 if (!strncasecmp(key, "uc_", 3))
3384 flag = toupper(key[3]);
3385 else if (!strncasecmp(key, "lc_", 3))
3386 flag = tolower(key[3]);
3390 if ((pos = handle_inverse_flags[flag])) {
3391 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3392 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3395 if (nickserv_conf.weak_password_dict)
3396 dict_delete(nickserv_conf.weak_password_dict);
3397 nickserv_conf.weak_password_dict = dict_new();
3398 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3399 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3400 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3401 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3403 nickserv_load_dict(str);
3404 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3405 if (nickserv && str)
3406 NickChange(nickserv, str, 0);
3407 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3408 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3409 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3410 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3411 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3412 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3413 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3414 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3415 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3416 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3417 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3418 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3419 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3420 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3421 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3422 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3423 str = conf_get_data("server/network", RECDB_QSTRING);
3424 nickserv_conf.network_name = str ? str : "some IRC network";
3425 if (!nickserv_conf.auth_policer_params) {
3426 nickserv_conf.auth_policer_params = policer_params_new();
3427 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3428 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3430 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3431 for (it=dict_first(child); it; it=iter_next(it))
3432 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3436 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3437 char newnick[NICKLEN+1];
3446 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3448 case RECLAIM_SVSNICK:
3450 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3451 } while (GetUserH(newnick));
3452 irc_svsnick(nickserv, user, newnick);
3455 irc_kill(nickserv, user, "NSMSG_RECLAIM_KILL");
3461 nickserv_reclaim_p(void *data) {
3462 struct userNode *user = data;
3463 struct nick_info *ni = get_nick_info(user->nick);
3465 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3469 check_user_nick(struct userNode *user) {
3470 struct nick_info *ni;
3471 user->modes &= ~FLAGS_REGNICK;
3472 if (!(ni = get_nick_info(user->nick)))
3474 if (user->handle_info == ni->owner) {
3475 user->modes |= FLAGS_REGNICK;
3479 if (nickserv_conf.warn_nick_owned)
3480 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3481 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3483 if (nickserv_conf.auto_reclaim_delay)
3484 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3486 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3491 handle_new_user(struct userNode *user)
3493 return check_user_nick(user);
3497 handle_account(struct userNode *user, const char *stamp)
3499 struct handle_info *hi;
3501 #ifdef WITH_PROTOCOL_P10
3502 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3504 hi = dict_find(nickserv_id_dict, stamp, NULL);
3508 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3511 set_user_handle_info(user, hi, 0);
3513 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3518 handle_nick_change(struct userNode *user, const char *old_nick)
3520 struct handle_info *hi;
3522 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3523 dict_remove(nickserv_allow_auth_dict, old_nick);
3524 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3526 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3527 check_user_nick(user);
3531 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3533 dict_remove(nickserv_allow_auth_dict, user->nick);
3534 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3535 set_user_handle_info(user, NULL, 0);
3538 static struct modcmd *
3539 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3541 if (min_level > 0) {
3543 sprintf(buf, "%u", min_level);
3544 if (must_be_qualified) {
3545 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3547 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3549 } else if (min_level == 0) {
3550 if (must_be_qualified) {
3551 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3553 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3556 if (must_be_qualified) {
3557 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3559 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3565 nickserv_db_cleanup(void)
3567 unreg_del_user_func(nickserv_remove_user);
3568 userList_clean(&curr_helpers);
3569 policer_params_delete(nickserv_conf.auth_policer_params);
3570 dict_delete(nickserv_handle_dict);
3571 dict_delete(nickserv_nick_dict);
3572 dict_delete(nickserv_opt_dict);
3573 dict_delete(nickserv_allow_auth_dict);
3574 dict_delete(nickserv_email_dict);
3575 dict_delete(nickserv_id_dict);
3576 dict_delete(nickserv_conf.weak_password_dict);
3577 free(auth_func_list);
3578 free(unreg_func_list);
3580 free(allowauth_func_list);
3581 free(handle_merge_func_list);
3582 free(failpw_func_list);
3583 if (nickserv_conf.valid_handle_regex_set)
3584 regfree(&nickserv_conf.valid_handle_regex);
3585 if (nickserv_conf.valid_nick_regex_set)
3586 regfree(&nickserv_conf.valid_nick_regex);
3590 init_nickserv(const char *nick)
3593 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3594 reg_new_user_func(handle_new_user);
3595 reg_nick_change_func(handle_nick_change);
3596 reg_del_user_func(nickserv_remove_user);
3597 reg_account_func(handle_account);
3599 /* set up handle_inverse_flags */
3600 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3601 for (i=0; handle_flags[i]; i++) {
3602 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3603 flag_access_levels[i] = 0;
3606 conf_register_reload(nickserv_conf_read);
3607 nickserv_opt_dict = dict_new();
3608 nickserv_email_dict = dict_new();
3609 dict_set_free_keys(nickserv_email_dict, free);
3610 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3612 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3613 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3614 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3615 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3616 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3617 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3618 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3619 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3620 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3621 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3622 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3623 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3624 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3625 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3626 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3627 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3628 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3629 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3630 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3631 if (!nickserv_conf.disable_nicks) {
3632 /* nick management commands */
3633 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3634 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3635 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3636 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3637 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3638 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3640 if (nickserv_conf.email_enabled) {
3641 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3642 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3643 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3644 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3645 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3647 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3648 /* miscellaneous commands */
3649 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3650 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3651 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3652 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3653 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3655 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3656 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3657 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3658 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3659 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3660 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3661 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3662 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3663 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3664 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3665 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3666 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3667 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3668 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3669 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3671 nickserv_handle_dict = dict_new();
3672 dict_set_free_keys(nickserv_handle_dict, free);
3673 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3675 nickserv_id_dict = dict_new();
3676 dict_set_free_keys(nickserv_id_dict, free);
3678 nickserv_nick_dict = dict_new();
3679 dict_set_free_data(nickserv_nick_dict, free_nick_info);
3681 nickserv_allow_auth_dict = dict_new();
3683 userList_init(&curr_helpers);
3686 nickserv = AddService(nick, "Nick Services");
3687 nickserv_service = service_register(nickserv, 0);
3689 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3690 reg_exit_func(nickserv_db_cleanup);
3691 if(nickserv_conf.handle_expire_frequency)
3692 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3693 message_register_table(msgtab);