1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for gag_create(), opserv_bad_channel() */
34 #define NICKSERV_CONF_NAME "services/nickserv"
36 #define KEY_DISABLE_NICKS "disable_nicks"
37 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
38 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
39 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
40 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
41 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
42 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
43 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
44 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
45 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
46 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
47 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
48 #define KEY_MODOPER_LEVEL "modoper_level"
49 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
50 #define KEY_FLAG_LEVELS "flag_levels"
51 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
52 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
53 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
54 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
55 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
56 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
57 #define KEY_DICT_FILE "dict_file"
58 #define KEY_NICK "nick"
59 #define KEY_LANGUAGE "language"
60 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
61 #define KEY_AUTOGAG_DURATION "autogag_duration"
62 #define KEY_AUTH_POLICER "auth_policer"
63 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
64 #define KEY_EMAIL_ENABLED "email_enabled"
65 #define KEY_EMAIL_REQUIRED "email_required"
66 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
67 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
68 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
71 #define KEY_PASSWD "passwd"
72 #define KEY_NICKS "nicks"
73 #define KEY_MASKS "masks"
74 #define KEY_OPSERV_LEVEL "opserv_level"
75 #define KEY_FLAGS "flags"
76 #define KEY_REGISTER_ON "register"
77 #define KEY_LAST_SEEN "lastseen"
78 #define KEY_INFO "info"
79 #define KEY_USERLIST_STYLE "user_style"
80 #define KEY_SCREEN_WIDTH "screen_width"
81 #define KEY_LAST_AUTHED_HOST "last_authed_host"
82 #define KEY_LAST_QUIT_HOST "last_quit_host"
83 #define KEY_EMAIL_ADDR "email_addr"
84 #define KEY_COOKIE "cookie"
85 #define KEY_COOKIE_DATA "data"
86 #define KEY_COOKIE_TYPE "type"
87 #define KEY_COOKIE_EXPIRES "expires"
88 #define KEY_ACTIVATION "activation"
89 #define KEY_PASSWORD_CHANGE "password change"
90 #define KEY_EMAIL_CHANGE "email change"
91 #define KEY_ALLOWAUTH "allowauth"
92 #define KEY_EPITHET "epithet"
93 #define KEY_TABLE_WIDTH "table_width"
94 #define KEY_ANNOUNCEMENTS "announcements"
95 #define KEY_MAXLOGINS "maxlogins"
97 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
99 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
100 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
101 typedef OPTION_FUNC(option_func_t);
103 DEFINE_LIST(handle_info_list, struct handle_info*);
105 #define NICKSERV_MIN_PARMS(N) do { \
107 reply("MSG_MISSING_PARAMS", argv[0]); \
108 svccmd_send_help(user, nickserv, cmd); \
112 struct userNode *nickserv;
113 struct userList curr_helpers;
114 const char *handle_flags = HANDLE_FLAGS;
116 static struct module *nickserv_module;
117 static struct service *nickserv_service;
118 static struct log_type *NS_LOG;
119 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
120 static dict_t nickserv_id_dict; /* contains struct handle_info* */
121 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
122 static dict_t nickserv_opt_dict; /* contains option_func_t* */
123 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
124 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
125 static char handle_inverse_flags[256];
126 static unsigned int flag_access_levels[32];
127 static const struct message_entry msgtab[] = {
128 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
129 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
130 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
131 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
132 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
133 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
134 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
135 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
136 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
137 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
138 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
139 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
140 { "NSMSG_USE_COOKIE_REGISTER", "To activate your account, you must check your email for the \"cookie\" that has been mailed to it. When you have it, use the $bcookie$b command to complete registration." },
141 { "NSMSG_USE_COOKIE_RESETPASS", "A cookie has been mailed to your account's email address. You must check your email and use the $bcookie$b command to confirm the password change." },
142 { "NSMSG_USE_COOKIE_EMAIL_1", "A cookie has been mailed to the new address you requested. To finish setting your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
143 { "NSMSG_USE_COOKIE_EMAIL_2", "A cookie has been generated, and half mailed to each your old and new addresses. To finish changing your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
144 { "NSMSG_USE_COOKIE_AUTH", "A cookie has been generated and sent to your email address. Once you have checked your email and received the cookie, auth using the $bcookie$b command." },
145 { "NSMSG_COOKIE_LIVE", "Account $b%s$b already has a cookie active. Please either finish using that cookie, wait for it to expire, or auth to the account and use the $bdelcookie$b command." },
146 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
147 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
148 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
149 { "NSMSG_BAD_COOKIE", "That cookie is not the right one. Please make sure you are copying it EXACTLY from the email; it is case-sensitive, so $bABC$b is different from $babc$b." },
150 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
151 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
152 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
153 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
154 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
155 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
156 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
157 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
158 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
159 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
160 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
161 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
162 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
163 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
164 { "NSMSG_USE_AUTHCOOKIE", "Your hostmask is not valid for account $b%1$s$b. Please use the $bauthcookie$b command to grant yourself access. (/msg $S authcookie %1$s)" },
165 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
166 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
167 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
168 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
169 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
170 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
171 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
172 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
173 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
174 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
175 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
176 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
177 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
178 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
179 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
180 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
181 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
182 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
183 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
184 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
185 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
186 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
187 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
188 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
189 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
190 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
191 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
192 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
193 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
194 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
195 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
196 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
197 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
198 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
199 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
200 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
201 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
202 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
203 { "NSMSG_WEAK_PASSWORD", "WARNING: You are using a password that is considered weak (easy to guess). It is STRONGLY recommended you change it (now, if not sooner) by typing \"/msg $S@$s PASS oldpass newpass\" (with your current password and a new password)." },
204 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
205 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
206 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
207 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
208 { "NSMSG_AUTH_ALLOWED_MSG", "You may now authenticate to account $b%s$b by typing $b/msg $N@$s auth %s password$b (using your password). If you will be using this computer regularly, please type $b/msg $N addmask$b (AFTER you auth) to permanently add your hostmask." },
209 { "NSMSG_AUTH_ALLOWED_EMAIL", "You may also (after you auth) type $b/msg $N set email user@your.isp$b to set an email address. This will let you use the $bauthcookie$b command to be authenticated in the future." },
210 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
211 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
212 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
213 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
214 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
215 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
216 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
217 { "NSMSG_PASS_SUCCESS", "Password changed." },
218 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
219 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
220 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
221 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
222 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
223 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
224 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
225 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
226 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
227 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
228 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
229 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
230 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
231 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
232 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
233 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
234 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
235 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
236 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
237 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
238 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
239 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
240 { "NSMSG_NO_ACCESS", "Access denied." },
241 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
242 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
243 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
244 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
245 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
246 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
247 { "NSMSG_BAD_HANDLE", "Account $b%s$b not registered because it is in use by a network service, is too long, or contains invalid characters." },
248 { "NSMSG_BAD_NICK", "Nickname $b%s$b not registered because it is in use by a network service, is too long, or contains invalid characters." },
249 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
250 { "NSMSG_FAIL_RENAME", "Account $b%s$b not renamed to $b%s$b because it is in use by a network services, or contains invalid characters." },
251 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
252 { "NSMSG_SEARCH_MATCH", "Match: %s" },
253 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
254 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
255 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
256 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
257 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
258 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
259 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
260 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
261 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
262 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
263 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
264 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
265 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
266 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
267 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
268 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
269 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
270 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
271 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
272 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
273 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
274 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
275 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
276 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
277 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
278 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
279 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
280 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
281 { "NSEMAIL_ACTIVATION_BODY", "This email has been sent to verify that this email address belongs to the person who tried to register an account on %1$s. Your cookie is:\n %2$s\nTo verify your email address and complete the account registration, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
282 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
283 { "NSEMAIL_PASSWORD_CHANGE_BODY", "This email has been sent to verify that you wish to change the password on your account %5$s. Your cookie is %2$s.\nTo complete the password change, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request your password to be changed, you do not need to do anything. Please contact the %1$s staff if you have questions." },
284 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
285 { "NSEMAIL_EMAIL_CHANGE_BODY_NEW", "This email has been sent to verify that your email address belongs to the same person as account %5$s on %1$s. The SECOND HALF of your cookie is %2$.*6$s.\nTo verify your address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s ?????%2$.*6$s\n(Replace the ????? with the FIRST HALF of the cookie, as sent to your OLD email address.)\nIf you did NOT request this email address to be associated with this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
286 { "NSEMAIL_EMAIL_CHANGE_BODY_OLD", "This email has been sent to verify that you want to change your email for account %5$s on %1$s from this address to %7$s. The FIRST HALF of your cookie is %2$.*6$s\nTo verify your new address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$.*6$s?????\n(Replace the ????? with the SECOND HALF of the cookie, as sent to your NEW email address.)\nIf you did NOT request this change of email address, you do not need to do anything. Please contact the %1$s staff if you have questions." },
287 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
288 { "NSEMAIL_EMAIL_VERIFY_BODY", "This email has been sent to verify that this address belongs to the same person as %5$s on %1$s. Your cookie is %2$s.\nTo verify your address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request this email address to be associated with this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
289 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
290 { "NSEMAIL_ALLOWAUTH_BODY", "This email has been sent to let you authenticate (auth) to account %5$s on %1$s. Your cookie is %2$s.\nTo auth to that account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %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 = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
978 snprintf(subject, sizeof(subject), fmt, netname);
979 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
980 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
983 case PASSWORD_CHANGE:
984 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
985 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
986 snprintf(subject, sizeof(subject), fmt, netname);
987 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
988 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
991 misc = hi->email_addr;
992 hi->email_addr = cookie->data;
994 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
995 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
996 snprintf(subject, sizeof(subject), fmt, netname);
997 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
998 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
999 sendmail(nickserv, hi, subject, body, 1);
1000 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1001 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1003 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1004 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1005 snprintf(subject, sizeof(subject), fmt, netname);
1006 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1007 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1008 sendmail(nickserv, hi, subject, body, 1);
1011 hi->email_addr = misc;
1014 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1015 snprintf(subject, sizeof(subject), fmt, netname);
1016 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1017 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1018 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1021 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1025 sendmail(nickserv, hi, subject, body, first_time);
1026 nickserv_bake_cookie(cookie);
1030 nickserv_eat_cookie(struct handle_cookie *cookie)
1032 cookie->hi->cookie = NULL;
1033 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1034 nickserv_free_cookie(cookie);
1038 nickserv_free_email_addr(void *data)
1040 handle_info_list_clean(data);
1045 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1047 struct handle_info_list *hil;
1048 /* Remove from old handle_info_list ... */
1049 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1050 handle_info_list_remove(hil, hi);
1051 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1052 hi->email_addr = NULL;
1054 /* Add to the new list.. */
1055 if (new_email_addr) {
1056 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1057 hil = calloc(1, sizeof(*hil));
1058 hil->tag = strdup(new_email_addr);
1059 handle_info_list_init(hil);
1060 dict_insert(nickserv_email_dict, hil->tag, hil);
1062 handle_info_list_append(hil, hi);
1063 hi->email_addr = hil->tag;
1067 static NICKSERV_FUNC(cmd_register)
1069 struct handle_info *hi;
1070 const char *email_addr, *password;
1073 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1074 /* Require the first handle registered to belong to someone +o. */
1075 reply("NSMSG_REQUIRE_OPER");
1079 if (user->handle_info) {
1080 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1084 if (IsStamped(user)) {
1085 /* Unauthenticated users might still have been stamped
1086 previously and could therefore have a hidden host;
1087 do not allow them to register a new account. */
1088 reply("NSMSG_STAMPED_REGISTER");
1092 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1094 if (!is_valid_handle(argv[1])) {
1095 reply("NSMSG_BAD_HANDLE", argv[1]);
1099 if ((argc >= 4) && nickserv_conf.email_enabled) {
1100 struct handle_info_list *hil;
1103 /* Remember email address. */
1104 email_addr = argv[3];
1106 /* Check that the email address looks valid.. */
1107 if (!is_valid_email_addr(email_addr)) {
1108 reply("NSMSG_BAD_EMAIL_ADDR");
1112 /* .. and that we are allowed to send to it. */
1113 if ((str = sendmail_prohibited_address(email_addr))) {
1114 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1118 /* If we do email verify, make sure we don't spam the address. */
1119 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1121 for (nn=0; nn<hil->used; nn++) {
1122 if (hil->list[nn]->cookie) {
1123 reply("NSMSG_EMAIL_UNACTIVATED");
1127 if (hil->used >= nickserv_conf.handles_per_email) {
1128 reply("NSMSG_EMAIL_OVERUSED");
1141 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1143 /* Add any masks they should get. */
1144 if (nickserv_conf.default_hostmask) {
1145 string_list_append(hi->masks, strdup("*@*"));
1147 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1148 if (user->ip.s_addr && user->hostname[strspn(user->hostname, "0123456789.")])
1149 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1152 /* If they're the first to register, give them level 1000. */
1153 if (dict_size(nickserv_handle_dict) == 1) {
1154 hi->opserv_level = 1000;
1155 reply("NSMSG_ROOT_HANDLE", argv[1]);
1158 /* Set their email address. */
1160 nickserv_set_email_addr(hi, email_addr);
1162 /* If they need to do email verification, tell them. */
1164 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1169 static NICKSERV_FUNC(cmd_oregister)
1172 struct userNode *settee;
1173 struct handle_info *hi;
1175 NICKSERV_MIN_PARMS(4);
1177 if (!is_valid_handle(argv[1])) {
1178 reply("NSMSG_BAD_HANDLE", argv[1]);
1182 if (strchr(argv[3], '@')) {
1183 mask = canonicalize_hostmask(strdup(argv[3]));
1185 settee = GetUserH(argv[4]);
1187 reply("MSG_NICK_UNKNOWN", argv[4]);
1194 } else if ((settee = GetUserH(argv[3]))) {
1195 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1197 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1200 if (settee && settee->handle_info) {
1201 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1205 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1209 string_list_append(hi->masks, mask);
1213 static NICKSERV_FUNC(cmd_handleinfo)
1216 unsigned int i, pos=0, herelen;
1217 struct userNode *target, *next_un;
1218 struct handle_info *hi;
1219 const char *nsmsg_none;
1222 if (!(hi = user->handle_info)) {
1223 reply("NSMSG_MUST_AUTH");
1226 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1230 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1231 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1232 #ifdef WITH_PROTOCOL_BAHAMUT
1233 reply("NSMSG_HANDLEINFO_ID", hi->id);
1235 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1238 intervalString(buff, now - hi->lastseen, user->handle_info);
1239 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1241 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1244 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1245 if (HANDLE_FLAGGED(hi, FROZEN))
1246 reply("NSMSG_HANDLEINFO_VACATION");
1248 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1249 struct do_not_register *dnr;
1250 if ((dnr = chanserv_is_dnr(NULL, hi)))
1251 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1252 if (!oper_outranks(user, hi))
1254 } else if (hi != user->handle_info)
1257 if (nickserv_conf.email_enabled)
1258 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1262 switch (hi->cookie->type) {
1263 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1264 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1265 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1266 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1267 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1273 unsigned long flen = 1;
1274 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1276 for (i=0, flen=1; handle_flags[i]; i++)
1277 if (hi->flags & 1 << i)
1278 flags[flen++] = handle_flags[i];
1280 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1282 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1285 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1286 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1287 || (hi->opserv_level > 0)) {
1288 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1291 if (hi->last_quit_host[0])
1292 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1294 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1296 if (nickserv_conf.disable_nicks) {
1297 /* nicks disabled; don't show anything about registered nicks */
1298 } else if (hi->nicks) {
1299 struct nick_info *ni, *next_ni;
1300 for (ni = hi->nicks; ni; ni = next_ni) {
1301 herelen = strlen(ni->nick);
1302 if (pos + herelen + 1 > ArrayLength(buff)) {
1304 goto print_nicks_buff;
1308 memcpy(buff+pos, ni->nick, herelen);
1309 pos += herelen; buff[pos++] = ' ';
1313 reply("NSMSG_HANDLEINFO_NICKS", buff);
1318 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1321 if (hi->masks->used) {
1322 for (i=0; i < hi->masks->used; i++) {
1323 herelen = strlen(hi->masks->list[i]);
1324 if (pos + herelen + 1 > ArrayLength(buff)) {
1326 goto print_mask_buff;
1328 memcpy(buff+pos, hi->masks->list[i], herelen);
1329 pos += herelen; buff[pos++] = ' ';
1330 if (i+1 == hi->masks->used) {
1333 reply("NSMSG_HANDLEINFO_MASKS", buff);
1338 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1342 struct userData *channel, *next;
1345 for (channel = hi->channels; channel; channel = next) {
1346 next = channel->u_next;
1347 name = channel->channel->channel->name;
1348 herelen = strlen(name);
1349 if (pos + herelen + 7 > ArrayLength(buff)) {
1351 goto print_chans_buff;
1353 if (IsUserSuspended(channel))
1355 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1359 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1364 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1367 for (target = hi->users; target; target = next_un) {
1368 herelen = strlen(target->nick);
1369 if (pos + herelen + 1 > ArrayLength(buff)) {
1371 goto print_cnick_buff;
1373 next_un = target->next_authed;
1375 memcpy(buff+pos, target->nick, herelen);
1376 pos += herelen; buff[pos++] = ' ';
1380 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1388 static NICKSERV_FUNC(cmd_userinfo)
1390 struct userNode *target;
1392 NICKSERV_MIN_PARMS(2);
1393 if (!(target = GetUserH(argv[1]))) {
1394 reply("MSG_NICK_UNKNOWN", argv[1]);
1397 if (target->handle_info)
1398 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1400 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1404 static NICKSERV_FUNC(cmd_nickinfo)
1406 struct nick_info *ni;
1408 NICKSERV_MIN_PARMS(2);
1409 if (!(ni = get_nick_info(argv[1]))) {
1410 reply("MSG_NICK_UNKNOWN", argv[1]);
1413 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1417 static NICKSERV_FUNC(cmd_rename_handle)
1419 struct handle_info *hi;
1420 char msgbuf[MAXLEN], *old_handle;
1423 NICKSERV_MIN_PARMS(3);
1424 if (!(hi = get_victim_oper(user, argv[1])))
1426 if (!is_valid_handle(argv[2])) {
1427 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1430 if (get_handle_info(argv[2])) {
1431 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1435 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1436 hi->handle = strdup(argv[2]);
1437 dict_insert(nickserv_handle_dict, hi->handle, hi);
1438 for (nn=0; nn<rf_list_used; nn++)
1439 rf_list[nn](hi, old_handle);
1440 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1441 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1442 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1447 static failpw_func_t *failpw_func_list;
1448 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1451 reg_failpw_func(failpw_func_t func)
1453 if (failpw_func_used == failpw_func_size) {
1454 if (failpw_func_size) {
1455 failpw_func_size <<= 1;
1456 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1458 failpw_func_size = 8;
1459 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1462 failpw_func_list[failpw_func_used++] = func;
1465 static NICKSERV_FUNC(cmd_auth)
1467 int pw_arg, used, maxlogins;
1468 struct handle_info *hi;
1470 struct userNode *other;
1472 if (user->handle_info) {
1473 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1476 if (IsStamped(user)) {
1477 /* Unauthenticated users might still have been stamped
1478 previously and could therefore have a hidden host;
1479 do not allow them to authenticate. */
1480 reply("NSMSG_STAMPED_AUTH");
1484 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1486 } else if (argc == 2) {
1487 if (nickserv_conf.disable_nicks) {
1488 if (!(hi = get_handle_info(user->nick))) {
1489 reply("NSMSG_HANDLE_NOT_FOUND");
1493 /* try to look up their handle from their nick */
1494 struct nick_info *ni;
1495 ni = get_nick_info(user->nick);
1497 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1504 reply("MSG_MISSING_PARAMS", argv[0]);
1505 svccmd_send_help(user, nickserv, cmd);
1509 reply("NSMSG_HANDLE_NOT_FOUND");
1512 /* Responses from here on look up the language used by the handle they asked about. */
1513 passwd = argv[pw_arg];
1514 if (!valid_user_for(user, hi)) {
1515 if (hi->email_addr && nickserv_conf.email_enabled)
1516 send_message_type(4, user, cmd->parent->bot,
1517 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1520 send_message_type(4, user, cmd->parent->bot,
1521 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1523 argv[pw_arg] = "BADMASK";
1526 if (!checkpass(passwd, hi->passwd)) {
1528 send_message_type(4, user, cmd->parent->bot,
1529 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1530 argv[pw_arg] = "BADPASS";
1531 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1532 if (nickserv_conf.autogag_enabled) {
1533 if (!user->auth_policer.params) {
1534 user->auth_policer.last_req = now;
1535 user->auth_policer.params = nickserv_conf.auth_policer_params;
1537 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1539 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1540 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1541 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1543 argv[pw_arg] = "GAGGED";
1548 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1549 send_message_type(4, user, cmd->parent->bot,
1550 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1551 argv[pw_arg] = "SUSPENDED";
1554 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1555 for (used = 0, other = hi->users; other; other = other->next_authed) {
1556 if (++used >= maxlogins) {
1557 send_message_type(4, user, cmd->parent->bot,
1558 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1560 argv[pw_arg] = "MAXLOGINS";
1565 set_user_handle_info(user, hi, 1);
1566 if (nickserv_conf.email_required && !hi->email_addr)
1567 reply("NSMSG_PLEASE_SET_EMAIL");
1568 if (!is_secure_password(hi->handle, passwd, NULL))
1569 reply("NSMSG_WEAK_PASSWORD");
1570 if (hi->passwd[0] != '$')
1571 cryptpass(passwd, hi->passwd);
1572 reply("NSMSG_AUTH_SUCCESS");
1573 argv[pw_arg] = "****";
1577 static allowauth_func_t *allowauth_func_list;
1578 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1581 reg_allowauth_func(allowauth_func_t func)
1583 if (allowauth_func_used == allowauth_func_size) {
1584 if (allowauth_func_size) {
1585 allowauth_func_size <<= 1;
1586 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1588 allowauth_func_size = 8;
1589 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1592 allowauth_func_list[allowauth_func_used++] = func;
1595 static NICKSERV_FUNC(cmd_allowauth)
1597 struct userNode *target;
1598 struct handle_info *hi;
1601 NICKSERV_MIN_PARMS(2);
1602 if (!(target = GetUserH(argv[1]))) {
1603 reply("MSG_NICK_UNKNOWN", argv[1]);
1606 if (target->handle_info) {
1607 reply("NSMSG_USER_PREV_AUTH", target->nick);
1610 if (IsStamped(target)) {
1611 /* Unauthenticated users might still have been stamped
1612 previously and could therefore have a hidden host;
1613 do not allow them to authenticate to an account. */
1614 send_message(target, nickserv, "NSMSG_USER_PREV_STAMP", target->nick);
1619 else if (!(hi = get_handle_info(argv[2]))) {
1620 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1624 if (hi->opserv_level > user->handle_info->opserv_level) {
1625 reply("MSG_USER_OUTRANKED", hi->handle);
1628 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1629 || (hi->opserv_level > 0))
1630 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1631 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1634 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1635 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1636 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1637 if (nickserv_conf.email_enabled)
1638 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1640 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1641 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1643 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1645 for (n=0; n<allowauth_func_used; n++)
1646 allowauth_func_list[n](user, target, hi);
1650 static NICKSERV_FUNC(cmd_authcookie)
1652 struct handle_info *hi;
1654 NICKSERV_MIN_PARMS(2);
1655 if (user->handle_info) {
1656 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1659 if (IsStamped(user)) {
1660 /* Unauthenticated users might still have been stamped
1661 previously and could therefore have a hidden host;
1662 do not allow them to authenticate to an account. */
1663 reply("NSMSG_STAMPED_AUTHCOOKIE");
1666 if (!(hi = get_handle_info(argv[1]))) {
1667 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1670 if (!hi->email_addr) {
1671 reply("MSG_SET_EMAIL_ADDR");
1674 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1678 static NICKSERV_FUNC(cmd_delcookie)
1680 struct handle_info *hi;
1682 hi = user->handle_info;
1684 reply("NSMSG_NO_COOKIE");
1687 switch (hi->cookie->type) {
1690 reply("NSMSG_MUST_TIME_OUT");
1693 nickserv_eat_cookie(hi->cookie);
1694 reply("NSMSG_ATE_COOKIE");
1700 static NICKSERV_FUNC(cmd_resetpass)
1702 struct handle_info *hi;
1703 char crypted[MD5_CRYPT_LENGTH];
1705 NICKSERV_MIN_PARMS(3);
1706 if (user->handle_info) {
1707 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1710 if (IsStamped(user)) {
1711 /* Unauthenticated users might still have been stamped
1712 previously and could therefore have a hidden host;
1713 do not allow them to activate an account. */
1714 reply("NSMSG_STAMPED_RESETPASS");
1717 if (!(hi = get_handle_info(argv[1]))) {
1718 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1721 if (!hi->email_addr) {
1722 reply("MSG_SET_EMAIL_ADDR");
1725 cryptpass(argv[2], crypted);
1727 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1731 static NICKSERV_FUNC(cmd_cookie)
1733 struct handle_info *hi;
1736 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1739 NICKSERV_MIN_PARMS(3);
1740 if (!(hi = get_handle_info(argv[1]))) {
1741 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1747 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1748 reply("NSMSG_HANDLE_SUSPENDED");
1753 reply("NSMSG_NO_COOKIE");
1757 /* Check validity of operation before comparing cookie to
1758 * prohibit guessing by authed users. */
1759 if (user->handle_info
1760 && (hi->cookie->type != EMAIL_CHANGE)
1761 && (hi->cookie->type != PASSWORD_CHANGE)) {
1762 reply("NSMSG_CANNOT_COOKIE");
1766 if (strcmp(cookie, hi->cookie->cookie)) {
1767 reply("NSMSG_BAD_COOKIE");
1771 switch (hi->cookie->type) {
1773 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1774 set_user_handle_info(user, hi, 1);
1775 reply("NSMSG_HANDLE_ACTIVATED");
1777 case PASSWORD_CHANGE:
1778 set_user_handle_info(user, hi, 1);
1779 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1780 reply("NSMSG_PASSWORD_CHANGED");
1783 nickserv_set_email_addr(hi, hi->cookie->data);
1784 reply("NSMSG_EMAIL_CHANGED");
1787 set_user_handle_info(user, hi, 1);
1788 reply("NSMSG_AUTH_SUCCESS");
1791 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1792 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1796 nickserv_eat_cookie(hi->cookie);
1801 static NICKSERV_FUNC(cmd_oregnick) {
1803 struct handle_info *target;
1804 struct nick_info *ni;
1806 NICKSERV_MIN_PARMS(3);
1807 if (!(target = modcmd_get_handle_info(user, argv[1])))
1810 if (!is_registerable_nick(nick)) {
1811 reply("NSMSG_BAD_NICK", nick);
1814 ni = dict_find(nickserv_nick_dict, nick, NULL);
1816 reply("NSMSG_NICK_EXISTS", nick);
1819 register_nick(nick, target);
1820 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1824 static NICKSERV_FUNC(cmd_regnick) {
1826 struct nick_info *ni;
1828 if (!is_registerable_nick(user->nick)) {
1829 reply("NSMSG_BAD_NICK", user->nick);
1832 /* count their nicks, see if it's too many */
1833 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1834 if (n >= nickserv_conf.nicks_per_handle) {
1835 reply("NSMSG_TOO_MANY_NICKS");
1838 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1840 reply("NSMSG_NICK_EXISTS", user->nick);
1843 register_nick(user->nick, user->handle_info);
1844 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1848 static NICKSERV_FUNC(cmd_pass)
1850 struct handle_info *hi;
1851 const char *old_pass, *new_pass;
1853 NICKSERV_MIN_PARMS(3);
1854 hi = user->handle_info;
1858 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1859 if (!checkpass(old_pass, hi->passwd)) {
1860 argv[1] = "BADPASS";
1861 reply("NSMSG_PASSWORD_INVALID");
1864 cryptpass(new_pass, hi->passwd);
1866 reply("NSMSG_PASS_SUCCESS");
1871 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1874 char *new_mask = canonicalize_hostmask(strdup(mask));
1875 for (i=0; i<hi->masks->used; i++) {
1876 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1877 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1882 string_list_append(hi->masks, new_mask);
1883 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
1887 static NICKSERV_FUNC(cmd_addmask)
1890 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1891 int res = nickserv_addmask(user, user->handle_info, mask);
1895 if (!is_gline(argv[1])) {
1896 reply("NSMSG_MASK_INVALID", argv[1]);
1899 return nickserv_addmask(user, user->handle_info, argv[1]);
1903 static NICKSERV_FUNC(cmd_oaddmask)
1905 struct handle_info *hi;
1907 NICKSERV_MIN_PARMS(3);
1908 if (!(hi = get_victim_oper(user, argv[1])))
1910 return nickserv_addmask(user, hi, argv[2]);
1914 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
1917 for (i=0; i<hi->masks->used; i++) {
1918 if (!strcmp(del_mask, hi->masks->list[i])) {
1919 char *old_mask = hi->masks->list[i];
1920 if (hi->masks->used == 1) {
1921 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
1924 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
1925 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
1930 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
1934 static NICKSERV_FUNC(cmd_delmask)
1936 NICKSERV_MIN_PARMS(2);
1937 return nickserv_delmask(user, user->handle_info, argv[1]);
1940 static NICKSERV_FUNC(cmd_odelmask)
1942 struct handle_info *hi;
1943 NICKSERV_MIN_PARMS(3);
1944 if (!(hi = get_victim_oper(user, argv[1])))
1946 return nickserv_delmask(user, hi, argv[2]);
1950 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
1951 unsigned int nn, add = 1, pos;
1952 unsigned long added, removed, flag;
1954 for (added=removed=nn=0; str[nn]; nn++) {
1956 case '+': add = 1; break;
1957 case '-': add = 0; break;
1959 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
1960 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
1963 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
1964 /* cheesy avoidance of looking up the flag name.. */
1965 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
1968 flag = 1 << (pos - 1);
1970 added |= flag, removed &= ~flag;
1972 removed |= flag, added &= ~flag;
1977 *premoved = removed;
1982 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
1984 unsigned long before, after, added, removed;
1985 struct userNode *uNode;
1987 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
1988 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
1990 hi->flags = (hi->flags | added) & ~removed;
1991 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
1993 /* Strip helping flag if they're only a support helper and not
1994 * currently in #support. */
1995 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
1996 struct channelList *schannels;
1998 schannels = chanserv_support_channels();
1999 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2000 for (ii = 0; ii < schannels->used; ++ii)
2001 if (GetUserMode(schannels->list[ii], uNode))
2003 if (ii < schannels->used)
2007 HANDLE_CLEAR_FLAG(hi, HELPING);
2010 if (after && !before) {
2011 /* Add user to current helper list. */
2012 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2013 userList_append(&curr_helpers, uNode);
2014 } else if (!after && before) {
2015 /* Remove user from current helper list. */
2016 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2017 userList_remove(&curr_helpers, uNode);
2024 set_list(struct userNode *user, struct handle_info *hi, int override)
2028 char *set_display[] = {
2029 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2030 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2033 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2035 /* Do this so options are presented in a consistent order. */
2036 for (i = 0; i < ArrayLength(set_display); ++i)
2037 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2038 opt(user, hi, override, 0, NULL);
2041 static NICKSERV_FUNC(cmd_set)
2043 struct handle_info *hi;
2046 hi = user->handle_info;
2048 set_list(user, hi, 0);
2051 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2052 reply("NSMSG_INVALID_OPTION", argv[1]);
2055 return opt(user, hi, 0, argc-1, argv+1);
2058 static NICKSERV_FUNC(cmd_oset)
2060 struct handle_info *hi;
2063 NICKSERV_MIN_PARMS(2);
2065 if (!(hi = get_victim_oper(user, argv[1])))
2069 set_list(user, hi, 0);
2073 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2074 reply("NSMSG_INVALID_OPTION", argv[2]);
2078 return opt(user, hi, 1, argc-2, argv+2);
2081 static OPTION_FUNC(opt_info)
2085 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2087 hi->infoline = NULL;
2089 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2093 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2094 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2098 static OPTION_FUNC(opt_width)
2101 unsigned int new_width = strtoul(argv[1], NULL, 0);
2102 hi->screen_width = new_width;
2105 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2106 hi->screen_width = MIN_LINE_SIZE;
2107 else if (hi->screen_width > MAX_LINE_SIZE)
2108 hi->screen_width = MAX_LINE_SIZE;
2110 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2114 static OPTION_FUNC(opt_tablewidth)
2117 unsigned int new_width = strtoul(argv[1], NULL, 0);
2118 hi->table_width = new_width;
2121 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2122 hi->table_width = MIN_LINE_SIZE;
2123 else if (hi->screen_width > MAX_LINE_SIZE)
2124 hi->table_width = MAX_LINE_SIZE;
2126 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2130 static OPTION_FUNC(opt_color)
2133 if (enabled_string(argv[1]))
2134 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2135 else if (disabled_string(argv[1]))
2136 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2138 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2143 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2147 static OPTION_FUNC(opt_privmsg)
2150 if (enabled_string(argv[1]))
2151 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2152 else if (disabled_string(argv[1]))
2153 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2155 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2160 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2164 static OPTION_FUNC(opt_style)
2169 if (!irccasecmp(argv[1], "Zoot"))
2170 hi->userlist_style = HI_STYLE_ZOOT;
2171 else if (!irccasecmp(argv[1], "def"))
2172 hi->userlist_style = HI_STYLE_DEF;
2175 switch (hi->userlist_style) {
2184 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2188 static OPTION_FUNC(opt_announcements)
2193 if (enabled_string(argv[1]))
2194 hi->announcements = 'y';
2195 else if (disabled_string(argv[1]))
2196 hi->announcements = 'n';
2197 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2198 hi->announcements = '?';
2200 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2205 switch (hi->announcements) {
2206 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2207 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2208 case '?': choice = "default"; break;
2209 default: choice = "unknown"; break;
2211 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2215 static OPTION_FUNC(opt_password)
2218 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2223 cryptpass(argv[1], hi->passwd);
2225 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2229 static OPTION_FUNC(opt_flags)
2232 unsigned int ii, flen;
2235 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2240 nickserv_apply_flags(user, hi, argv[1]);
2242 for (ii = flen = 0; handle_flags[ii]; ii++)
2243 if (hi->flags & (1 << ii))
2244 flags[flen++] = handle_flags[ii];
2247 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2249 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2253 static OPTION_FUNC(opt_email)
2257 if (!is_valid_email_addr(argv[1])) {
2258 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2261 if ((str = sendmail_prohibited_address(argv[1]))) {
2262 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2265 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2266 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2268 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2270 nickserv_set_email_addr(hi, argv[1]);
2272 nickserv_eat_cookie(hi->cookie);
2273 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2276 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2280 static OPTION_FUNC(opt_maxlogins)
2284 maxlogins = strtoul(argv[1], NULL, 0);
2285 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2286 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2289 hi->maxlogins = maxlogins;
2291 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2292 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2296 static OPTION_FUNC(opt_language)
2298 struct language *lang;
2300 lang = language_find(argv[1]);
2301 if (irccasecmp(lang->name, argv[1]))
2302 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2303 hi->language = lang;
2305 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2310 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2311 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2313 if ((user->handle_info->opserv_level < target->opserv_level)
2314 || ((user->handle_info->opserv_level == target->opserv_level)
2315 && (user->handle_info->opserv_level < 1000))) {
2316 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2319 if ((user->handle_info->opserv_level < new_level)
2320 || ((user->handle_info->opserv_level == new_level)
2321 && (user->handle_info->opserv_level < 1000))) {
2322 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2325 if (user->handle_info == target) {
2326 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2329 if (target->opserv_level == new_level)
2331 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2332 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2333 target->opserv_level = new_level;
2337 static OPTION_FUNC(opt_level)
2342 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2346 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2347 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2351 static OPTION_FUNC(opt_epithet)
2354 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2358 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2359 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2362 if ((epithet[0] == '*') && !epithet[1])
2365 hi->epithet = strdup(epithet);
2369 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2371 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2375 static NICKSERV_FUNC(cmd_reclaim)
2377 struct handle_info *hi;
2378 struct nick_info *ni;
2379 struct userNode *victim;
2381 NICKSERV_MIN_PARMS(2);
2382 hi = user->handle_info;
2383 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2385 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2388 if (ni->owner != user->handle_info) {
2389 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2392 victim = GetUserH(ni->nick);
2394 reply("MSG_NICK_UNKNOWN", ni->nick);
2397 if (victim == user) {
2398 reply("NSMSG_NICK_USER_YOU");
2401 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2402 switch (nickserv_conf.reclaim_action) {
2403 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2404 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2405 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2406 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2411 static NICKSERV_FUNC(cmd_unregnick)
2414 struct handle_info *hi;
2415 struct nick_info *ni;
2417 hi = user->handle_info;
2418 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2419 ni = dict_find(nickserv_nick_dict, nick, NULL);
2421 reply("NSMSG_UNKNOWN_NICK", nick);
2424 if (hi != ni->owner) {
2425 reply("NSMSG_NOT_YOUR_NICK", nick);
2428 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2433 static NICKSERV_FUNC(cmd_ounregnick)
2435 struct nick_info *ni;
2437 NICKSERV_MIN_PARMS(2);
2438 if (!(ni = get_nick_info(argv[1]))) {
2439 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2442 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2443 reply("MSG_USER_OUTRANKED", ni->nick);
2446 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2451 static NICKSERV_FUNC(cmd_unregister)
2453 struct handle_info *hi;
2456 NICKSERV_MIN_PARMS(2);
2457 hi = user->handle_info;
2460 if (checkpass(passwd, hi->passwd)) {
2461 nickserv_unregister_handle(hi, user);
2464 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2465 reply("NSMSG_PASSWORD_INVALID");
2470 static NICKSERV_FUNC(cmd_ounregister)
2472 struct handle_info *hi;
2474 NICKSERV_MIN_PARMS(2);
2475 if (!(hi = get_victim_oper(user, argv[1])))
2477 nickserv_unregister_handle(hi, user);
2481 static NICKSERV_FUNC(cmd_status)
2483 if (nickserv_conf.disable_nicks) {
2484 reply("NSMSG_GLOBAL_STATS_NONICK",
2485 dict_size(nickserv_handle_dict));
2487 if (user->handle_info) {
2489 struct nick_info *ni;
2490 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2491 reply("NSMSG_HANDLE_STATS", cnt);
2493 reply("NSMSG_HANDLE_NONE");
2495 reply("NSMSG_GLOBAL_STATS",
2496 dict_size(nickserv_handle_dict),
2497 dict_size(nickserv_nick_dict));
2502 static NICKSERV_FUNC(cmd_ghost)
2504 struct userNode *target;
2505 char reason[MAXLEN];
2507 NICKSERV_MIN_PARMS(2);
2508 if (!(target = GetUserH(argv[1]))) {
2509 reply("MSG_NICK_UNKNOWN", argv[1]);
2512 if (target == user) {
2513 reply("NSMSG_CANNOT_GHOST_SELF");
2516 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2517 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2520 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2521 DelUser(target, nickserv, 1, reason);
2522 reply("NSMSG_GHOST_KILLED", argv[1]);
2526 static NICKSERV_FUNC(cmd_vacation)
2528 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2529 reply("NSMSG_ON_VACATION");
2534 nickserv_saxdb_write(struct saxdb_context *ctx) {
2536 struct handle_info *hi;
2539 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2541 #ifdef WITH_PROTOCOL_BAHAMUT
2544 saxdb_start_record(ctx, iter_key(it), 0);
2545 if (hi->announcements != '?') {
2546 flags[0] = hi->announcements;
2548 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2551 struct handle_cookie *cookie = hi->cookie;
2554 switch (cookie->type) {
2555 case ACTIVATION: type = KEY_ACTIVATION; break;
2556 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2557 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2558 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2559 default: type = NULL; break;
2562 saxdb_start_record(ctx, KEY_COOKIE, 0);
2563 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2564 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2566 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2567 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2568 saxdb_end_record(ctx);
2572 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2574 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2578 for (ii=flen=0; handle_flags[ii]; ++ii)
2579 if (hi->flags & (1 << ii))
2580 flags[flen++] = handle_flags[ii];
2582 saxdb_write_string(ctx, KEY_FLAGS, flags);
2584 #ifdef WITH_PROTOCOL_BAHAMUT
2585 saxdb_write_int(ctx, KEY_ID, hi->id);
2588 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2589 if (hi->last_quit_host[0])
2590 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2591 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2592 if (hi->masks->used)
2593 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2595 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2597 struct string_list *slist;
2598 struct nick_info *ni;
2600 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2601 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2602 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2606 if (hi->opserv_level)
2607 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2608 if (hi->language != lang_C)
2609 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2610 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2611 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2612 if (hi->screen_width)
2613 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2614 if (hi->table_width)
2615 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2616 flags[0] = hi->userlist_style;
2618 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2619 saxdb_end_record(ctx);
2624 static handle_merge_func_t *handle_merge_func_list;
2625 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2628 reg_handle_merge_func(handle_merge_func_t func)
2630 if (handle_merge_func_used == handle_merge_func_size) {
2631 if (handle_merge_func_size) {
2632 handle_merge_func_size <<= 1;
2633 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2635 handle_merge_func_size = 8;
2636 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2639 handle_merge_func_list[handle_merge_func_used++] = func;
2642 static NICKSERV_FUNC(cmd_merge)
2644 struct handle_info *hi_from, *hi_to;
2645 struct userNode *last_user;
2646 struct userData *cList, *cListNext;
2647 unsigned int ii, jj, n;
2648 char buffer[MAXLEN];
2650 NICKSERV_MIN_PARMS(3);
2652 if (!(hi_from = get_victim_oper(user, argv[1])))
2654 if (!(hi_to = get_victim_oper(user, argv[2])))
2656 if (hi_to == hi_from) {
2657 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2661 for (n=0; n<handle_merge_func_used; n++)
2662 handle_merge_func_list[n](user, hi_to, hi_from);
2664 /* Append "from" handle's nicks to "to" handle's nick list. */
2666 struct nick_info *last_ni;
2667 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2668 last_ni->next = hi_from->nicks;
2670 while (hi_from->nicks) {
2671 hi_from->nicks->owner = hi_to;
2672 hi_from->nicks = hi_from->nicks->next;
2675 /* Merge the hostmasks. */
2676 for (ii=0; ii<hi_from->masks->used; ii++) {
2677 char *mask = hi_from->masks->list[ii];
2678 for (jj=0; jj<hi_to->masks->used; jj++)
2679 if (match_ircglobs(hi_to->masks->list[jj], mask))
2681 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2682 string_list_append(hi_to->masks, strdup(mask));
2685 /* Merge the lists of authed users. */
2687 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2688 last_user->next_authed = hi_from->users;
2690 hi_to->users = hi_from->users;
2692 /* Repoint the old "from" handle's users. */
2693 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2694 last_user->handle_info = hi_to;
2696 hi_from->users = NULL;
2698 /* Merge channel userlists. */
2699 for (cList=hi_from->channels; cList; cList=cListNext) {
2700 struct userData *cList2;
2701 cListNext = cList->u_next;
2702 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2703 if (cList->channel == cList2->channel)
2705 log_module(NS_LOG, LOG_DEBUG, "Merging %s->%s@%s: before %p->%p->%-p, %p->%p->%p",
2706 hi_from->handle, hi_to->handle, cList->channel->channel->name,
2707 cList->u_prev, cList, cList->u_next,
2708 (cList2?cList2->u_prev:0), cList2, (cList2?cList2->u_next:0));
2709 if (cList2 && (cList2->access >= cList->access)) {
2710 /* keep cList2 in hi_to; remove cList from hi_from */
2711 log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList);
2712 del_channel_user(cList, 1);
2715 /* remove the lower-ranking cList2 from hi_to */
2716 log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList2);
2717 del_channel_user(cList2, 1);
2719 /* cList needs to be moved from hi_from to hi_to */
2720 cList->handle = hi_to;
2721 /* Remove from linked list for hi_from */
2722 assert(!cList->u_prev);
2723 hi_from->channels = cList->u_next;
2725 cList->u_next->u_prev = cList->u_prev;
2726 /* Add to linked list for hi_to */
2727 cList->u_prev = NULL;
2728 cList->u_next = hi_to->channels;
2729 if (hi_to->channels)
2730 hi_to->channels->u_prev = cList;
2731 hi_to->channels = cList;
2732 log_module(NS_LOG, LOG_DEBUG, "Now %p->%p->%p",
2733 cList->u_prev, cList, cList->u_next);
2737 /* Do they get an OpServ level promotion? */
2738 if (hi_from->opserv_level > hi_to->opserv_level)
2739 hi_to->opserv_level = hi_from->opserv_level;
2741 /* What about last seen time? */
2742 if (hi_from->lastseen > hi_to->lastseen)
2743 hi_to->lastseen = hi_from->lastseen;
2745 /* Notify of success. */
2746 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2747 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2748 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2750 /* Unregister the "from" handle. */
2751 nickserv_unregister_handle(hi_from, NULL);
2756 struct nickserv_discrim {
2757 unsigned int limit, min_level, max_level;
2758 unsigned long flags_on, flags_off;
2759 time_t min_registered, max_registered;
2761 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2762 const char *nickmask;
2763 const char *hostmask;
2764 const char *handlemask;
2765 const char *emailmask;
2768 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2770 struct discrim_apply_info {
2771 struct nickserv_discrim *discrim;
2772 discrim_search_func func;
2773 struct userNode *source;
2774 unsigned int matched;
2777 static struct nickserv_discrim *
2778 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2781 struct nickserv_discrim *discrim;
2783 discrim = malloc(sizeof(*discrim));
2784 memset(discrim, 0, sizeof(*discrim));
2785 discrim->min_level = 0;
2786 discrim->max_level = ~0;
2787 discrim->limit = 50;
2788 discrim->min_registered = 0;
2789 discrim->max_registered = INT_MAX;
2790 discrim->lastseen = now;
2792 for (i=0; i<argc; i++) {
2793 if (i == argc - 1) {
2794 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2797 if (!irccasecmp(argv[i], "limit")) {
2798 discrim->limit = atoi(argv[++i]);
2799 } else if (!irccasecmp(argv[i], "flags")) {
2800 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2801 } else if (!irccasecmp(argv[i], "registered")) {
2802 const char *cmp = argv[++i];
2803 if (cmp[0] == '<') {
2804 if (cmp[1] == '=') {
2805 discrim->min_registered = now - ParseInterval(cmp+2);
2807 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2809 } else if (cmp[0] == '=') {
2810 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2811 } else if (cmp[0] == '>') {
2812 if (cmp[1] == '=') {
2813 discrim->max_registered = now - ParseInterval(cmp+2);
2815 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2818 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2820 } else if (!irccasecmp(argv[i], "seen")) {
2821 discrim->lastseen = now - ParseInterval(argv[++i]);
2822 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2823 discrim->nickmask = argv[++i];
2824 } else if (!irccasecmp(argv[i], "hostmask")) {
2826 if (!irccasecmp(argv[i], "exact")) {
2827 if (i == argc - 1) {
2828 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2831 discrim->hostmask_type = EXACT;
2832 } else if (!irccasecmp(argv[i], "subset")) {
2833 if (i == argc - 1) {
2834 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2837 discrim->hostmask_type = SUBSET;
2838 } else if (!irccasecmp(argv[i], "superset")) {
2839 if (i == argc - 1) {
2840 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2843 discrim->hostmask_type = SUPERSET;
2844 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2845 if (i == argc - 1) {
2846 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2849 discrim->hostmask_type = LASTQUIT;
2852 discrim->hostmask_type = SUPERSET;
2854 discrim->hostmask = argv[++i];
2855 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2856 if (!irccasecmp(argv[++i], "*")) {
2857 discrim->handlemask = 0;
2859 discrim->handlemask = argv[i];
2861 } else if (!irccasecmp(argv[i], "email")) {
2862 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2863 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2865 } else if (!irccasecmp(argv[++i], "*")) {
2866 discrim->emailmask = 0;
2868 discrim->emailmask = argv[i];
2870 } else if (!irccasecmp(argv[i], "access")) {
2871 const char *cmp = argv[++i];
2872 if (cmp[0] == '<') {
2873 if (discrim->min_level == 0) discrim->min_level = 1;
2874 if (cmp[1] == '=') {
2875 discrim->max_level = strtoul(cmp+2, NULL, 0);
2877 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
2879 } else if (cmp[0] == '=') {
2880 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
2881 } else if (cmp[0] == '>') {
2882 if (cmp[1] == '=') {
2883 discrim->min_level = strtoul(cmp+2, NULL, 0);
2885 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
2888 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2891 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
2902 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
2904 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
2905 || (discrim->flags_off & hi->flags)
2906 || (discrim->min_registered > hi->registered)
2907 || (discrim->max_registered < hi->registered)
2908 || (discrim->lastseen < (hi->users?now:hi->lastseen))
2909 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
2910 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
2911 || (discrim->min_level > hi->opserv_level)
2912 || (discrim->max_level < hi->opserv_level)) {
2915 if (discrim->hostmask) {
2917 for (i=0; i<hi->masks->used; i++) {
2918 const char *mask = hi->masks->list[i];
2919 if ((discrim->hostmask_type == SUBSET)
2920 && (match_ircglobs(discrim->hostmask, mask))) break;
2921 else if ((discrim->hostmask_type == EXACT)
2922 && !irccasecmp(discrim->hostmask, mask)) break;
2923 else if ((discrim->hostmask_type == SUPERSET)
2924 && (match_ircglobs(mask, discrim->hostmask))) break;
2925 else if ((discrim->hostmask_type == LASTQUIT)
2926 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
2928 if (i==hi->masks->used) return 0;
2930 if (discrim->nickmask) {
2931 struct nick_info *nick = hi->nicks;
2933 if (match_ircglob(nick->nick, discrim->nickmask)) break;
2936 if (!nick) return 0;
2942 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
2944 dict_iterator_t it, next;
2945 unsigned int matched;
2947 for (it = dict_first(nickserv_handle_dict), matched = 0;
2948 it && (matched < discrim->limit);
2950 next = iter_next(it);
2951 if (nickserv_discrim_match(discrim, iter_data(it))) {
2952 dsf(source, iter_data(it));
2960 search_print_func(struct userNode *source, struct handle_info *match)
2962 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
2966 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
2971 search_unregister_func (struct userNode *source, struct handle_info *match)
2973 if (oper_has_access(source, nickserv, match->opserv_level, 0))
2974 nickserv_unregister_handle(match, source);
2978 nickserv_sort_accounts_by_access(const void *a, const void *b)
2980 const struct handle_info *hi_a = *(const struct handle_info**)a;
2981 const struct handle_info *hi_b = *(const struct handle_info**)b;
2982 if (hi_a->opserv_level != hi_b->opserv_level)
2983 return hi_b->opserv_level - hi_a->opserv_level;
2984 return irccasecmp(hi_a->handle, hi_b->handle);
2988 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
2990 struct handle_info_list hil;
2991 struct helpfile_table tbl;
2996 memset(&hil, 0, sizeof(hil));
2997 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2998 struct handle_info *hi = iter_data(it);
2999 if (hi->opserv_level)
3000 handle_info_list_append(&hil, hi);
3002 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3003 tbl.length = hil.used + 1;
3005 tbl.flags = TABLE_NO_FREE;
3006 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3007 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3010 for (ii = 0; ii < hil.used; ) {
3011 ary = malloc(tbl.width * sizeof(ary[0]));
3012 ary[0] = hil.list[ii]->handle;
3013 ary[1] = strtab(hil.list[ii]->opserv_level);
3014 tbl.contents[++ii] = ary;
3016 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3017 reply("MSG_MATCH_COUNT", hil.used);
3018 for (ii = 0; ii < hil.used; )
3019 free(tbl.contents[++ii]);
3024 static NICKSERV_FUNC(cmd_search)
3026 struct nickserv_discrim *discrim;
3027 discrim_search_func action;
3028 struct svccmd *subcmd;
3029 unsigned int matches;
3032 NICKSERV_MIN_PARMS(3);
3033 sprintf(buf, "search %s", argv[1]);
3034 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3035 if (!irccasecmp(argv[1], "print"))
3036 action = search_print_func;
3037 else if (!irccasecmp(argv[1], "count"))
3038 action = search_count_func;
3039 else if (!irccasecmp(argv[1], "unregister"))
3040 action = search_unregister_func;
3042 reply("NSMSG_INVALID_ACTION", argv[1]);
3046 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3049 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3053 if (action == search_print_func)
3054 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3055 else if (action == search_count_func)
3056 discrim->limit = INT_MAX;
3058 matches = nickserv_discrim_search(discrim, action, user);
3061 reply("MSG_MATCH_COUNT", matches);
3063 reply("MSG_NO_MATCHES");
3069 static MODCMD_FUNC(cmd_checkpass)
3071 struct handle_info *hi;
3073 NICKSERV_MIN_PARMS(3);
3074 if (!(hi = get_handle_info(argv[1]))) {
3075 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3078 if (checkpass(argv[2], hi->passwd))
3079 reply("CHECKPASS_YES");
3081 reply("CHECKPASS_NO");
3087 nickserv_db_read_handle(const char *handle, dict_t obj)
3090 struct string_list *masks, *slist;
3091 struct handle_info *hi;
3092 struct userNode *authed_users;
3093 unsigned long int id;
3097 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3098 id = str ? strtoul(str, NULL, 0) : 0;
3099 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3101 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3104 if ((hi = get_handle_info(handle))) {
3105 authed_users = hi->users;
3107 dict_remove(nickserv_handle_dict, hi->handle);
3109 authed_users = NULL;
3111 hi = register_handle(handle, str, id);
3113 hi->users = authed_users;
3114 while (authed_users) {
3115 authed_users->handle_info = hi;
3116 authed_users = authed_users->next_authed;
3119 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3120 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3121 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3122 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3123 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3124 hi->language = language_find(str ? str : "C");
3125 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3126 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3127 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3129 hi->infoline = strdup(str);
3130 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3131 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3132 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3133 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3134 /* We want to read the nicks even if disable_nicks is set. This is so
3135 * that we don't lose the nick data entirely. */
3136 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3138 for (ii=0; ii<slist->used; ii++)
3139 register_nick(slist->list[ii], hi);
3141 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3143 for (ii=0; str[ii]; ii++)
3144 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3146 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3147 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3148 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3149 hi->announcements = str ? str[0] : '?';
3150 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3151 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3152 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3153 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3154 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3156 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3158 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3159 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3161 nickserv_set_email_addr(hi, str);
3162 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3164 hi->epithet = strdup(str);
3165 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3167 const char *data, *type, *expires, *cookie_str;
3168 struct handle_cookie *cookie;
3170 cookie = calloc(1, sizeof(*cookie));
3171 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3172 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3173 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3174 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3175 if (!type || !expires || !cookie_str) {
3176 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3179 if (!irccasecmp(type, KEY_ACTIVATION))
3180 cookie->type = ACTIVATION;
3181 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3182 cookie->type = PASSWORD_CHANGE;
3183 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3184 cookie->type = EMAIL_CHANGE;
3185 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3186 cookie->type = ALLOWAUTH;
3188 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3191 cookie->expires = strtoul(expires, NULL, 0);
3192 if (cookie->expires < now)
3195 cookie->data = strdup(data);
3196 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3200 nickserv_bake_cookie(cookie);
3202 nickserv_free_cookie(cookie);
3207 nickserv_saxdb_read(dict_t db) {
3209 struct record_data *rd;
3211 for (it=dict_first(db); it; it=iter_next(it)) {
3213 nickserv_db_read_handle(iter_key(it), rd->d.object);
3218 static NICKSERV_FUNC(cmd_mergedb)
3220 struct timeval start, stop;
3223 NICKSERV_MIN_PARMS(2);
3224 gettimeofday(&start, NULL);
3225 if (!(db = parse_database(argv[1]))) {
3226 reply("NSMSG_DB_UNREADABLE", argv[1]);
3229 nickserv_saxdb_read(db);
3231 gettimeofday(&stop, NULL);
3232 stop.tv_sec -= start.tv_sec;
3233 stop.tv_usec -= start.tv_usec;
3234 if (stop.tv_usec < 0) {
3236 stop.tv_usec += 1000000;
3238 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3243 expire_handles(UNUSED_ARG(void *data))
3245 dict_iterator_t it, next;
3247 struct handle_info *hi;
3249 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3250 next = iter_next(it);
3252 if ((hi->opserv_level > 0)
3254 || HANDLE_FLAGGED(hi, FROZEN)
3255 || HANDLE_FLAGGED(hi, NODELETE)) {
3258 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3259 if ((now - hi->lastseen) > expiry) {
3260 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3261 nickserv_unregister_handle(hi, NULL);
3265 if (nickserv_conf.handle_expire_frequency)
3266 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3270 nickserv_load_dict(const char *fname)
3274 if (!(file = fopen(fname, "r"))) {
3275 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3278 while (!feof(file)) {
3279 fgets(line, sizeof(line), file);
3282 if (line[strlen(line)-1] == '\n')
3283 line[strlen(line)-1] = 0;
3284 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3287 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3290 static enum reclaim_action
3291 reclaim_action_from_string(const char *str) {
3293 return RECLAIM_NONE;
3294 else if (!irccasecmp(str, "warn"))
3295 return RECLAIM_WARN;
3296 else if (!irccasecmp(str, "svsnick"))
3297 return RECLAIM_SVSNICK;
3298 else if (!irccasecmp(str, "kill"))
3299 return RECLAIM_KILL;
3301 return RECLAIM_NONE;
3305 nickserv_conf_read(void)
3307 dict_t conf_node, child;
3311 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3312 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3315 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3317 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3318 if (nickserv_conf.valid_handle_regex_set) regfree(&nickserv_conf.valid_handle_regex);
3320 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3321 nickserv_conf.valid_handle_regex_set = !err;
3322 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3324 nickserv_conf.valid_handle_regex_set = 0;
3326 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3327 if (nickserv_conf.valid_nick_regex_set) regfree(&nickserv_conf.valid_nick_regex);
3329 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3330 nickserv_conf.valid_nick_regex_set = !err;
3331 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3333 nickserv_conf.valid_nick_regex_set = 0;
3335 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3337 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3338 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3339 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3340 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3341 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3342 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3343 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3344 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3345 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3346 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3347 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3348 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3349 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3350 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3351 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3352 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3353 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3354 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3355 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3356 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3357 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3359 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3360 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3361 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3363 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3364 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3365 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3367 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3368 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3369 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3370 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3371 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3372 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3373 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3374 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3375 if (!nickserv_conf.disable_nicks) {
3376 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3377 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3378 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3379 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3380 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3381 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3382 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3383 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3385 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3386 for (it=dict_first(child); it; it=iter_next(it)) {
3387 const char *key = iter_key(it), *value;
3391 if (!strncasecmp(key, "uc_", 3))
3392 flag = toupper(key[3]);
3393 else if (!strncasecmp(key, "lc_", 3))
3394 flag = tolower(key[3]);
3398 if ((pos = handle_inverse_flags[flag])) {
3399 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3400 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3403 if (nickserv_conf.weak_password_dict)
3404 dict_delete(nickserv_conf.weak_password_dict);
3405 nickserv_conf.weak_password_dict = dict_new();
3406 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3407 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3408 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3409 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3411 nickserv_load_dict(str);
3412 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3413 if (nickserv && str)
3414 NickChange(nickserv, str, 0);
3415 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3416 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3417 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3418 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3419 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3420 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3421 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3422 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3423 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3424 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3425 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3426 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3427 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3428 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3429 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3430 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3431 str = conf_get_data("server/network", RECDB_QSTRING);
3432 nickserv_conf.network_name = str ? str : "some IRC network";
3433 if (!nickserv_conf.auth_policer_params) {
3434 nickserv_conf.auth_policer_params = policer_params_new();
3435 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3436 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3438 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3439 for (it=dict_first(child); it; it=iter_next(it))
3440 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3444 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3445 char newnick[NICKLEN+1];
3454 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3456 case RECLAIM_SVSNICK:
3458 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3459 } while (GetUserH(newnick));
3460 irc_svsnick(nickserv, user, newnick);
3463 irc_kill(nickserv, user, "NSMSG_RECLAIM_KILL");
3469 nickserv_reclaim_p(void *data) {
3470 struct userNode *user = data;
3471 struct nick_info *ni = get_nick_info(user->nick);
3473 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3477 check_user_nick(struct userNode *user) {
3478 struct nick_info *ni;
3479 user->modes &= ~FLAGS_REGNICK;
3480 if (!(ni = get_nick_info(user->nick)))
3482 if (user->handle_info == ni->owner) {
3483 user->modes |= FLAGS_REGNICK;
3487 if (nickserv_conf.warn_nick_owned)
3488 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3489 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3491 if (nickserv_conf.auto_reclaim_delay)
3492 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3494 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3499 handle_new_user(struct userNode *user)
3501 return check_user_nick(user);
3505 handle_account(struct userNode *user, const char *stamp)
3507 struct handle_info *hi;
3509 #ifdef WITH_PROTOCOL_P10
3510 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3512 hi = dict_find(nickserv_id_dict, stamp, NULL);
3516 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3519 set_user_handle_info(user, hi, 0);
3521 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3526 handle_nick_change(struct userNode *user, const char *old_nick)
3528 struct handle_info *hi;
3530 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3531 dict_remove(nickserv_allow_auth_dict, old_nick);
3532 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3534 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3535 check_user_nick(user);
3539 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3541 dict_remove(nickserv_allow_auth_dict, user->nick);
3542 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3543 set_user_handle_info(user, NULL, 0);
3546 static struct modcmd *
3547 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3549 if (min_level > 0) {
3551 sprintf(buf, "%u", min_level);
3552 if (must_be_qualified) {
3553 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3555 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3557 } else if (min_level == 0) {
3558 if (must_be_qualified) {
3559 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3561 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3564 if (must_be_qualified) {
3565 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3567 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3573 nickserv_db_cleanup(void)
3575 unreg_del_user_func(nickserv_remove_user);
3576 userList_clean(&curr_helpers);
3577 policer_params_delete(nickserv_conf.auth_policer_params);
3578 dict_delete(nickserv_handle_dict);
3579 dict_delete(nickserv_nick_dict);
3580 dict_delete(nickserv_opt_dict);
3581 dict_delete(nickserv_allow_auth_dict);
3582 dict_delete(nickserv_email_dict);
3583 dict_delete(nickserv_id_dict);
3584 dict_delete(nickserv_conf.weak_password_dict);
3585 free(auth_func_list);
3586 free(unreg_func_list);
3588 free(allowauth_func_list);
3589 free(handle_merge_func_list);
3590 free(failpw_func_list);
3591 if (nickserv_conf.valid_handle_regex_set)
3592 regfree(&nickserv_conf.valid_handle_regex);
3593 if (nickserv_conf.valid_nick_regex_set)
3594 regfree(&nickserv_conf.valid_nick_regex);
3598 init_nickserv(const char *nick)
3601 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3602 reg_new_user_func(handle_new_user);
3603 reg_nick_change_func(handle_nick_change);
3604 reg_del_user_func(nickserv_remove_user);
3605 reg_account_func(handle_account);
3607 /* set up handle_inverse_flags */
3608 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3609 for (i=0; handle_flags[i]; i++) {
3610 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3611 flag_access_levels[i] = 0;
3614 conf_register_reload(nickserv_conf_read);
3615 nickserv_opt_dict = dict_new();
3616 nickserv_email_dict = dict_new();
3617 dict_set_free_keys(nickserv_email_dict, free);
3618 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3620 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3621 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3622 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3623 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3624 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3625 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3626 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3627 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3628 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3629 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3630 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3631 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3632 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3633 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3634 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3635 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3636 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3637 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3638 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3639 if (!nickserv_conf.disable_nicks) {
3640 /* nick management commands */
3641 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3642 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3643 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3644 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3645 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3646 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3648 if (nickserv_conf.email_enabled) {
3649 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3650 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3651 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3652 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3653 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3655 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3656 /* miscellaneous commands */
3657 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3658 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3659 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3660 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3661 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3663 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3664 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3665 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3666 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3667 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3668 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3669 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3670 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3671 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3672 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3673 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3674 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3675 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3676 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3677 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3679 nickserv_handle_dict = dict_new();
3680 dict_set_free_keys(nickserv_handle_dict, free);
3681 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3683 nickserv_id_dict = dict_new();
3684 dict_set_free_keys(nickserv_id_dict, free);
3686 nickserv_nick_dict = dict_new();
3687 dict_set_free_data(nickserv_nick_dict, free_nick_info);
3689 nickserv_allow_auth_dict = dict_new();
3691 userList_init(&curr_helpers);
3694 nickserv = AddService(nick, "Nick Services");
3695 nickserv_service = service_register(nickserv, 0);
3697 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3698 reg_exit_func(nickserv_db_cleanup);
3699 if(nickserv_conf.handle_expire_frequency)
3700 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3701 message_register_table(msgtab);