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 %2$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 if (IsService(target))
677 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
679 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
682 if (!(hi = target->handle_info)) {
683 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
691 oper_outranks(struct userNode *user, struct handle_info *hi) {
692 if (user->handle_info->opserv_level > hi->opserv_level)
694 if (user->handle_info->opserv_level == hi->opserv_level) {
695 if ((user->handle_info->opserv_level == 1000)
696 || (user->handle_info == hi)
697 || ((user->handle_info->opserv_level == 0)
698 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
699 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
703 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
707 static struct handle_info *
708 get_victim_oper(struct userNode *user, const char *target)
710 struct handle_info *hi;
711 if (!(hi = smart_get_handle_info(nickserv, user, target)))
713 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
714 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
717 return oper_outranks(user, hi) ? hi : NULL;
721 valid_user_for(struct userNode *user, struct handle_info *hi)
725 /* If no hostmasks on the account, allow it. */
726 if (!hi->masks->used)
728 /* If any hostmask matches, allow it. */
729 for (ii=0; ii<hi->masks->used; ii++)
730 if (user_matches_glob(user, hi->masks->list[ii], 0))
732 /* If they are allowauthed to this account, allow it (removing the aa). */
733 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
734 dict_remove(nickserv_allow_auth_dict, user->nick);
737 /* The user is not allowed to use this account. */
742 is_secure_password(const char *handle, const char *pass, struct userNode *user)
745 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
747 if (len < nickserv_conf.password_min_length) {
749 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
752 if (!irccasecmp(pass, handle)) {
754 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
757 dict_find(nickserv_conf.weak_password_dict, pass, &i);
760 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
763 for (i=0; i<len; i++) {
764 if (isdigit(pass[i]))
766 if (isupper(pass[i]))
768 if (islower(pass[i]))
771 if ((cnt_lower < nickserv_conf.password_min_lower)
772 || (cnt_upper < nickserv_conf.password_min_upper)
773 || (cnt_digits < nickserv_conf.password_min_digits)) {
775 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
781 static auth_func_t *auth_func_list;
782 static unsigned int auth_func_size = 0, auth_func_used = 0;
785 reg_auth_func(auth_func_t func)
787 if (auth_func_used == auth_func_size) {
788 if (auth_func_size) {
789 auth_func_size <<= 1;
790 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
793 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
796 auth_func_list[auth_func_used++] = func;
799 static handle_rename_func_t *rf_list;
800 static unsigned int rf_list_size, rf_list_used;
803 reg_handle_rename_func(handle_rename_func_t func)
805 if (rf_list_used == rf_list_size) {
808 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
811 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
814 rf_list[rf_list_used++] = func;
818 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
821 struct handle_info *old_info;
823 /* This can happen if somebody uses COOKIE while authed, or if
824 * they re-auth to their current handle (which is silly, but users
826 if (user->handle_info == hi)
829 if (user->handle_info) {
830 struct userNode *other;
833 userList_remove(&curr_helpers, user);
835 /* remove from next_authed linked list */
836 if (user->handle_info->users == user) {
837 user->handle_info->users = user->next_authed;
839 for (other = user->handle_info->users;
840 other->next_authed != user;
841 other = other->next_authed) ;
842 other->next_authed = user->next_authed;
844 /* if nobody left on old handle, and they're not an oper, remove !god */
845 if (!user->handle_info->users && !user->handle_info->opserv_level)
846 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
847 /* record them as being last seen at this time */
848 user->handle_info->lastseen = now;
849 /* and record their hostmask */
850 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
852 old_info = user->handle_info;
853 user->handle_info = hi;
854 if (hi && !hi->users && !hi->opserv_level)
855 HANDLE_CLEAR_FLAG(hi, HELPING);
856 for (n=0; n<auth_func_used; n++)
857 auth_func_list[n](user, old_info);
859 struct nick_info *ni;
861 HANDLE_CLEAR_FLAG(hi, FROZEN);
862 if (nickserv_conf.warn_clone_auth) {
863 struct userNode *other;
864 for (other = hi->users; other; other = other->next_authed)
865 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
867 user->next_authed = hi->users;
871 userList_append(&curr_helpers, user);
874 #ifdef WITH_PROTOCOL_BAHAMUT
875 /* Stamp users with their account ID. */
877 inttobase64(id, hi->id, IDLEN);
878 #elif WITH_PROTOCOL_P10
879 /* Stamp users with their account name. */
880 char *id = hi->handle;
882 const char *id = "???";
884 if (!nickserv_conf.disable_nicks) {
885 struct nick_info *ni;
886 for (ni = hi->nicks; ni; ni = ni->next) {
887 if (!irccasecmp(user->nick, ni->nick)) {
888 user->modes |= FLAGS_REGNICK;
896 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
897 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
899 /* We cannot clear the user's account ID, unfortunately. */
900 user->next_authed = NULL;
904 static struct handle_info*
905 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
907 struct handle_info *hi;
908 struct nick_info *ni;
909 char crypted[MD5_CRYPT_LENGTH];
911 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
912 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
916 if (!is_secure_password(handle, passwd, user))
919 cryptpass(passwd, crypted);
920 hi = register_handle(handle, crypted, 0);
921 hi->masks = alloc_string_list(1);
923 hi->language = lang_C;
924 hi->registered = now;
926 hi->flags = HI_DEFAULT_FLAGS;
927 if (settee && !no_auth)
928 set_user_handle_info(settee, hi, 1);
931 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
932 else if (nickserv_conf.disable_nicks)
933 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
934 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
935 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
937 register_nick(user->nick, hi);
938 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
940 if (settee && (user != settee))
941 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
946 nickserv_bake_cookie(struct handle_cookie *cookie)
948 cookie->hi->cookie = cookie;
949 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
953 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
955 struct handle_cookie *cookie;
956 char subject[128], body[4096], *misc;
957 const char *netname, *fmt;
961 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
965 cookie = calloc(1, sizeof(*cookie));
968 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
969 cookie->expires = now + nickserv_conf.cookie_timeout;
970 inttobase64(cookie->cookie, rand(), 5);
971 inttobase64(cookie->cookie+5, rand(), 5);
973 netname = nickserv_conf.network_name;
976 switch (cookie->type) {
978 hi->passwd[0] = 0; /* invalidate password */
979 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
980 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
981 snprintf(subject, sizeof(subject), fmt, netname);
982 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
983 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
986 case PASSWORD_CHANGE:
987 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
988 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
989 snprintf(subject, sizeof(subject), fmt, netname);
990 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
991 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
994 misc = hi->email_addr;
995 hi->email_addr = cookie->data;
997 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
998 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
999 snprintf(subject, sizeof(subject), fmt, netname);
1000 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1001 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1002 sendmail(nickserv, hi, subject, body, 1);
1003 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1004 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1006 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1007 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1008 snprintf(subject, sizeof(subject), fmt, netname);
1009 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1010 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1011 sendmail(nickserv, hi, subject, body, 1);
1014 hi->email_addr = misc;
1017 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1018 snprintf(subject, sizeof(subject), fmt, netname);
1019 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1020 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1021 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1024 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1028 sendmail(nickserv, hi, subject, body, first_time);
1029 nickserv_bake_cookie(cookie);
1033 nickserv_eat_cookie(struct handle_cookie *cookie)
1035 cookie->hi->cookie = NULL;
1036 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1037 nickserv_free_cookie(cookie);
1041 nickserv_free_email_addr(void *data)
1043 handle_info_list_clean(data);
1048 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1050 struct handle_info_list *hil;
1051 /* Remove from old handle_info_list ... */
1052 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1053 handle_info_list_remove(hil, hi);
1054 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1055 hi->email_addr = NULL;
1057 /* Add to the new list.. */
1058 if (new_email_addr) {
1059 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1060 hil = calloc(1, sizeof(*hil));
1061 hil->tag = strdup(new_email_addr);
1062 handle_info_list_init(hil);
1063 dict_insert(nickserv_email_dict, hil->tag, hil);
1065 handle_info_list_append(hil, hi);
1066 hi->email_addr = hil->tag;
1070 static NICKSERV_FUNC(cmd_register)
1072 struct handle_info *hi;
1073 const char *email_addr, *password;
1076 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1077 /* Require the first handle registered to belong to someone +o. */
1078 reply("NSMSG_REQUIRE_OPER");
1082 if (user->handle_info) {
1083 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1087 if (IsStamped(user)) {
1088 /* Unauthenticated users might still have been stamped
1089 previously and could therefore have a hidden host;
1090 do not allow them to register a new account. */
1091 reply("NSMSG_STAMPED_REGISTER");
1095 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1097 if (!is_valid_handle(argv[1])) {
1098 reply("NSMSG_BAD_HANDLE", argv[1]);
1102 if ((argc >= 4) && nickserv_conf.email_enabled) {
1103 struct handle_info_list *hil;
1106 /* Remember email address. */
1107 email_addr = argv[3];
1109 /* Check that the email address looks valid.. */
1110 if (!is_valid_email_addr(email_addr)) {
1111 reply("NSMSG_BAD_EMAIL_ADDR");
1115 /* .. and that we are allowed to send to it. */
1116 if ((str = sendmail_prohibited_address(email_addr))) {
1117 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1121 /* If we do email verify, make sure we don't spam the address. */
1122 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1124 for (nn=0; nn<hil->used; nn++) {
1125 if (hil->list[nn]->cookie) {
1126 reply("NSMSG_EMAIL_UNACTIVATED");
1130 if (hil->used >= nickserv_conf.handles_per_email) {
1131 reply("NSMSG_EMAIL_OVERUSED");
1144 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1146 /* Add any masks they should get. */
1147 if (nickserv_conf.default_hostmask) {
1148 string_list_append(hi->masks, strdup("*@*"));
1150 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1151 if (user->ip.s_addr && user->hostname[strspn(user->hostname, "0123456789.")])
1152 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1155 /* If they're the first to register, give them level 1000. */
1156 if (dict_size(nickserv_handle_dict) == 1) {
1157 hi->opserv_level = 1000;
1158 reply("NSMSG_ROOT_HANDLE", argv[1]);
1161 /* Set their email address. */
1163 nickserv_set_email_addr(hi, email_addr);
1165 /* If they need to do email verification, tell them. */
1167 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1172 static NICKSERV_FUNC(cmd_oregister)
1175 struct userNode *settee;
1176 struct handle_info *hi;
1178 NICKSERV_MIN_PARMS(4);
1180 if (!is_valid_handle(argv[1])) {
1181 reply("NSMSG_BAD_HANDLE", argv[1]);
1185 if (strchr(argv[3], '@')) {
1186 mask = canonicalize_hostmask(strdup(argv[3]));
1188 settee = GetUserH(argv[4]);
1190 reply("MSG_NICK_UNKNOWN", argv[4]);
1197 } else if ((settee = GetUserH(argv[3]))) {
1198 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1200 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1203 if (settee && settee->handle_info) {
1204 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1208 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1212 string_list_append(hi->masks, mask);
1216 static NICKSERV_FUNC(cmd_handleinfo)
1219 unsigned int i, pos=0, herelen;
1220 struct userNode *target, *next_un;
1221 struct handle_info *hi;
1222 const char *nsmsg_none;
1225 if (!(hi = user->handle_info)) {
1226 reply("NSMSG_MUST_AUTH");
1229 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1233 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1234 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1235 #ifdef WITH_PROTOCOL_BAHAMUT
1236 reply("NSMSG_HANDLEINFO_ID", hi->id);
1238 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1241 intervalString(buff, now - hi->lastseen, user->handle_info);
1242 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1244 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1247 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1248 if (HANDLE_FLAGGED(hi, FROZEN))
1249 reply("NSMSG_HANDLEINFO_VACATION");
1251 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1252 struct do_not_register *dnr;
1253 if ((dnr = chanserv_is_dnr(NULL, hi)))
1254 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1255 if (!oper_outranks(user, hi))
1257 } else if (hi != user->handle_info)
1260 if (nickserv_conf.email_enabled)
1261 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1265 switch (hi->cookie->type) {
1266 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1267 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1268 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1269 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1270 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1276 unsigned long flen = 1;
1277 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1279 for (i=0, flen=1; handle_flags[i]; i++)
1280 if (hi->flags & 1 << i)
1281 flags[flen++] = handle_flags[i];
1283 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1285 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1288 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1289 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1290 || (hi->opserv_level > 0)) {
1291 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1294 if (hi->last_quit_host[0])
1295 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1297 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1299 if (nickserv_conf.disable_nicks) {
1300 /* nicks disabled; don't show anything about registered nicks */
1301 } else if (hi->nicks) {
1302 struct nick_info *ni, *next_ni;
1303 for (ni = hi->nicks; ni; ni = next_ni) {
1304 herelen = strlen(ni->nick);
1305 if (pos + herelen + 1 > ArrayLength(buff)) {
1307 goto print_nicks_buff;
1311 memcpy(buff+pos, ni->nick, herelen);
1312 pos += herelen; buff[pos++] = ' ';
1316 reply("NSMSG_HANDLEINFO_NICKS", buff);
1321 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1324 if (hi->masks->used) {
1325 for (i=0; i < hi->masks->used; i++) {
1326 herelen = strlen(hi->masks->list[i]);
1327 if (pos + herelen + 1 > ArrayLength(buff)) {
1329 goto print_mask_buff;
1331 memcpy(buff+pos, hi->masks->list[i], herelen);
1332 pos += herelen; buff[pos++] = ' ';
1333 if (i+1 == hi->masks->used) {
1336 reply("NSMSG_HANDLEINFO_MASKS", buff);
1341 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1345 struct userData *channel, *next;
1348 for (channel = hi->channels; channel; channel = next) {
1349 next = channel->u_next;
1350 name = channel->channel->channel->name;
1351 herelen = strlen(name);
1352 if (pos + herelen + 7 > ArrayLength(buff)) {
1354 goto print_chans_buff;
1356 if (IsUserSuspended(channel))
1358 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1362 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1367 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1370 for (target = hi->users; target; target = next_un) {
1371 herelen = strlen(target->nick);
1372 if (pos + herelen + 1 > ArrayLength(buff)) {
1374 goto print_cnick_buff;
1376 next_un = target->next_authed;
1378 memcpy(buff+pos, target->nick, herelen);
1379 pos += herelen; buff[pos++] = ' ';
1383 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1391 static NICKSERV_FUNC(cmd_userinfo)
1393 struct userNode *target;
1395 NICKSERV_MIN_PARMS(2);
1396 if (!(target = GetUserH(argv[1]))) {
1397 reply("MSG_NICK_UNKNOWN", argv[1]);
1400 if (target->handle_info)
1401 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1403 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1407 static NICKSERV_FUNC(cmd_nickinfo)
1409 struct nick_info *ni;
1411 NICKSERV_MIN_PARMS(2);
1412 if (!(ni = get_nick_info(argv[1]))) {
1413 reply("MSG_NICK_UNKNOWN", argv[1]);
1416 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1420 static NICKSERV_FUNC(cmd_rename_handle)
1422 struct handle_info *hi;
1423 char msgbuf[MAXLEN], *old_handle;
1426 NICKSERV_MIN_PARMS(3);
1427 if (!(hi = get_victim_oper(user, argv[1])))
1429 if (!is_valid_handle(argv[2])) {
1430 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1433 if (get_handle_info(argv[2])) {
1434 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1438 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1439 hi->handle = strdup(argv[2]);
1440 dict_insert(nickserv_handle_dict, hi->handle, hi);
1441 for (nn=0; nn<rf_list_used; nn++)
1442 rf_list[nn](hi, old_handle);
1443 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1444 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1445 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1450 static failpw_func_t *failpw_func_list;
1451 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1454 reg_failpw_func(failpw_func_t func)
1456 if (failpw_func_used == failpw_func_size) {
1457 if (failpw_func_size) {
1458 failpw_func_size <<= 1;
1459 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1461 failpw_func_size = 8;
1462 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1465 failpw_func_list[failpw_func_used++] = func;
1468 static NICKSERV_FUNC(cmd_auth)
1470 int pw_arg, used, maxlogins;
1471 struct handle_info *hi;
1473 struct userNode *other;
1475 if (user->handle_info) {
1476 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1479 if (IsStamped(user)) {
1480 /* Unauthenticated users might still have been stamped
1481 previously and could therefore have a hidden host;
1482 do not allow them to authenticate. */
1483 reply("NSMSG_STAMPED_AUTH");
1487 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1489 } else if (argc == 2) {
1490 if (nickserv_conf.disable_nicks) {
1491 if (!(hi = get_handle_info(user->nick))) {
1492 reply("NSMSG_HANDLE_NOT_FOUND");
1496 /* try to look up their handle from their nick */
1497 struct nick_info *ni;
1498 ni = get_nick_info(user->nick);
1500 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1507 reply("MSG_MISSING_PARAMS", argv[0]);
1508 svccmd_send_help(user, nickserv, cmd);
1512 reply("NSMSG_HANDLE_NOT_FOUND");
1515 /* Responses from here on look up the language used by the handle they asked about. */
1516 passwd = argv[pw_arg];
1517 if (!valid_user_for(user, hi)) {
1518 if (hi->email_addr && nickserv_conf.email_enabled)
1519 send_message_type(4, user, cmd->parent->bot,
1520 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1523 send_message_type(4, user, cmd->parent->bot,
1524 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1526 argv[pw_arg] = "BADMASK";
1529 if (!checkpass(passwd, hi->passwd)) {
1531 send_message_type(4, user, cmd->parent->bot,
1532 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1533 argv[pw_arg] = "BADPASS";
1534 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1535 if (nickserv_conf.autogag_enabled) {
1536 if (!user->auth_policer.params) {
1537 user->auth_policer.last_req = now;
1538 user->auth_policer.params = nickserv_conf.auth_policer_params;
1540 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1542 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1543 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1544 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1546 argv[pw_arg] = "GAGGED";
1551 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1552 send_message_type(4, user, cmd->parent->bot,
1553 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1554 argv[pw_arg] = "SUSPENDED";
1557 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1558 for (used = 0, other = hi->users; other; other = other->next_authed) {
1559 if (++used >= maxlogins) {
1560 send_message_type(4, user, cmd->parent->bot,
1561 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1563 argv[pw_arg] = "MAXLOGINS";
1568 set_user_handle_info(user, hi, 1);
1569 if (nickserv_conf.email_required && !hi->email_addr)
1570 reply("NSMSG_PLEASE_SET_EMAIL");
1571 if (!is_secure_password(hi->handle, passwd, NULL))
1572 reply("NSMSG_WEAK_PASSWORD");
1573 if (hi->passwd[0] != '$')
1574 cryptpass(passwd, hi->passwd);
1575 reply("NSMSG_AUTH_SUCCESS");
1576 argv[pw_arg] = "****";
1580 static allowauth_func_t *allowauth_func_list;
1581 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1584 reg_allowauth_func(allowauth_func_t func)
1586 if (allowauth_func_used == allowauth_func_size) {
1587 if (allowauth_func_size) {
1588 allowauth_func_size <<= 1;
1589 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1591 allowauth_func_size = 8;
1592 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1595 allowauth_func_list[allowauth_func_used++] = func;
1598 static NICKSERV_FUNC(cmd_allowauth)
1600 struct userNode *target;
1601 struct handle_info *hi;
1604 NICKSERV_MIN_PARMS(2);
1605 if (!(target = GetUserH(argv[1]))) {
1606 reply("MSG_NICK_UNKNOWN", argv[1]);
1609 if (target->handle_info) {
1610 reply("NSMSG_USER_PREV_AUTH", target->nick);
1613 if (IsStamped(target)) {
1614 /* Unauthenticated users might still have been stamped
1615 previously and could therefore have a hidden host;
1616 do not allow them to authenticate to an account. */
1617 send_message(target, nickserv, "NSMSG_USER_PREV_STAMP", target->nick);
1622 else if (!(hi = get_handle_info(argv[2]))) {
1623 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1627 if (hi->opserv_level > user->handle_info->opserv_level) {
1628 reply("MSG_USER_OUTRANKED", hi->handle);
1631 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1632 || (hi->opserv_level > 0))
1633 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1634 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1637 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1638 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1639 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1640 if (nickserv_conf.email_enabled)
1641 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1643 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1644 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1646 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1648 for (n=0; n<allowauth_func_used; n++)
1649 allowauth_func_list[n](user, target, hi);
1653 static NICKSERV_FUNC(cmd_authcookie)
1655 struct handle_info *hi;
1657 NICKSERV_MIN_PARMS(2);
1658 if (user->handle_info) {
1659 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1662 if (IsStamped(user)) {
1663 /* Unauthenticated users might still have been stamped
1664 previously and could therefore have a hidden host;
1665 do not allow them to authenticate to an account. */
1666 reply("NSMSG_STAMPED_AUTHCOOKIE");
1669 if (!(hi = get_handle_info(argv[1]))) {
1670 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1673 if (!hi->email_addr) {
1674 reply("MSG_SET_EMAIL_ADDR");
1677 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1681 static NICKSERV_FUNC(cmd_delcookie)
1683 struct handle_info *hi;
1685 hi = user->handle_info;
1687 reply("NSMSG_NO_COOKIE");
1690 switch (hi->cookie->type) {
1693 reply("NSMSG_MUST_TIME_OUT");
1696 nickserv_eat_cookie(hi->cookie);
1697 reply("NSMSG_ATE_COOKIE");
1703 static NICKSERV_FUNC(cmd_resetpass)
1705 struct handle_info *hi;
1706 char crypted[MD5_CRYPT_LENGTH];
1708 NICKSERV_MIN_PARMS(3);
1709 if (user->handle_info) {
1710 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1713 if (IsStamped(user)) {
1714 /* Unauthenticated users might still have been stamped
1715 previously and could therefore have a hidden host;
1716 do not allow them to activate an account. */
1717 reply("NSMSG_STAMPED_RESETPASS");
1720 if (!(hi = get_handle_info(argv[1]))) {
1721 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1724 if (!hi->email_addr) {
1725 reply("MSG_SET_EMAIL_ADDR");
1728 cryptpass(argv[2], crypted);
1730 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1734 static NICKSERV_FUNC(cmd_cookie)
1736 struct handle_info *hi;
1739 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1742 NICKSERV_MIN_PARMS(3);
1743 if (!(hi = get_handle_info(argv[1]))) {
1744 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1750 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1751 reply("NSMSG_HANDLE_SUSPENDED");
1756 reply("NSMSG_NO_COOKIE");
1760 /* Check validity of operation before comparing cookie to
1761 * prohibit guessing by authed users. */
1762 if (user->handle_info
1763 && (hi->cookie->type != EMAIL_CHANGE)
1764 && (hi->cookie->type != PASSWORD_CHANGE)) {
1765 reply("NSMSG_CANNOT_COOKIE");
1769 if (strcmp(cookie, hi->cookie->cookie)) {
1770 reply("NSMSG_BAD_COOKIE");
1774 switch (hi->cookie->type) {
1776 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1777 set_user_handle_info(user, hi, 1);
1778 reply("NSMSG_HANDLE_ACTIVATED");
1780 case PASSWORD_CHANGE:
1781 set_user_handle_info(user, hi, 1);
1782 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1783 reply("NSMSG_PASSWORD_CHANGED");
1786 nickserv_set_email_addr(hi, hi->cookie->data);
1787 reply("NSMSG_EMAIL_CHANGED");
1790 set_user_handle_info(user, hi, 1);
1791 reply("NSMSG_AUTH_SUCCESS");
1794 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1795 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1799 nickserv_eat_cookie(hi->cookie);
1804 static NICKSERV_FUNC(cmd_oregnick) {
1806 struct handle_info *target;
1807 struct nick_info *ni;
1809 NICKSERV_MIN_PARMS(3);
1810 if (!(target = modcmd_get_handle_info(user, argv[1])))
1813 if (!is_registerable_nick(nick)) {
1814 reply("NSMSG_BAD_NICK", nick);
1817 ni = dict_find(nickserv_nick_dict, nick, NULL);
1819 reply("NSMSG_NICK_EXISTS", nick);
1822 register_nick(nick, target);
1823 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1827 static NICKSERV_FUNC(cmd_regnick) {
1829 struct nick_info *ni;
1831 if (!is_registerable_nick(user->nick)) {
1832 reply("NSMSG_BAD_NICK", user->nick);
1835 /* count their nicks, see if it's too many */
1836 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1837 if (n >= nickserv_conf.nicks_per_handle) {
1838 reply("NSMSG_TOO_MANY_NICKS");
1841 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1843 reply("NSMSG_NICK_EXISTS", user->nick);
1846 register_nick(user->nick, user->handle_info);
1847 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1851 static NICKSERV_FUNC(cmd_pass)
1853 struct handle_info *hi;
1854 const char *old_pass, *new_pass;
1856 NICKSERV_MIN_PARMS(3);
1857 hi = user->handle_info;
1861 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1862 if (!checkpass(old_pass, hi->passwd)) {
1863 argv[1] = "BADPASS";
1864 reply("NSMSG_PASSWORD_INVALID");
1867 cryptpass(new_pass, hi->passwd);
1869 reply("NSMSG_PASS_SUCCESS");
1874 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1877 char *new_mask = canonicalize_hostmask(strdup(mask));
1878 for (i=0; i<hi->masks->used; i++) {
1879 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1880 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1885 string_list_append(hi->masks, new_mask);
1886 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
1890 static NICKSERV_FUNC(cmd_addmask)
1893 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1894 int res = nickserv_addmask(user, user->handle_info, mask);
1898 if (!is_gline(argv[1])) {
1899 reply("NSMSG_MASK_INVALID", argv[1]);
1902 return nickserv_addmask(user, user->handle_info, argv[1]);
1906 static NICKSERV_FUNC(cmd_oaddmask)
1908 struct handle_info *hi;
1910 NICKSERV_MIN_PARMS(3);
1911 if (!(hi = get_victim_oper(user, argv[1])))
1913 return nickserv_addmask(user, hi, argv[2]);
1917 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
1920 for (i=0; i<hi->masks->used; i++) {
1921 if (!strcmp(del_mask, hi->masks->list[i])) {
1922 char *old_mask = hi->masks->list[i];
1923 if (hi->masks->used == 1) {
1924 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
1927 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
1928 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
1933 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
1937 static NICKSERV_FUNC(cmd_delmask)
1939 NICKSERV_MIN_PARMS(2);
1940 return nickserv_delmask(user, user->handle_info, argv[1]);
1943 static NICKSERV_FUNC(cmd_odelmask)
1945 struct handle_info *hi;
1946 NICKSERV_MIN_PARMS(3);
1947 if (!(hi = get_victim_oper(user, argv[1])))
1949 return nickserv_delmask(user, hi, argv[2]);
1953 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
1954 unsigned int nn, add = 1, pos;
1955 unsigned long added, removed, flag;
1957 for (added=removed=nn=0; str[nn]; nn++) {
1959 case '+': add = 1; break;
1960 case '-': add = 0; break;
1962 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
1963 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
1966 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
1967 /* cheesy avoidance of looking up the flag name.. */
1968 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
1971 flag = 1 << (pos - 1);
1973 added |= flag, removed &= ~flag;
1975 removed |= flag, added &= ~flag;
1980 *premoved = removed;
1985 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
1987 unsigned long before, after, added, removed;
1988 struct userNode *uNode;
1990 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
1991 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
1993 hi->flags = (hi->flags | added) & ~removed;
1994 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
1996 /* Strip helping flag if they're only a support helper and not
1997 * currently in #support. */
1998 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
1999 struct channelList *schannels;
2001 schannels = chanserv_support_channels();
2002 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2003 for (ii = 0; ii < schannels->used; ++ii)
2004 if (GetUserMode(schannels->list[ii], uNode))
2006 if (ii < schannels->used)
2010 HANDLE_CLEAR_FLAG(hi, HELPING);
2013 if (after && !before) {
2014 /* Add user to current helper list. */
2015 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2016 userList_append(&curr_helpers, uNode);
2017 } else if (!after && before) {
2018 /* Remove user from current helper list. */
2019 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2020 userList_remove(&curr_helpers, uNode);
2027 set_list(struct userNode *user, struct handle_info *hi, int override)
2031 char *set_display[] = {
2032 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2033 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2036 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2038 /* Do this so options are presented in a consistent order. */
2039 for (i = 0; i < ArrayLength(set_display); ++i)
2040 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2041 opt(user, hi, override, 0, NULL);
2044 static NICKSERV_FUNC(cmd_set)
2046 struct handle_info *hi;
2049 hi = user->handle_info;
2051 set_list(user, hi, 0);
2054 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2055 reply("NSMSG_INVALID_OPTION", argv[1]);
2058 return opt(user, hi, 0, argc-1, argv+1);
2061 static NICKSERV_FUNC(cmd_oset)
2063 struct handle_info *hi;
2066 NICKSERV_MIN_PARMS(2);
2068 if (!(hi = get_victim_oper(user, argv[1])))
2072 set_list(user, hi, 0);
2076 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2077 reply("NSMSG_INVALID_OPTION", argv[2]);
2081 return opt(user, hi, 1, argc-2, argv+2);
2084 static OPTION_FUNC(opt_info)
2088 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2090 hi->infoline = NULL;
2092 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2096 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2097 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2101 static OPTION_FUNC(opt_width)
2104 hi->screen_width = strtoul(argv[1], NULL, 0);
2106 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2107 hi->screen_width = MIN_LINE_SIZE;
2108 else if (hi->screen_width > MAX_LINE_SIZE)
2109 hi->screen_width = MAX_LINE_SIZE;
2111 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2115 static OPTION_FUNC(opt_tablewidth)
2118 hi->table_width = strtoul(argv[1], NULL, 0);
2120 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2121 hi->table_width = MIN_LINE_SIZE;
2122 else if (hi->screen_width > MAX_LINE_SIZE)
2123 hi->table_width = MAX_LINE_SIZE;
2125 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2129 static OPTION_FUNC(opt_color)
2132 if (enabled_string(argv[1]))
2133 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2134 else if (disabled_string(argv[1]))
2135 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2137 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2142 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2146 static OPTION_FUNC(opt_privmsg)
2149 if (enabled_string(argv[1]))
2150 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2151 else if (disabled_string(argv[1]))
2152 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2154 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2159 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2163 static OPTION_FUNC(opt_style)
2168 if (!irccasecmp(argv[1], "Zoot"))
2169 hi->userlist_style = HI_STYLE_ZOOT;
2170 else if (!irccasecmp(argv[1], "def"))
2171 hi->userlist_style = HI_STYLE_DEF;
2174 switch (hi->userlist_style) {
2183 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2187 static OPTION_FUNC(opt_announcements)
2192 if (enabled_string(argv[1]))
2193 hi->announcements = 'y';
2194 else if (disabled_string(argv[1]))
2195 hi->announcements = 'n';
2196 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2197 hi->announcements = '?';
2199 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2204 switch (hi->announcements) {
2205 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2206 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2207 case '?': choice = "default"; break;
2208 default: choice = "unknown"; break;
2210 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2214 static OPTION_FUNC(opt_password)
2217 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2222 cryptpass(argv[1], hi->passwd);
2224 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2228 static OPTION_FUNC(opt_flags)
2231 unsigned int ii, flen;
2234 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2239 nickserv_apply_flags(user, hi, argv[1]);
2241 for (ii = flen = 0; handle_flags[ii]; ii++)
2242 if (hi->flags & (1 << ii))
2243 flags[flen++] = handle_flags[ii];
2246 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2248 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2252 static OPTION_FUNC(opt_email)
2256 if (!is_valid_email_addr(argv[1])) {
2257 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2260 if ((str = sendmail_prohibited_address(argv[1]))) {
2261 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2264 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2265 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2267 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2269 nickserv_set_email_addr(hi, argv[1]);
2271 nickserv_eat_cookie(hi->cookie);
2272 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2275 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2279 static OPTION_FUNC(opt_maxlogins)
2281 unsigned char maxlogins;
2283 maxlogins = strtoul(argv[1], NULL, 0);
2284 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2285 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2288 hi->maxlogins = maxlogins;
2290 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2291 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2295 static OPTION_FUNC(opt_language)
2297 struct language *lang;
2299 lang = language_find(argv[1]);
2300 if (irccasecmp(lang->name, argv[1]))
2301 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2302 hi->language = lang;
2304 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2309 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2310 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2312 if ((user->handle_info->opserv_level < target->opserv_level)
2313 || ((user->handle_info->opserv_level == target->opserv_level)
2314 && (user->handle_info->opserv_level < 1000))) {
2315 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2318 if ((user->handle_info->opserv_level < new_level)
2319 || ((user->handle_info->opserv_level == new_level)
2320 && (user->handle_info->opserv_level < 1000))) {
2321 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2324 if (user->handle_info == target) {
2325 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2328 if (target->opserv_level == new_level)
2330 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2331 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2332 target->opserv_level = new_level;
2336 static OPTION_FUNC(opt_level)
2341 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2345 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2346 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2350 static OPTION_FUNC(opt_epithet)
2353 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2357 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2358 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2361 if ((epithet[0] == '*') && !epithet[1])
2364 hi->epithet = strdup(epithet);
2368 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2370 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2374 static NICKSERV_FUNC(cmd_reclaim)
2376 struct handle_info *hi;
2377 struct nick_info *ni;
2378 struct userNode *victim;
2380 NICKSERV_MIN_PARMS(2);
2381 hi = user->handle_info;
2382 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2384 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2387 if (ni->owner != user->handle_info) {
2388 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2391 victim = GetUserH(ni->nick);
2393 reply("MSG_NICK_UNKNOWN", ni->nick);
2396 if (victim == user) {
2397 reply("NSMSG_NICK_USER_YOU");
2400 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2401 switch (nickserv_conf.reclaim_action) {
2402 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2403 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2404 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2405 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2410 static NICKSERV_FUNC(cmd_unregnick)
2413 struct handle_info *hi;
2414 struct nick_info *ni;
2416 hi = user->handle_info;
2417 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2418 ni = dict_find(nickserv_nick_dict, nick, NULL);
2420 reply("NSMSG_UNKNOWN_NICK", nick);
2423 if (hi != ni->owner) {
2424 reply("NSMSG_NOT_YOUR_NICK", nick);
2427 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2432 static NICKSERV_FUNC(cmd_ounregnick)
2434 struct nick_info *ni;
2436 NICKSERV_MIN_PARMS(2);
2437 if (!(ni = get_nick_info(argv[1]))) {
2438 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2441 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2442 reply("MSG_USER_OUTRANKED", ni->nick);
2445 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2450 static NICKSERV_FUNC(cmd_unregister)
2452 struct handle_info *hi;
2455 NICKSERV_MIN_PARMS(2);
2456 hi = user->handle_info;
2459 if (checkpass(passwd, hi->passwd)) {
2460 nickserv_unregister_handle(hi, user);
2463 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2464 reply("NSMSG_PASSWORD_INVALID");
2469 static NICKSERV_FUNC(cmd_ounregister)
2471 struct handle_info *hi;
2473 NICKSERV_MIN_PARMS(2);
2474 if (!(hi = get_victim_oper(user, argv[1])))
2476 nickserv_unregister_handle(hi, user);
2480 static NICKSERV_FUNC(cmd_status)
2482 if (nickserv_conf.disable_nicks) {
2483 reply("NSMSG_GLOBAL_STATS_NONICK",
2484 dict_size(nickserv_handle_dict));
2486 if (user->handle_info) {
2488 struct nick_info *ni;
2489 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2490 reply("NSMSG_HANDLE_STATS", cnt);
2492 reply("NSMSG_HANDLE_NONE");
2494 reply("NSMSG_GLOBAL_STATS",
2495 dict_size(nickserv_handle_dict),
2496 dict_size(nickserv_nick_dict));
2501 static NICKSERV_FUNC(cmd_ghost)
2503 struct userNode *target;
2504 char reason[MAXLEN];
2506 NICKSERV_MIN_PARMS(2);
2507 if (!(target = GetUserH(argv[1]))) {
2508 reply("MSG_NICK_UNKNOWN", argv[1]);
2511 if (target == user) {
2512 reply("NSMSG_CANNOT_GHOST_SELF");
2515 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2516 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2519 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2520 DelUser(target, nickserv, 1, reason);
2521 reply("NSMSG_GHOST_KILLED", argv[1]);
2525 static NICKSERV_FUNC(cmd_vacation)
2527 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2528 reply("NSMSG_ON_VACATION");
2533 nickserv_saxdb_write(struct saxdb_context *ctx) {
2535 struct handle_info *hi;
2538 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2540 #ifdef WITH_PROTOCOL_BAHAMUT
2543 saxdb_start_record(ctx, iter_key(it), 0);
2544 if (hi->announcements != '?') {
2545 flags[0] = hi->announcements;
2547 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2550 struct handle_cookie *cookie = hi->cookie;
2553 switch (cookie->type) {
2554 case ACTIVATION: type = KEY_ACTIVATION; break;
2555 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2556 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2557 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2558 default: type = NULL; break;
2561 saxdb_start_record(ctx, KEY_COOKIE, 0);
2562 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2563 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2565 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2566 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2567 saxdb_end_record(ctx);
2571 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2573 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2577 for (ii=flen=0; handle_flags[ii]; ++ii)
2578 if (hi->flags & (1 << ii))
2579 flags[flen++] = handle_flags[ii];
2581 saxdb_write_string(ctx, KEY_FLAGS, flags);
2583 #ifdef WITH_PROTOCOL_BAHAMUT
2584 saxdb_write_int(ctx, KEY_ID, hi->id);
2587 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2588 if (hi->last_quit_host[0])
2589 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2590 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2591 if (hi->masks->used)
2592 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2594 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2596 struct string_list *slist;
2597 struct nick_info *ni;
2599 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2600 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2601 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2605 if (hi->opserv_level)
2606 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2607 if (hi->language != lang_C)
2608 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2609 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2610 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2611 if (hi->screen_width)
2612 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2613 if (hi->table_width)
2614 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2615 flags[0] = hi->userlist_style;
2617 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2618 saxdb_end_record(ctx);
2623 static handle_merge_func_t *handle_merge_func_list;
2624 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2627 reg_handle_merge_func(handle_merge_func_t func)
2629 if (handle_merge_func_used == handle_merge_func_size) {
2630 if (handle_merge_func_size) {
2631 handle_merge_func_size <<= 1;
2632 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2634 handle_merge_func_size = 8;
2635 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2638 handle_merge_func_list[handle_merge_func_used++] = func;
2641 static NICKSERV_FUNC(cmd_merge)
2643 struct handle_info *hi_from, *hi_to;
2644 struct userNode *last_user;
2645 struct userData *cList, *cListNext;
2646 unsigned int ii, jj, n;
2647 char buffer[MAXLEN];
2649 NICKSERV_MIN_PARMS(3);
2651 if (!(hi_from = get_victim_oper(user, argv[1])))
2653 if (!(hi_to = get_victim_oper(user, argv[2])))
2655 if (hi_to == hi_from) {
2656 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2660 for (n=0; n<handle_merge_func_used; n++)
2661 handle_merge_func_list[n](user, hi_to, hi_from);
2663 /* Append "from" handle's nicks to "to" handle's nick list. */
2665 struct nick_info *last_ni;
2666 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2667 last_ni->next = hi_from->nicks;
2669 while (hi_from->nicks) {
2670 hi_from->nicks->owner = hi_to;
2671 hi_from->nicks = hi_from->nicks->next;
2674 /* Merge the hostmasks. */
2675 for (ii=0; ii<hi_from->masks->used; ii++) {
2676 char *mask = hi_from->masks->list[ii];
2677 for (jj=0; jj<hi_to->masks->used; jj++)
2678 if (match_ircglobs(hi_to->masks->list[jj], mask))
2680 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2681 string_list_append(hi_to->masks, strdup(mask));
2684 /* Merge the lists of authed users. */
2686 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2687 last_user->next_authed = hi_from->users;
2689 hi_to->users = hi_from->users;
2691 /* Repoint the old "from" handle's users. */
2692 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2693 last_user->handle_info = hi_to;
2695 hi_from->users = NULL;
2697 /* Merge channel userlists. */
2698 for (cList=hi_from->channels; cList; cList=cListNext) {
2699 struct userData *cList2;
2700 cListNext = cList->u_next;
2701 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2702 if (cList->channel == cList2->channel)
2704 if (cList2 && (cList2->access >= cList->access)) {
2705 log_module(NS_LOG, LOG_INFO, "Merge: %s had only %d access in %s (versus %d for %s)", hi_from->handle, cList->access, cList->channel->channel->name, cList2->access, hi_to->handle);
2706 /* keep cList2 in hi_to; remove cList from hi_from */
2707 del_channel_user(cList, 1);
2710 log_module(NS_LOG, LOG_INFO, "Merge: %s had only %d access in %s (versus %d for %s)", hi_to->handle, cList2->access, cList->channel->channel->name, cList->access, hi_from->handle);
2711 /* remove the lower-ranking cList2 from hi_to */
2712 del_channel_user(cList2, 1);
2714 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
2716 /* cList needs to be moved from hi_from to hi_to */
2717 cList->handle = hi_to;
2718 /* Remove from linked list for hi_from */
2719 assert(!cList->u_prev);
2720 hi_from->channels = cList->u_next;
2722 cList->u_next->u_prev = cList->u_prev;
2723 /* Add to linked list for hi_to */
2724 cList->u_prev = NULL;
2725 cList->u_next = hi_to->channels;
2726 if (hi_to->channels)
2727 hi_to->channels->u_prev = cList;
2728 hi_to->channels = cList;
2732 /* Do they get an OpServ level promotion? */
2733 if (hi_from->opserv_level > hi_to->opserv_level)
2734 hi_to->opserv_level = hi_from->opserv_level;
2736 /* What about last seen time? */
2737 if (hi_from->lastseen > hi_to->lastseen)
2738 hi_to->lastseen = hi_from->lastseen;
2740 /* Notify of success. */
2741 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2742 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2743 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2745 /* Unregister the "from" handle. */
2746 nickserv_unregister_handle(hi_from, NULL);
2751 struct nickserv_discrim {
2752 unsigned int limit, min_level, max_level;
2753 unsigned long flags_on, flags_off;
2754 time_t min_registered, max_registered;
2756 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2757 const char *nickmask;
2758 const char *hostmask;
2759 const char *handlemask;
2760 const char *emailmask;
2763 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2765 struct discrim_apply_info {
2766 struct nickserv_discrim *discrim;
2767 discrim_search_func func;
2768 struct userNode *source;
2769 unsigned int matched;
2772 static struct nickserv_discrim *
2773 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2776 struct nickserv_discrim *discrim;
2778 discrim = malloc(sizeof(*discrim));
2779 memset(discrim, 0, sizeof(*discrim));
2780 discrim->min_level = 0;
2781 discrim->max_level = ~0;
2782 discrim->limit = 50;
2783 discrim->min_registered = 0;
2784 discrim->max_registered = INT_MAX;
2785 discrim->lastseen = now;
2787 for (i=0; i<argc; i++) {
2788 if (i == argc - 1) {
2789 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2792 if (!irccasecmp(argv[i], "limit")) {
2793 discrim->limit = strtoul(argv[++i], NULL, 0);
2794 } else if (!irccasecmp(argv[i], "flags")) {
2795 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2796 } else if (!irccasecmp(argv[i], "registered")) {
2797 const char *cmp = argv[++i];
2798 if (cmp[0] == '<') {
2799 if (cmp[1] == '=') {
2800 discrim->min_registered = now - ParseInterval(cmp+2);
2802 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2804 } else if (cmp[0] == '=') {
2805 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2806 } else if (cmp[0] == '>') {
2807 if (cmp[1] == '=') {
2808 discrim->max_registered = now - ParseInterval(cmp+2);
2810 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2813 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2815 } else if (!irccasecmp(argv[i], "seen")) {
2816 discrim->lastseen = now - ParseInterval(argv[++i]);
2817 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2818 discrim->nickmask = argv[++i];
2819 } else if (!irccasecmp(argv[i], "hostmask")) {
2821 if (!irccasecmp(argv[i], "exact")) {
2822 if (i == argc - 1) {
2823 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2826 discrim->hostmask_type = EXACT;
2827 } else if (!irccasecmp(argv[i], "subset")) {
2828 if (i == argc - 1) {
2829 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2832 discrim->hostmask_type = SUBSET;
2833 } else if (!irccasecmp(argv[i], "superset")) {
2834 if (i == argc - 1) {
2835 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2838 discrim->hostmask_type = SUPERSET;
2839 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2840 if (i == argc - 1) {
2841 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2844 discrim->hostmask_type = LASTQUIT;
2847 discrim->hostmask_type = SUPERSET;
2849 discrim->hostmask = argv[++i];
2850 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2851 if (!irccasecmp(argv[++i], "*")) {
2852 discrim->handlemask = 0;
2854 discrim->handlemask = argv[i];
2856 } else if (!irccasecmp(argv[i], "email")) {
2857 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2858 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2860 } else if (!irccasecmp(argv[++i], "*")) {
2861 discrim->emailmask = 0;
2863 discrim->emailmask = argv[i];
2865 } else if (!irccasecmp(argv[i], "access")) {
2866 const char *cmp = argv[++i];
2867 if (cmp[0] == '<') {
2868 if (discrim->min_level == 0) discrim->min_level = 1;
2869 if (cmp[1] == '=') {
2870 discrim->max_level = strtoul(cmp+2, NULL, 0);
2872 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
2874 } else if (cmp[0] == '=') {
2875 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
2876 } else if (cmp[0] == '>') {
2877 if (cmp[1] == '=') {
2878 discrim->min_level = strtoul(cmp+2, NULL, 0);
2880 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
2883 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2886 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
2897 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
2899 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
2900 || (discrim->flags_off & hi->flags)
2901 || (discrim->min_registered > hi->registered)
2902 || (discrim->max_registered < hi->registered)
2903 || (discrim->lastseen < (hi->users?now:hi->lastseen))
2904 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
2905 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
2906 || (discrim->min_level > hi->opserv_level)
2907 || (discrim->max_level < hi->opserv_level)) {
2910 if (discrim->hostmask) {
2912 for (i=0; i<hi->masks->used; i++) {
2913 const char *mask = hi->masks->list[i];
2914 if ((discrim->hostmask_type == SUBSET)
2915 && (match_ircglobs(discrim->hostmask, mask))) break;
2916 else if ((discrim->hostmask_type == EXACT)
2917 && !irccasecmp(discrim->hostmask, mask)) break;
2918 else if ((discrim->hostmask_type == SUPERSET)
2919 && (match_ircglobs(mask, discrim->hostmask))) break;
2920 else if ((discrim->hostmask_type == LASTQUIT)
2921 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
2923 if (i==hi->masks->used) return 0;
2925 if (discrim->nickmask) {
2926 struct nick_info *nick = hi->nicks;
2928 if (match_ircglob(nick->nick, discrim->nickmask)) break;
2931 if (!nick) return 0;
2937 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
2939 dict_iterator_t it, next;
2940 unsigned int matched;
2942 for (it = dict_first(nickserv_handle_dict), matched = 0;
2943 it && (matched < discrim->limit);
2945 next = iter_next(it);
2946 if (nickserv_discrim_match(discrim, iter_data(it))) {
2947 dsf(source, iter_data(it));
2955 search_print_func(struct userNode *source, struct handle_info *match)
2957 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
2961 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
2966 search_unregister_func (struct userNode *source, struct handle_info *match)
2968 if (oper_has_access(source, nickserv, match->opserv_level, 0))
2969 nickserv_unregister_handle(match, source);
2973 nickserv_sort_accounts_by_access(const void *a, const void *b)
2975 const struct handle_info *hi_a = *(const struct handle_info**)a;
2976 const struct handle_info *hi_b = *(const struct handle_info**)b;
2977 if (hi_a->opserv_level != hi_b->opserv_level)
2978 return hi_b->opserv_level - hi_a->opserv_level;
2979 return irccasecmp(hi_a->handle, hi_b->handle);
2983 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
2985 struct handle_info_list hil;
2986 struct helpfile_table tbl;
2991 memset(&hil, 0, sizeof(hil));
2992 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2993 struct handle_info *hi = iter_data(it);
2994 if (hi->opserv_level)
2995 handle_info_list_append(&hil, hi);
2997 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
2998 tbl.length = hil.used + 1;
3000 tbl.flags = TABLE_NO_FREE;
3001 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3002 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3005 for (ii = 0; ii < hil.used; ) {
3006 ary = malloc(tbl.width * sizeof(ary[0]));
3007 ary[0] = hil.list[ii]->handle;
3008 ary[1] = strtab(hil.list[ii]->opserv_level);
3009 tbl.contents[++ii] = ary;
3011 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3012 reply("MSG_MATCH_COUNT", hil.used);
3013 for (ii = 0; ii < hil.used; )
3014 free(tbl.contents[++ii]);
3019 static NICKSERV_FUNC(cmd_search)
3021 struct nickserv_discrim *discrim;
3022 discrim_search_func action;
3023 struct svccmd *subcmd;
3024 unsigned int matches;
3027 NICKSERV_MIN_PARMS(3);
3028 sprintf(buf, "search %s", argv[1]);
3029 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3030 if (!irccasecmp(argv[1], "print"))
3031 action = search_print_func;
3032 else if (!irccasecmp(argv[1], "count"))
3033 action = search_count_func;
3034 else if (!irccasecmp(argv[1], "unregister"))
3035 action = search_unregister_func;
3037 reply("NSMSG_INVALID_ACTION", argv[1]);
3041 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3044 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3048 if (action == search_print_func)
3049 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3050 else if (action == search_count_func)
3051 discrim->limit = INT_MAX;
3053 matches = nickserv_discrim_search(discrim, action, user);
3056 reply("MSG_MATCH_COUNT", matches);
3058 reply("MSG_NO_MATCHES");
3064 static MODCMD_FUNC(cmd_checkpass)
3066 struct handle_info *hi;
3068 NICKSERV_MIN_PARMS(3);
3069 if (!(hi = get_handle_info(argv[1]))) {
3070 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3073 if (checkpass(argv[2], hi->passwd))
3074 reply("CHECKPASS_YES");
3076 reply("CHECKPASS_NO");
3082 nickserv_db_read_handle(const char *handle, dict_t obj)
3085 struct string_list *masks, *slist;
3086 struct handle_info *hi;
3087 struct userNode *authed_users;
3088 unsigned long int id;
3092 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3093 id = str ? strtoul(str, NULL, 0) : 0;
3094 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3096 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3099 if ((hi = get_handle_info(handle))) {
3100 authed_users = hi->users;
3102 dict_remove(nickserv_handle_dict, hi->handle);
3104 authed_users = NULL;
3106 hi = register_handle(handle, str, id);
3108 hi->users = authed_users;
3109 while (authed_users) {
3110 authed_users->handle_info = hi;
3111 authed_users = authed_users->next_authed;
3114 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3115 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3116 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3117 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3118 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3119 hi->language = language_find(str ? str : "C");
3120 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3121 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3122 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3124 hi->infoline = strdup(str);
3125 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3126 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3127 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3128 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3129 /* We want to read the nicks even if disable_nicks is set. This is so
3130 * that we don't lose the nick data entirely. */
3131 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3133 for (ii=0; ii<slist->used; ii++)
3134 register_nick(slist->list[ii], hi);
3136 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3138 for (ii=0; str[ii]; ii++)
3139 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3141 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3142 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3143 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3144 hi->announcements = str ? str[0] : '?';
3145 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3146 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3147 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3148 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3149 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3151 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3153 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3154 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3156 nickserv_set_email_addr(hi, str);
3157 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3159 hi->epithet = strdup(str);
3160 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3162 const char *data, *type, *expires, *cookie_str;
3163 struct handle_cookie *cookie;
3165 cookie = calloc(1, sizeof(*cookie));
3166 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3167 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3168 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3169 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3170 if (!type || !expires || !cookie_str) {
3171 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3174 if (!irccasecmp(type, KEY_ACTIVATION))
3175 cookie->type = ACTIVATION;
3176 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3177 cookie->type = PASSWORD_CHANGE;
3178 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3179 cookie->type = EMAIL_CHANGE;
3180 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3181 cookie->type = ALLOWAUTH;
3183 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3186 cookie->expires = strtoul(expires, NULL, 0);
3187 if (cookie->expires < now)
3190 cookie->data = strdup(data);
3191 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3195 nickserv_bake_cookie(cookie);
3197 nickserv_free_cookie(cookie);
3202 nickserv_saxdb_read(dict_t db) {
3204 struct record_data *rd;
3206 for (it=dict_first(db); it; it=iter_next(it)) {
3208 nickserv_db_read_handle(iter_key(it), rd->d.object);
3213 static NICKSERV_FUNC(cmd_mergedb)
3215 struct timeval start, stop;
3218 NICKSERV_MIN_PARMS(2);
3219 gettimeofday(&start, NULL);
3220 if (!(db = parse_database(argv[1]))) {
3221 reply("NSMSG_DB_UNREADABLE", argv[1]);
3224 nickserv_saxdb_read(db);
3226 gettimeofday(&stop, NULL);
3227 stop.tv_sec -= start.tv_sec;
3228 stop.tv_usec -= start.tv_usec;
3229 if (stop.tv_usec < 0) {
3231 stop.tv_usec += 1000000;
3233 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3238 expire_handles(UNUSED_ARG(void *data))
3240 dict_iterator_t it, next;
3242 struct handle_info *hi;
3244 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3245 next = iter_next(it);
3247 if ((hi->opserv_level > 0)
3249 || HANDLE_FLAGGED(hi, FROZEN)
3250 || HANDLE_FLAGGED(hi, NODELETE)) {
3253 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3254 if ((now - hi->lastseen) > expiry) {
3255 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3256 nickserv_unregister_handle(hi, NULL);
3260 if (nickserv_conf.handle_expire_frequency)
3261 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3265 nickserv_load_dict(const char *fname)
3269 if (!(file = fopen(fname, "r"))) {
3270 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3273 while (!feof(file)) {
3274 fgets(line, sizeof(line), file);
3277 if (line[strlen(line)-1] == '\n')
3278 line[strlen(line)-1] = 0;
3279 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3282 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3285 static enum reclaim_action
3286 reclaim_action_from_string(const char *str) {
3288 return RECLAIM_NONE;
3289 else if (!irccasecmp(str, "warn"))
3290 return RECLAIM_WARN;
3291 else if (!irccasecmp(str, "svsnick"))
3292 return RECLAIM_SVSNICK;
3293 else if (!irccasecmp(str, "kill"))
3294 return RECLAIM_KILL;
3296 return RECLAIM_NONE;
3300 nickserv_conf_read(void)
3302 dict_t conf_node, child;
3306 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3307 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3310 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3312 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3313 if (nickserv_conf.valid_handle_regex_set) regfree(&nickserv_conf.valid_handle_regex);
3315 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3316 nickserv_conf.valid_handle_regex_set = !err;
3317 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3319 nickserv_conf.valid_handle_regex_set = 0;
3321 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3322 if (nickserv_conf.valid_nick_regex_set) regfree(&nickserv_conf.valid_nick_regex);
3324 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3325 nickserv_conf.valid_nick_regex_set = !err;
3326 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3328 nickserv_conf.valid_nick_regex_set = 0;
3330 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3332 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3333 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3334 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3335 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3336 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3337 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3338 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3339 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3340 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3341 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3342 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3343 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3344 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3345 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3346 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3347 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3348 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3349 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3350 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3351 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3352 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3354 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3355 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3356 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3358 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3359 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3360 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3362 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3363 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3364 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3365 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3366 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3367 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3368 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3369 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3370 if (!nickserv_conf.disable_nicks) {
3371 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3372 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3373 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3374 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3375 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3376 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3377 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3378 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3380 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3381 for (it=dict_first(child); it; it=iter_next(it)) {
3382 const char *key = iter_key(it), *value;
3386 if (!strncasecmp(key, "uc_", 3))
3387 flag = toupper(key[3]);
3388 else if (!strncasecmp(key, "lc_", 3))
3389 flag = tolower(key[3]);
3393 if ((pos = handle_inverse_flags[flag])) {
3394 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3395 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3398 if (nickserv_conf.weak_password_dict)
3399 dict_delete(nickserv_conf.weak_password_dict);
3400 nickserv_conf.weak_password_dict = dict_new();
3401 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3402 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3403 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3404 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3406 nickserv_load_dict(str);
3407 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3408 if (nickserv && str)
3409 NickChange(nickserv, str, 0);
3410 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3411 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3412 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3413 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3414 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3415 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3416 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3417 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3418 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3419 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3420 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3421 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3422 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3423 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3424 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3425 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3426 str = conf_get_data("server/network", RECDB_QSTRING);
3427 nickserv_conf.network_name = str ? str : "some IRC network";
3428 if (!nickserv_conf.auth_policer_params) {
3429 nickserv_conf.auth_policer_params = policer_params_new();
3430 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3431 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3433 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3434 for (it=dict_first(child); it; it=iter_next(it))
3435 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3439 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3441 char newnick[NICKLEN+1];
3450 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3452 case RECLAIM_SVSNICK:
3454 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3455 } while (GetUserH(newnick));
3456 irc_svsnick(nickserv, user, newnick);
3459 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3460 irc_kill(nickserv, user, msg);
3466 nickserv_reclaim_p(void *data) {
3467 struct userNode *user = data;
3468 struct nick_info *ni = get_nick_info(user->nick);
3470 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3474 check_user_nick(struct userNode *user) {
3475 struct nick_info *ni;
3476 user->modes &= ~FLAGS_REGNICK;
3477 if (!(ni = get_nick_info(user->nick)))
3479 if (user->handle_info == ni->owner) {
3480 user->modes |= FLAGS_REGNICK;
3484 if (nickserv_conf.warn_nick_owned)
3485 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3486 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3488 if (nickserv_conf.auto_reclaim_delay)
3489 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3491 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3496 handle_new_user(struct userNode *user)
3498 return check_user_nick(user);
3502 handle_account(struct userNode *user, const char *stamp)
3504 struct handle_info *hi;
3506 #ifdef WITH_PROTOCOL_P10
3507 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3509 hi = dict_find(nickserv_id_dict, stamp, NULL);
3513 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3516 set_user_handle_info(user, hi, 0);
3518 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3523 handle_nick_change(struct userNode *user, const char *old_nick)
3525 struct handle_info *hi;
3527 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3528 dict_remove(nickserv_allow_auth_dict, old_nick);
3529 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3531 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3532 check_user_nick(user);
3536 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3538 dict_remove(nickserv_allow_auth_dict, user->nick);
3539 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3540 set_user_handle_info(user, NULL, 0);
3543 static struct modcmd *
3544 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3546 if (min_level > 0) {
3548 sprintf(buf, "%u", min_level);
3549 if (must_be_qualified) {
3550 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3552 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3554 } else if (min_level == 0) {
3555 if (must_be_qualified) {
3556 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3558 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3561 if (must_be_qualified) {
3562 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3564 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3570 nickserv_db_cleanup(void)
3572 unreg_del_user_func(nickserv_remove_user);
3573 userList_clean(&curr_helpers);
3574 policer_params_delete(nickserv_conf.auth_policer_params);
3575 dict_delete(nickserv_handle_dict);
3576 dict_delete(nickserv_nick_dict);
3577 dict_delete(nickserv_opt_dict);
3578 dict_delete(nickserv_allow_auth_dict);
3579 dict_delete(nickserv_email_dict);
3580 dict_delete(nickserv_id_dict);
3581 dict_delete(nickserv_conf.weak_password_dict);
3582 free(auth_func_list);
3583 free(unreg_func_list);
3585 free(allowauth_func_list);
3586 free(handle_merge_func_list);
3587 free(failpw_func_list);
3588 if (nickserv_conf.valid_handle_regex_set)
3589 regfree(&nickserv_conf.valid_handle_regex);
3590 if (nickserv_conf.valid_nick_regex_set)
3591 regfree(&nickserv_conf.valid_nick_regex);
3595 init_nickserv(const char *nick)
3598 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3599 reg_new_user_func(handle_new_user);
3600 reg_nick_change_func(handle_nick_change);
3601 reg_del_user_func(nickserv_remove_user);
3602 reg_account_func(handle_account);
3604 /* set up handle_inverse_flags */
3605 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3606 for (i=0; handle_flags[i]; i++) {
3607 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3608 flag_access_levels[i] = 0;
3611 conf_register_reload(nickserv_conf_read);
3612 nickserv_opt_dict = dict_new();
3613 nickserv_email_dict = dict_new();
3614 dict_set_free_keys(nickserv_email_dict, free);
3615 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3617 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3618 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3619 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3620 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3621 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3622 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3623 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3624 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3625 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3626 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3627 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3628 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3629 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3630 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3631 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3632 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3633 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3634 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3635 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3636 if (!nickserv_conf.disable_nicks) {
3637 /* nick management commands */
3638 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3639 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3640 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3641 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3642 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3643 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3645 if (nickserv_conf.email_enabled) {
3646 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3647 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3648 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3649 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3650 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3652 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3653 /* miscellaneous commands */
3654 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3655 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3656 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3657 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3658 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3660 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3661 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3662 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3663 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3664 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3665 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3666 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3667 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3668 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3669 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3670 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3671 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3672 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3673 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3674 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3676 nickserv_handle_dict = dict_new();
3677 dict_set_free_keys(nickserv_handle_dict, free);
3678 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3680 nickserv_id_dict = dict_new();
3681 dict_set_free_keys(nickserv_id_dict, free);
3683 nickserv_nick_dict = dict_new();
3684 dict_set_free_data(nickserv_nick_dict, free_nick_info);
3686 nickserv_allow_auth_dict = dict_new();
3688 userList_init(&curr_helpers);
3691 nickserv = AddService(nick, "Nick Services", NULL);
3692 nickserv_service = service_register(nickserv);
3694 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3695 reg_exit_func(nickserv_db_cleanup);
3696 if(nickserv_conf.handle_expire_frequency)
3697 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3698 message_register_table(msgtab);