1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for gag_create(), opserv_bad_channel() */
34 #define NICKSERV_CONF_NAME "services/nickserv"
36 #define KEY_DISABLE_NICKS "disable_nicks"
37 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
38 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
39 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
40 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
41 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
42 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
43 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
44 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
45 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
46 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
47 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
48 #define KEY_MODOPER_LEVEL "modoper_level"
49 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
50 #define KEY_FLAG_LEVELS "flag_levels"
51 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
52 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
53 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
54 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
55 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
56 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
57 #define KEY_DICT_FILE "dict_file"
58 #define KEY_NICK "nick"
59 #define KEY_LANGUAGE "language"
60 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
61 #define KEY_AUTOGAG_DURATION "autogag_duration"
62 #define KEY_AUTH_POLICER "auth_policer"
63 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
64 #define KEY_EMAIL_ENABLED "email_enabled"
65 #define KEY_EMAIL_REQUIRED "email_required"
66 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
67 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
68 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
71 #define KEY_PASSWD "passwd"
72 #define KEY_NICKS "nicks"
73 #define KEY_MASKS "masks"
74 #define KEY_OPSERV_LEVEL "opserv_level"
75 #define KEY_FLAGS "flags"
76 #define KEY_REGISTER_ON "register"
77 #define KEY_LAST_SEEN "lastseen"
78 #define KEY_INFO "info"
79 #define KEY_USERLIST_STYLE "user_style"
80 #define KEY_SCREEN_WIDTH "screen_width"
81 #define KEY_LAST_AUTHED_HOST "last_authed_host"
82 #define KEY_LAST_QUIT_HOST "last_quit_host"
83 #define KEY_EMAIL_ADDR "email_addr"
84 #define KEY_COOKIE "cookie"
85 #define KEY_COOKIE_DATA "data"
86 #define KEY_COOKIE_TYPE "type"
87 #define KEY_COOKIE_EXPIRES "expires"
88 #define KEY_ACTIVATION "activation"
89 #define KEY_PASSWORD_CHANGE "password change"
90 #define KEY_EMAIL_CHANGE "email change"
91 #define KEY_ALLOWAUTH "allowauth"
92 #define KEY_EPITHET "epithet"
93 #define KEY_TABLE_WIDTH "table_width"
94 #define KEY_ANNOUNCEMENTS "announcements"
95 #define KEY_MAXLOGINS "maxlogins"
97 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
99 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
100 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
101 typedef OPTION_FUNC(option_func_t);
103 DEFINE_LIST(handle_info_list, struct handle_info*);
105 #define NICKSERV_MIN_PARMS(N) do { \
107 reply("MSG_MISSING_PARAMS", argv[0]); \
108 svccmd_send_help(user, nickserv, cmd); \
112 struct userNode *nickserv;
113 struct userList curr_helpers;
114 const char *handle_flags = HANDLE_FLAGS;
116 static struct module *nickserv_module;
117 static struct service *nickserv_service;
118 static struct log_type *NS_LOG;
119 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
120 static dict_t nickserv_id_dict; /* contains struct handle_info* */
121 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
122 static dict_t nickserv_opt_dict; /* contains option_func_t* */
123 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
124 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
125 static char handle_inverse_flags[256];
126 static unsigned int flag_access_levels[32];
127 static const struct message_entry msgtab[] = {
128 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
129 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
130 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
131 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
132 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
133 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
134 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
135 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
136 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
137 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
138 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
139 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
140 { "NSMSG_USE_COOKIE_REGISTER", "To activate your account, you must check your email for the \"cookie\" that has been mailed to it. When you have it, use the $bcookie$b command to complete registration." },
141 { "NSMSG_USE_COOKIE_RESETPASS", "A cookie has been mailed to your account's email address. You must check your email and use the $bcookie$b command to confirm the password change." },
142 { "NSMSG_USE_COOKIE_EMAIL_1", "A cookie has been mailed to the new address you requested. To finish setting your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
143 { "NSMSG_USE_COOKIE_EMAIL_2", "A cookie has been generated, and half mailed to each your old and new addresses. To finish changing your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
144 { "NSMSG_USE_COOKIE_AUTH", "A cookie has been generated and sent to your email address. Once you have checked your email and received the cookie, auth using the $bcookie$b command." },
145 { "NSMSG_COOKIE_LIVE", "Account $b%s$b already has a cookie active. Please either finish using that cookie, wait for it to expire, or auth to the account and use the $bdelcookie$b command." },
146 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
147 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
148 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
149 { "NSMSG_BAD_COOKIE", "That cookie is not the right one. Please make sure you are copying it EXACTLY from the email; it is case-sensitive, so $bABC$b is different from $babc$b." },
150 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
151 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
152 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
153 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
154 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
155 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
156 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
157 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
158 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
159 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
160 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
161 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
162 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
163 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
164 { "NSMSG_USE_AUTHCOOKIE", "Your hostmask is not valid for account $b%s$b. Please use the $bauthcookie$b command to grant yourself access. (/msg $S authcookie %s)" },
165 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
166 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
167 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
168 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
169 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
170 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
171 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
172 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
173 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
174 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
175 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
176 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
177 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
178 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
179 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
180 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
181 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
182 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
183 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
184 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
185 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
186 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
187 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
188 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
189 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
190 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
191 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
192 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
193 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
194 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
195 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
196 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
197 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
198 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
199 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
200 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
201 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
202 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
203 { "NSMSG_WEAK_PASSWORD", "WARNING: You are using a password that is considered weak (easy to guess). It is STRONGLY recommended you change it (now, if not sooner) by typing \"/msg $S@$s PASS oldpass newpass\" (with your current password and a new password)." },
204 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
205 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
206 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
207 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
208 { "NSMSG_AUTH_ALLOWED_MSG", "You may now authenticate to account $b%s$b by typing $b/msg $N@$s auth %s password$b (using your password). If you will be using this computer regularly, please type $b/msg $N addmask$b (AFTER you auth) to permanently add your hostmask." },
209 { "NSMSG_AUTH_ALLOWED_EMAIL", "You may also (after you auth) type $b/msg $N set email user@your.isp$b to set an email address. This will let you use the $bauthcookie$b command to be authenticated in the future." },
210 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
211 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
212 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
213 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
214 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
215 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
216 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
217 { "NSMSG_PASS_SUCCESS", "Password changed." },
218 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
219 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
220 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
221 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
222 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
223 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
224 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
225 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
226 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
227 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
228 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
229 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
230 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
231 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
232 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
233 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
234 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
235 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
236 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
237 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
238 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
239 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
240 { "NSMSG_NO_ACCESS", "Access denied." },
241 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
242 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
243 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
244 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
245 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
246 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
247 { "NSMSG_BAD_HANDLE", "Account $b%s$b not registered because it is in use by a network service, is too long, or contains invalid characters." },
248 { "NSMSG_BAD_NICK", "Nickname $b%s$b not registered because it is in use by a network service, is too long, or contains invalid characters." },
249 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
250 { "NSMSG_FAIL_RENAME", "Account $b%s$b not renamed to $b%s$b because it is in use by a network services, or contains invalid characters." },
251 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
252 { "NSMSG_SEARCH_MATCH", "Match: %s" },
253 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
254 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
255 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
256 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
257 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
258 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
259 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
260 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
261 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
262 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
263 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
264 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
265 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
266 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
267 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
268 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
269 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
270 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
271 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
272 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
273 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
274 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
275 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
276 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
277 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
278 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
279 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
280 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
281 { "NSEMAIL_ACTIVATION_BODY", "This email has been sent to verify that this email address belongs to the person who tried to register an account on %1$s. Your cookie is:\n %2$s\nTo verify your email address and complete the account registration, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
282 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
283 { "NSEMAIL_PASSWORD_CHANGE_BODY", "This email has been sent to verify that you wish to change the password on your account %5$s. Your cookie is %2$s.\nTo complete the password change, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request your password to be changed, you do not need to do anything. Please contact the %1$s staff if you have questions." },
284 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
285 { "NSEMAIL_EMAIL_CHANGE_BODY_NEW", "This email has been sent to verify that your email address belongs to the same person as account %5$s on %1$s. The SECOND HALF of your cookie is %2$.*6$s.\nTo verify your address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s ?????%2$.*6$s\n(Replace the ????? with the FIRST HALF of the cookie, as sent to your OLD email address.)\nIf you did NOT request this email address to be associated with this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
286 { "NSEMAIL_EMAIL_CHANGE_BODY_OLD", "This email has been sent to verify that you want to change your email for account %5$s on %1$s from this address to %7$s. The FIRST HALF of your cookie is %2$.*6$s\nTo verify your new address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$.*6$s?????\n(Replace the ????? with the SECOND HALF of the cookie, as sent to your NEW email address.)\nIf you did NOT request this change of email address, you do not need to do anything. Please contact the %1$s staff if you have questions." },
287 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
288 { "NSEMAIL_EMAIL_VERIFY_BODY", "This email has been sent to verify that this address belongs to the same person as %5$s on %1$s. Your cookie is %2$s.\nTo verify your address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %1$s\nIf you did NOT request this email address to be associated with this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
289 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
290 { "NSEMAIL_ALLOWAUTH_BODY", "This email has been sent to let you authenticate (auth) to account %5$s on %1$s. Your cookie is %2$s.\nTo auth to that account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %1$s\nIf you did NOT request this authorization, you do not need to do anything. Please contact the %1$s staff if you have questions." },
291 { "CHECKPASS_YES", "Yes." },
292 { "CHECKPASS_NO", "No." },
296 enum reclaim_action {
302 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
303 static void nickserv_reclaim_p(void *data);
306 unsigned int disable_nicks : 1;
307 unsigned int valid_handle_regex_set : 1;
308 unsigned int valid_nick_regex_set : 1;
309 unsigned int autogag_enabled : 1;
310 unsigned int email_enabled : 1;
311 unsigned int email_required : 1;
312 unsigned int default_hostmask : 1;
313 unsigned int warn_nick_owned : 1;
314 unsigned int warn_clone_auth : 1;
315 unsigned long nicks_per_handle;
316 unsigned long password_min_length;
317 unsigned long password_min_digits;
318 unsigned long password_min_upper;
319 unsigned long password_min_lower;
320 unsigned long db_backup_frequency;
321 unsigned long handle_expire_frequency;
322 unsigned long autogag_duration;
323 unsigned long email_visible_level;
324 unsigned long cookie_timeout;
325 unsigned long handle_expire_delay;
326 unsigned long nochan_handle_expire_delay;
327 unsigned long modoper_level;
328 unsigned long set_epithet_level;
329 unsigned long handles_per_email;
330 unsigned long email_search_level;
331 const char *network_name;
332 const char *titlehost_suffix;
333 regex_t valid_handle_regex;
334 regex_t valid_nick_regex;
335 dict_t weak_password_dict;
336 struct policer_params *auth_policer_params;
337 enum reclaim_action reclaim_action;
338 enum reclaim_action auto_reclaim_action;
339 unsigned long auto_reclaim_delay;
340 unsigned char default_maxlogins;
341 unsigned char hard_maxlogins;
344 /* We have 2^32 unique account IDs to use. */
345 unsigned long int highest_id = 0;
348 canonicalize_hostmask(char *mask)
350 char *out = mask, *temp;
351 if ((temp = strchr(mask, '!'))) {
353 while (*temp) *out++ = *temp++;
359 static struct handle_info *
360 register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
362 struct handle_info *hi;
364 #ifdef WITH_PROTOCOL_BAHAMUT
365 char id_base64[IDLEN + 1];
368 /* Assign a unique account ID to the account; note that 0 is
369 an invalid account ID. 1 is therefore the first account ID. */
371 id = 1 + highest_id++;
373 /* Note: highest_id is and must always be the highest ID. */
374 if(id > highest_id) {
378 inttobase64(id_base64, id, IDLEN);
380 /* Make sure an account with the same ID doesn't exist. If a
381 duplicate is found, log some details and assign a new one.
382 This should be impossible, but it never hurts to expect it. */
383 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
384 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
390 hi = calloc(1, sizeof(*hi));
391 hi->userlist_style = HI_DEFAULT_STYLE;
392 hi->announcements = '?';
393 hi->handle = strdup(handle);
394 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
396 dict_insert(nickserv_handle_dict, hi->handle, hi);
398 #ifdef WITH_PROTOCOL_BAHAMUT
400 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
407 register_nick(const char *nick, struct handle_info *owner)
409 struct nick_info *ni;
410 ni = malloc(sizeof(struct nick_info));
411 safestrncpy(ni->nick, nick, sizeof(ni->nick));
413 ni->next = owner->nicks;
415 dict_insert(nickserv_nick_dict, ni->nick, ni);
419 free_nick_info(void *vni)
421 struct nick_info *ni = vni;
426 delete_nick(struct nick_info *ni)
428 struct nick_info *last, *next;
429 struct userNode *user;
430 /* Check to see if we should mark a user as unregistered. */
431 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
432 user->modes &= ~FLAGS_REGNICK;
435 /* Remove ni from the nick_info linked list. */
436 if (ni == ni->owner->nicks) {
437 ni->owner->nicks = ni->next;
439 last = ni->owner->nicks;
445 last->next = next->next;
447 dict_remove(nickserv_nick_dict, ni->nick);
450 static unreg_func_t *unreg_func_list;
451 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
454 reg_unreg_func(unreg_func_t func)
456 if (unreg_func_used == unreg_func_size) {
457 if (unreg_func_size) {
458 unreg_func_size <<= 1;
459 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
462 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
465 unreg_func_list[unreg_func_used++] = func;
469 nickserv_free_cookie(void *data)
471 struct handle_cookie *cookie = data;
472 if (cookie->hi) cookie->hi->cookie = NULL;
473 if (cookie->data) free(cookie->data);
478 free_handle_info(void *vhi)
480 struct handle_info *hi = vhi;
482 #ifdef WITH_PROTOCOL_BAHAMUT
485 inttobase64(id, hi->id, IDLEN);
486 dict_remove(nickserv_id_dict, id);
489 free_string_list(hi->masks);
493 delete_nick(hi->nicks);
497 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
498 nickserv_free_cookie(hi->cookie);
500 if (hi->email_addr) {
501 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
502 handle_info_list_remove(hil, hi);
504 dict_remove(nickserv_email_dict, hi->email_addr);
509 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
512 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
516 for (n=0; n<unreg_func_used; n++)
517 unreg_func_list[n](notify, hi);
519 set_user_handle_info(hi->users, NULL, 0);
521 if (nickserv_conf.disable_nicks)
522 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
524 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
526 dict_remove(nickserv_handle_dict, hi->handle);
530 get_handle_info(const char *handle)
532 return dict_find(nickserv_handle_dict, handle, 0);
536 get_nick_info(const char *nick)
538 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
542 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
547 for (nn=0; nn<channel->members.used; ++nn) {
548 mn = channel->members.list[nn];
549 if ((mn->user != except) && (mn->user->handle_info == handle))
556 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
557 if (!user->handle_info) {
559 send_message(user, bot, "MSG_AUTHENTICATE");
563 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
565 send_message(user, bot, "NSMSG_NO_ACCESS");
569 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
571 send_message(user, bot, "MSG_OPER_SUSPENDED");
575 if (user->handle_info->opserv_level < min_level) {
577 send_message(user, bot, "NSMSG_NO_ACCESS");
585 is_valid_handle(const char *handle)
587 struct userNode *user;
588 /* cant register a juped nick/service nick as handle, to prevent confusion */
589 user = GetUserH(handle);
590 if (user && IsLocal(user))
592 /* check against maximum length */
593 if (strlen(handle) > NICKSERV_HANDLE_LEN)
595 /* for consistency, only allow account names that could be nicks */
596 if (!is_valid_nick(handle))
598 /* disallow account names that look like bad words */
599 if (opserv_bad_channel(handle))
601 /* test either regex or containing all valid chars */
602 if (nickserv_conf.valid_handle_regex_set) {
603 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
606 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
607 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
611 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
616 is_registerable_nick(const char *nick)
618 /* make sure it could be used as an account name */
619 if (!is_valid_handle(nick))
622 if (strlen(nick) > NICKLEN)
624 /* test either regex or as valid handle */
625 if (nickserv_conf.valid_nick_regex_set) {
626 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
629 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
630 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
638 is_valid_email_addr(const char *email)
640 return strchr(email, '@') != NULL;
644 visible_email_addr(struct userNode *user, struct handle_info *hi)
646 if (hi->email_addr) {
647 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
648 return hi->email_addr;
658 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
660 struct handle_info *hi;
661 struct userNode *target;
665 if (!(hi = get_handle_info(++name))) {
666 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
671 if (!(target = GetUserH(name))) {
672 send_message(user, service, "MSG_NICK_UNKNOWN", name);
675 if (IsLocal(target)) {
676 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
679 if (!(hi = target->handle_info)) {
680 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
688 oper_outranks(struct userNode *user, struct handle_info *hi) {
689 if (user->handle_info->opserv_level > hi->opserv_level)
691 if (user->handle_info->opserv_level == hi->opserv_level) {
692 if ((user->handle_info->opserv_level == 1000)
693 || (user->handle_info == hi)
694 || ((user->handle_info->opserv_level == 0)
695 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
696 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
700 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
704 static struct handle_info *
705 get_victim_oper(struct userNode *user, const char *target)
707 struct handle_info *hi;
708 if (!(hi = smart_get_handle_info(nickserv, user, target)))
710 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
711 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
714 return oper_outranks(user, hi) ? hi : NULL;
718 valid_user_for(struct userNode *user, struct handle_info *hi)
722 /* If no hostmasks on the account, allow it. */
723 if (!hi->masks->used)
725 /* If any hostmask matches, allow it. */
726 for (ii=0; ii<hi->masks->used; ii++)
727 if (user_matches_glob(user, hi->masks->list[ii], 0))
729 /* If they are allowauthed to this account, allow it (removing the aa). */
730 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
731 dict_remove(nickserv_allow_auth_dict, user->nick);
734 /* The user is not allowed to use this account. */
739 is_secure_password(const char *handle, const char *pass, struct userNode *user)
742 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
744 if (len < nickserv_conf.password_min_length) {
746 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
749 if (!irccasecmp(pass, handle)) {
751 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
754 dict_find(nickserv_conf.weak_password_dict, pass, &i);
757 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
760 for (i=0; i<len; i++) {
761 if (isdigit(pass[i]))
763 if (isupper(pass[i]))
765 if (islower(pass[i]))
768 if ((cnt_lower < nickserv_conf.password_min_lower)
769 || (cnt_upper < nickserv_conf.password_min_upper)
770 || (cnt_digits < nickserv_conf.password_min_digits)) {
772 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
778 static auth_func_t *auth_func_list;
779 static unsigned int auth_func_size = 0, auth_func_used = 0;
782 reg_auth_func(auth_func_t func)
784 if (auth_func_used == auth_func_size) {
785 if (auth_func_size) {
786 auth_func_size <<= 1;
787 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
790 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
793 auth_func_list[auth_func_used++] = func;
796 static handle_rename_func_t *rf_list;
797 static unsigned int rf_list_size, rf_list_used;
800 reg_handle_rename_func(handle_rename_func_t func)
802 if (rf_list_used == rf_list_size) {
805 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
808 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
811 rf_list[rf_list_used++] = func;
815 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
818 struct handle_info *old_info;
820 /* This can happen if somebody uses COOKIE while authed, or if
821 * they re-auth to their current handle (which is silly, but users
823 if (user->handle_info == hi)
826 if (user->handle_info) {
827 struct userNode *other;
830 userList_remove(&curr_helpers, user);
832 /* remove from next_authed linked list */
833 if (user->handle_info->users == user) {
834 user->handle_info->users = user->next_authed;
836 for (other = user->handle_info->users;
837 other->next_authed != user;
838 other = other->next_authed) ;
839 other->next_authed = user->next_authed;
841 /* if nobody left on old handle, and they're not an oper, remove !god */
842 if (!user->handle_info->users && !user->handle_info->opserv_level)
843 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
844 /* record them as being last seen at this time */
845 user->handle_info->lastseen = now;
846 /* and record their hostmask */
847 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
849 old_info = user->handle_info;
850 user->handle_info = hi;
851 if (hi && !hi->users && !hi->opserv_level)
852 HANDLE_CLEAR_FLAG(hi, HELPING);
853 for (n=0; n<auth_func_used; n++)
854 auth_func_list[n](user, old_info);
856 struct nick_info *ni;
858 HANDLE_CLEAR_FLAG(hi, FROZEN);
859 if (nickserv_conf.warn_clone_auth) {
860 struct userNode *other;
861 for (other = hi->users; other; other = other->next_authed)
862 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
864 user->next_authed = hi->users;
868 userList_append(&curr_helpers, user);
871 #ifdef WITH_PROTOCOL_BAHAMUT
872 /* Stamp users with their account ID. */
874 inttobase64(id, hi->id, IDLEN);
875 #elif WITH_PROTOCOL_P10
876 /* Stamp users with their account name. */
877 char *id = hi->handle;
879 const char *id = "???";
881 if (!nickserv_conf.disable_nicks) {
882 struct nick_info *ni;
883 for (ni = hi->nicks; ni; ni = ni->next) {
884 if (!irccasecmp(user->nick, ni->nick)) {
885 user->modes |= FLAGS_REGNICK;
893 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
894 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
896 /* We cannot clear the user's account ID, unfortunately. */
897 user->next_authed = NULL;
901 static struct handle_info*
902 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
904 struct handle_info *hi;
905 struct nick_info *ni;
906 char crypted[MD5_CRYPT_LENGTH];
908 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
909 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
913 if (!is_secure_password(handle, passwd, user))
916 cryptpass(passwd, crypted);
917 hi = register_handle(handle, crypted, 0);
918 hi->masks = alloc_string_list(1);
920 hi->registered = now;
922 hi->flags = HI_DEFAULT_FLAGS;
923 if (settee && !no_auth)
924 set_user_handle_info(settee, hi, 1);
927 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
928 else if (nickserv_conf.disable_nicks)
929 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
930 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
931 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
933 register_nick(user->nick, hi);
934 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
936 if (settee && (user != settee))
937 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
942 nickserv_bake_cookie(struct handle_cookie *cookie)
944 cookie->hi->cookie = cookie;
945 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
949 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
951 struct handle_cookie *cookie;
952 char subject[128], body[4096], *misc;
953 const char *netname, *fmt;
957 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
961 cookie = calloc(1, sizeof(*cookie));
964 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
965 cookie->expires = now + nickserv_conf.cookie_timeout;
966 inttobase64(cookie->cookie, rand(), 5);
967 inttobase64(cookie->cookie+5, rand(), 5);
969 netname = nickserv_conf.network_name;
972 switch (cookie->type) {
974 hi->passwd[0] = 0; /* invalidate password */
975 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
976 fmt = user_find_message(user, "NSEMAIL_ACTIVATION_SUBJECT");
977 snprintf(subject, sizeof(subject), fmt, netname);
978 fmt = user_find_message(user, "NSEMAIL_ACTIVATION_BODY");
979 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
982 case PASSWORD_CHANGE:
983 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
984 fmt = user_find_message(user, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
985 snprintf(subject, sizeof(subject), fmt, netname);
986 fmt = user_find_message(user, "NSEMAIL_PASSWORD_CHANGE_BODY");
987 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
990 misc = hi->email_addr;
991 hi->email_addr = cookie->data;
993 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
994 fmt = user_find_message(user, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
995 snprintf(subject, sizeof(subject), fmt, netname);
996 fmt = user_find_message(user, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
997 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
998 sendmail(nickserv, hi, subject, body, 1);
999 fmt = user_find_message(user, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1000 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1002 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1003 fmt = user_find_message(user, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1004 snprintf(subject, sizeof(subject), fmt, netname);
1005 fmt = user_find_message(user, "NSEMAIL_EMAIL_VERIFY_BODY");
1006 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1007 sendmail(nickserv, hi, subject, body, 1);
1010 hi->email_addr = misc;
1013 fmt = user_find_message(user, "NSEMAIL_ALLOWAUTH_SUBJECT");
1014 snprintf(subject, sizeof(subject), fmt, netname);
1015 fmt = user_find_message(user, "NSEMAIL_ALLOWAUTH_BODY");
1016 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1019 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1023 sendmail(nickserv, hi, subject, body, first_time);
1024 nickserv_bake_cookie(cookie);
1028 nickserv_eat_cookie(struct handle_cookie *cookie)
1030 cookie->hi->cookie = NULL;
1031 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1032 nickserv_free_cookie(cookie);
1036 nickserv_free_email_addr(void *data)
1038 handle_info_list_clean(data);
1043 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1045 struct handle_info_list *hil;
1046 /* Remove from old handle_info_list ... */
1047 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1048 handle_info_list_remove(hil, hi);
1049 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1050 hi->email_addr = NULL;
1052 /* Add to the new list.. */
1053 if (new_email_addr) {
1054 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1055 hil = calloc(1, sizeof(*hil));
1056 hil->tag = strdup(new_email_addr);
1057 handle_info_list_init(hil);
1058 dict_insert(nickserv_email_dict, hil->tag, hil);
1060 handle_info_list_append(hil, hi);
1061 hi->email_addr = hil->tag;
1065 static NICKSERV_FUNC(cmd_register)
1067 struct handle_info *hi;
1068 const char *email_addr, *password;
1071 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1072 /* Require the first handle registered to belong to someone +o. */
1073 reply("NSMSG_REQUIRE_OPER");
1077 if (user->handle_info) {
1078 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1082 if (IsStamped(user)) {
1083 /* Unauthenticated users might still have been stamped
1084 previously and could therefore have a hidden host;
1085 do not allow them to register a new account. */
1086 reply("NSMSG_STAMPED_REGISTER");
1090 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1092 if (!is_valid_handle(argv[1])) {
1093 reply("NSMSG_BAD_HANDLE", argv[1]);
1097 if ((argc >= 4) && nickserv_conf.email_enabled) {
1098 struct handle_info_list *hil;
1101 /* Remember email address. */
1102 email_addr = argv[3];
1104 /* Check that the email address looks valid.. */
1105 if (!is_valid_email_addr(email_addr)) {
1106 reply("NSMSG_BAD_EMAIL_ADDR");
1110 /* .. and that we are allowed to send to it. */
1111 if ((str = sendmail_prohibited_address(email_addr))) {
1112 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1116 /* If we do email verify, make sure we don't spam the address. */
1117 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1119 for (nn=0; nn<hil->used; nn++) {
1120 if (hil->list[nn]->cookie) {
1121 reply("NSMSG_EMAIL_UNACTIVATED");
1125 if (hil->used >= nickserv_conf.handles_per_email) {
1126 reply("NSMSG_EMAIL_OVERUSED");
1139 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1141 /* Add any masks they should get. */
1142 if (nickserv_conf.default_hostmask) {
1143 string_list_append(hi->masks, strdup("*@*"));
1145 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1146 if (user->ip.s_addr && user->hostname[strspn(user->hostname, "0123456789.")])
1147 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1150 /* If they're the first to register, give them level 1000. */
1151 if (dict_size(nickserv_handle_dict) == 1) {
1152 hi->opserv_level = 1000;
1153 reply("NSMSG_ROOT_HANDLE", argv[1]);
1156 /* Set their email address. */
1158 nickserv_set_email_addr(hi, email_addr);
1160 /* If they need to do email verification, tell them. */
1162 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1167 static NICKSERV_FUNC(cmd_oregister)
1170 struct userNode *settee;
1171 struct handle_info *hi;
1173 NICKSERV_MIN_PARMS(4);
1175 if (!is_valid_handle(argv[1])) {
1176 reply("NSMSG_BAD_HANDLE", argv[1]);
1180 if (strchr(argv[3], '@')) {
1181 mask = canonicalize_hostmask(strdup(argv[3]));
1183 settee = GetUserH(argv[4]);
1185 reply("MSG_NICK_UNKNOWN", argv[4]);
1192 } else if ((settee = GetUserH(argv[3]))) {
1193 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1195 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1198 if (settee && settee->handle_info) {
1199 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1203 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1207 string_list_append(hi->masks, mask);
1211 static NICKSERV_FUNC(cmd_handleinfo)
1214 unsigned int i, pos=0, herelen;
1215 struct userNode *target, *next_un;
1216 struct handle_info *hi;
1217 const char *nsmsg_none;
1220 if (!(hi = user->handle_info)) {
1221 reply("NSMSG_MUST_AUTH");
1224 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1228 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1229 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1230 #ifdef WITH_PROTOCOL_BAHAMUT
1231 reply("NSMSG_HANDLEINFO_ID", hi->id);
1233 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1236 intervalString(buff, now - hi->lastseen);
1237 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1239 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1242 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1243 if (HANDLE_FLAGGED(hi, FROZEN))
1244 reply("NSMSG_HANDLEINFO_VACATION");
1246 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1247 struct do_not_register *dnr;
1248 if ((dnr = chanserv_is_dnr(NULL, hi)))
1249 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1250 if (!oper_outranks(user, hi))
1252 } else if (hi != user->handle_info)
1255 if (nickserv_conf.email_enabled)
1256 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1260 switch (hi->cookie->type) {
1261 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1262 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1263 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1264 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1265 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1271 unsigned long flen = 1;
1272 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1274 for (i=0, flen=1; handle_flags[i]; i++)
1275 if (hi->flags & 1 << i)
1276 flags[flen++] = handle_flags[i];
1278 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1280 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1283 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1284 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1285 || (hi->opserv_level > 0)) {
1286 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1289 if (hi->last_quit_host[0])
1290 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1292 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1294 if (nickserv_conf.disable_nicks) {
1295 /* nicks disabled; don't show anything about registered nicks */
1296 } else if (hi->nicks) {
1297 struct nick_info *ni, *next_ni;
1298 for (ni = hi->nicks; ni; ni = next_ni) {
1299 herelen = strlen(ni->nick);
1300 if (pos + herelen + 1 > ArrayLength(buff)) {
1302 goto print_nicks_buff;
1306 memcpy(buff+pos, ni->nick, herelen);
1307 pos += herelen; buff[pos++] = ' ';
1311 reply("NSMSG_HANDLEINFO_NICKS", buff);
1316 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1319 if (hi->masks->used) {
1320 for (i=0; i < hi->masks->used; i++) {
1321 herelen = strlen(hi->masks->list[i]);
1322 if (pos + herelen + 1 > ArrayLength(buff)) {
1324 goto print_mask_buff;
1326 memcpy(buff+pos, hi->masks->list[i], herelen);
1327 pos += herelen; buff[pos++] = ' ';
1328 if (i+1 == hi->masks->used) {
1331 reply("NSMSG_HANDLEINFO_MASKS", buff);
1336 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1340 struct userData *channel, *next;
1343 for (channel = hi->channels; channel; channel = next) {
1344 next = channel->u_next;
1345 name = channel->channel->channel->name;
1346 herelen = strlen(name);
1347 if (pos + herelen + 7 > ArrayLength(buff)) {
1349 goto print_chans_buff;
1351 if (IsUserSuspended(channel))
1353 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1357 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1362 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1365 for (target = hi->users; target; target = next_un) {
1366 herelen = strlen(target->nick);
1367 if (pos + herelen + 1 > ArrayLength(buff)) {
1369 goto print_cnick_buff;
1371 next_un = target->next_authed;
1373 memcpy(buff+pos, target->nick, herelen);
1374 pos += herelen; buff[pos++] = ' ';
1378 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1386 static NICKSERV_FUNC(cmd_userinfo)
1388 struct userNode *target;
1390 NICKSERV_MIN_PARMS(2);
1391 if (!(target = GetUserH(argv[1]))) {
1392 reply("MSG_NICK_UNKNOWN", argv[1]);
1395 if (target->handle_info)
1396 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1398 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1402 static NICKSERV_FUNC(cmd_nickinfo)
1404 struct nick_info *ni;
1406 NICKSERV_MIN_PARMS(2);
1407 if (!(ni = get_nick_info(argv[1]))) {
1408 reply("MSG_NICK_UNKNOWN", argv[1]);
1411 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1415 static NICKSERV_FUNC(cmd_rename_handle)
1417 struct handle_info *hi;
1418 char msgbuf[MAXLEN], *old_handle;
1421 NICKSERV_MIN_PARMS(3);
1422 if (!(hi = get_victim_oper(user, argv[1])))
1424 if (!is_valid_handle(argv[2])) {
1425 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1428 if (get_handle_info(argv[2])) {
1429 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1433 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1434 hi->handle = strdup(argv[2]);
1435 dict_insert(nickserv_handle_dict, hi->handle, hi);
1436 for (nn=0; nn<rf_list_used; nn++)
1437 rf_list[nn](hi, old_handle);
1438 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1439 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1440 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1445 static failpw_func_t *failpw_func_list;
1446 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1449 reg_failpw_func(failpw_func_t func)
1451 if (failpw_func_used == failpw_func_size) {
1452 if (failpw_func_size) {
1453 failpw_func_size <<= 1;
1454 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1456 failpw_func_size = 8;
1457 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1460 failpw_func_list[failpw_func_used++] = func;
1463 static NICKSERV_FUNC(cmd_auth)
1465 int pw_arg, used, maxlogins;
1466 struct handle_info *hi;
1468 struct userNode *other;
1470 if (user->handle_info) {
1471 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1474 if (IsStamped(user)) {
1475 /* Unauthenticated users might still have been stamped
1476 previously and could therefore have a hidden host;
1477 do not allow them to authenticate. */
1478 reply("NSMSG_STAMPED_AUTH");
1482 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1484 } else if (argc == 2) {
1485 if (nickserv_conf.disable_nicks) {
1486 if (!(hi = get_handle_info(user->nick))) {
1487 reply("NSMSG_HANDLE_NOT_FOUND");
1491 /* try to look up their handle from their nick */
1492 struct nick_info *ni;
1493 ni = get_nick_info(user->nick);
1495 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1502 reply("MSG_MISSING_PARAMS", argv[0]);
1503 svccmd_send_help(user, nickserv, cmd);
1507 reply("NSMSG_HANDLE_NOT_FOUND");
1510 passwd = argv[pw_arg];
1511 if (!valid_user_for(user, hi)) {
1512 if (hi->email_addr && nickserv_conf.email_enabled)
1513 reply("NSMSG_USE_AUTHCOOKIE", hi->handle, hi->handle);
1515 reply("NSMSG_HOSTMASK_INVALID", hi->handle);
1516 argv[pw_arg] = "BADMASK";
1519 if (!checkpass(passwd, hi->passwd)) {
1521 reply("NSMSG_PASSWORD_INVALID");
1522 argv[pw_arg] = "BADPASS";
1523 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1524 if (nickserv_conf.autogag_enabled) {
1525 if (!user->auth_policer.params) {
1526 user->auth_policer.last_req = now;
1527 user->auth_policer.params = nickserv_conf.auth_policer_params;
1529 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1531 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1532 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1533 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1535 argv[pw_arg] = "GAGGED";
1540 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1541 reply("NSMSG_HANDLE_SUSPENDED");
1542 argv[pw_arg] = "SUSPENDED";
1545 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1546 for (used = 0, other = hi->users; other; other = other->next_authed) {
1547 if (++used >= maxlogins) {
1548 reply("NSMSG_MAX_LOGINS", maxlogins);
1549 argv[pw_arg] = "MAXLOGINS";
1554 if (nickserv_conf.email_required && !hi->email_addr)
1555 reply("NSMSG_PLEASE_SET_EMAIL");
1556 if (!is_secure_password(hi->handle, passwd, NULL))
1557 reply("NSMSG_WEAK_PASSWORD");
1558 if (hi->passwd[0] != '$')
1559 cryptpass(passwd, hi->passwd);
1561 reply("NSMSG_AUTH_SUCCESS");
1562 argv[pw_arg] = "****";
1563 set_user_handle_info(user, hi, 1);
1567 static allowauth_func_t *allowauth_func_list;
1568 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1571 reg_allowauth_func(allowauth_func_t func)
1573 if (allowauth_func_used == allowauth_func_size) {
1574 if (allowauth_func_size) {
1575 allowauth_func_size <<= 1;
1576 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1578 allowauth_func_size = 8;
1579 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1582 allowauth_func_list[allowauth_func_used++] = func;
1585 static NICKSERV_FUNC(cmd_allowauth)
1587 struct userNode *target;
1588 struct handle_info *hi;
1591 NICKSERV_MIN_PARMS(2);
1592 if (!(target = GetUserH(argv[1]))) {
1593 reply("MSG_NICK_UNKNOWN", argv[1]);
1596 if (target->handle_info) {
1597 reply("NSMSG_USER_PREV_AUTH", target->nick);
1600 if (IsStamped(target)) {
1601 /* Unauthenticated users might still have been stamped
1602 previously and could therefore have a hidden host;
1603 do not allow them to authenticate to an account. */
1604 send_message(target, nickserv, "NSMSG_USER_PREV_STAMP", target->nick);
1609 else if (!(hi = get_handle_info(argv[2]))) {
1610 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1614 if (hi->opserv_level > user->handle_info->opserv_level) {
1615 reply("MSG_USER_OUTRANKED", hi->handle);
1618 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1619 || (hi->opserv_level > 0))
1620 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1621 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1624 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1625 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1626 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1627 if (nickserv_conf.email_enabled)
1628 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1630 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1631 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1633 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1635 for (n=0; n<allowauth_func_used; n++)
1636 allowauth_func_list[n](user, target, hi);
1640 static NICKSERV_FUNC(cmd_authcookie)
1642 struct handle_info *hi;
1644 NICKSERV_MIN_PARMS(2);
1645 if (user->handle_info) {
1646 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1649 if (IsStamped(user)) {
1650 /* Unauthenticated users might still have been stamped
1651 previously and could therefore have a hidden host;
1652 do not allow them to authenticate to an account. */
1653 reply("NSMSG_STAMPED_AUTHCOOKIE");
1656 if (!(hi = get_handle_info(argv[1]))) {
1657 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1660 if (!hi->email_addr) {
1661 reply("MSG_SET_EMAIL_ADDR");
1664 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1665 reply("NSMSG_USE_COOKIE_AUTH");
1669 static NICKSERV_FUNC(cmd_delcookie)
1671 struct handle_info *hi;
1673 hi = user->handle_info;
1675 reply("NSMSG_NO_COOKIE");
1678 switch (hi->cookie->type) {
1681 reply("NSMSG_MUST_TIME_OUT");
1684 nickserv_eat_cookie(hi->cookie);
1685 reply("NSMSG_ATE_COOKIE");
1691 static NICKSERV_FUNC(cmd_resetpass)
1693 struct handle_info *hi;
1694 char crypted[MD5_CRYPT_LENGTH];
1696 NICKSERV_MIN_PARMS(3);
1697 if (user->handle_info) {
1698 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1701 if (IsStamped(user)) {
1702 /* Unauthenticated users might still have been stamped
1703 previously and could therefore have a hidden host;
1704 do not allow them to activate an account. */
1705 reply("NSMSG_STAMPED_RESETPASS");
1708 if (!(hi = get_handle_info(argv[1]))) {
1709 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1712 if (!hi->email_addr) {
1713 reply("MSG_SET_EMAIL_ADDR");
1716 cryptpass(argv[2], crypted);
1718 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1722 static NICKSERV_FUNC(cmd_cookie)
1724 struct handle_info *hi;
1727 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1730 NICKSERV_MIN_PARMS(3);
1731 if (!(hi = get_handle_info(argv[1]))) {
1732 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1738 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1739 reply("NSMSG_HANDLE_SUSPENDED");
1744 reply("NSMSG_NO_COOKIE");
1748 /* Check validity of operation before comparing cookie to
1749 * prohibit guessing by authed users. */
1750 if (user->handle_info
1751 && (hi->cookie->type != EMAIL_CHANGE)
1752 && (hi->cookie->type != PASSWORD_CHANGE)) {
1753 reply("NSMSG_CANNOT_COOKIE");
1757 if (strcmp(cookie, hi->cookie->cookie)) {
1758 reply("NSMSG_BAD_COOKIE");
1762 switch (hi->cookie->type) {
1764 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1765 set_user_handle_info(user, hi, 1);
1766 reply("NSMSG_HANDLE_ACTIVATED");
1768 case PASSWORD_CHANGE:
1769 set_user_handle_info(user, hi, 1);
1770 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1771 reply("NSMSG_PASSWORD_CHANGED");
1774 nickserv_set_email_addr(hi, hi->cookie->data);
1775 reply("NSMSG_EMAIL_CHANGED");
1778 set_user_handle_info(user, hi, 1);
1779 reply("NSMSG_AUTH_SUCCESS");
1782 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1783 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1787 nickserv_eat_cookie(hi->cookie);
1792 static NICKSERV_FUNC(cmd_oregnick) {
1794 struct handle_info *target;
1795 struct nick_info *ni;
1797 NICKSERV_MIN_PARMS(3);
1798 if (!(target = modcmd_get_handle_info(user, argv[1])))
1801 if (!is_registerable_nick(nick)) {
1802 reply("NSMSG_BAD_NICK", nick);
1805 ni = dict_find(nickserv_nick_dict, nick, NULL);
1807 reply("NSMSG_NICK_EXISTS", nick);
1810 register_nick(nick, target);
1811 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1815 static NICKSERV_FUNC(cmd_regnick) {
1817 struct nick_info *ni;
1819 if (!is_registerable_nick(user->nick)) {
1820 reply("NSMSG_BAD_NICK", user->nick);
1823 /* count their nicks, see if it's too many */
1824 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1825 if (n >= nickserv_conf.nicks_per_handle) {
1826 reply("NSMSG_TOO_MANY_NICKS");
1829 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1831 reply("NSMSG_NICK_EXISTS", user->nick);
1834 register_nick(user->nick, user->handle_info);
1835 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1839 static NICKSERV_FUNC(cmd_pass)
1841 struct handle_info *hi;
1842 const char *old_pass, *new_pass;
1844 NICKSERV_MIN_PARMS(3);
1845 hi = user->handle_info;
1849 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1850 if (!checkpass(old_pass, hi->passwd)) {
1851 argv[1] = "BADPASS";
1852 reply("NSMSG_PASSWORD_INVALID");
1855 cryptpass(new_pass, hi->passwd);
1857 reply("NSMSG_PASS_SUCCESS");
1862 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1865 char *new_mask = canonicalize_hostmask(strdup(mask));
1866 for (i=0; i<hi->masks->used; i++) {
1867 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1868 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1873 string_list_append(hi->masks, new_mask);
1874 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
1878 static NICKSERV_FUNC(cmd_addmask)
1881 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1882 int res = nickserv_addmask(user, user->handle_info, mask);
1886 if (!is_gline(argv[1])) {
1887 reply("NSMSG_MASK_INVALID", argv[1]);
1890 return nickserv_addmask(user, user->handle_info, argv[1]);
1894 static NICKSERV_FUNC(cmd_oaddmask)
1896 struct handle_info *hi;
1898 NICKSERV_MIN_PARMS(3);
1899 if (!(hi = get_victim_oper(user, argv[1])))
1901 return nickserv_addmask(user, hi, argv[2]);
1905 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
1908 for (i=0; i<hi->masks->used; i++) {
1909 if (!strcmp(del_mask, hi->masks->list[i])) {
1910 char *old_mask = hi->masks->list[i];
1911 if (hi->masks->used == 1) {
1912 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
1915 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
1916 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
1921 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
1925 static NICKSERV_FUNC(cmd_delmask)
1927 NICKSERV_MIN_PARMS(2);
1928 return nickserv_delmask(user, user->handle_info, argv[1]);
1931 static NICKSERV_FUNC(cmd_odelmask)
1933 struct handle_info *hi;
1934 NICKSERV_MIN_PARMS(3);
1935 if (!(hi = get_victim_oper(user, argv[1])))
1937 return nickserv_delmask(user, hi, argv[2]);
1941 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
1942 unsigned int nn, add = 1, pos;
1943 unsigned long added, removed, flag;
1945 for (added=removed=nn=0; str[nn]; nn++) {
1947 case '+': add = 1; break;
1948 case '-': add = 0; break;
1950 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
1951 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
1954 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
1955 /* cheesy avoidance of looking up the flag name.. */
1956 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
1959 flag = 1 << (pos - 1);
1961 added |= flag, removed &= ~flag;
1963 removed |= flag, added &= ~flag;
1968 *premoved = removed;
1973 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
1975 unsigned long before, after, added, removed;
1976 struct userNode *uNode;
1978 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
1979 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
1981 hi->flags = (hi->flags | added) & ~removed;
1982 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
1984 /* Strip helping flag if they're only a support helper and not
1985 * currently in #support. */
1986 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
1987 struct channelList *schannels;
1989 schannels = chanserv_support_channels();
1990 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
1991 for (ii = 0; ii < schannels->used; ++ii)
1992 if (GetUserMode(schannels->list[ii], uNode))
1994 if (ii < schannels->used)
1998 HANDLE_CLEAR_FLAG(hi, HELPING);
2001 if (after && !before) {
2002 /* Add user to current helper list. */
2003 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2004 userList_append(&curr_helpers, uNode);
2005 } else if (!after && before) {
2006 /* Remove user from current helper list. */
2007 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2008 userList_remove(&curr_helpers, uNode);
2015 set_list(struct userNode *user, struct handle_info *hi, int override)
2019 char *set_display[] = {
2020 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2021 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS"
2024 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2026 /* Do this so options are presented in a consistent order. */
2027 for (i = 0; i < ArrayLength(set_display); ++i)
2028 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2029 opt(user, hi, override, 0, NULL);
2032 static NICKSERV_FUNC(cmd_set)
2034 struct handle_info *hi;
2037 hi = user->handle_info;
2039 set_list(user, hi, 0);
2042 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2043 reply("NSMSG_INVALID_OPTION", argv[1]);
2046 return opt(user, hi, 0, argc-1, argv+1);
2049 static NICKSERV_FUNC(cmd_oset)
2051 struct handle_info *hi;
2054 NICKSERV_MIN_PARMS(2);
2056 if (!(hi = get_victim_oper(user, argv[1])))
2060 set_list(user, hi, 0);
2064 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2065 reply("NSMSG_INVALID_OPTION", argv[2]);
2069 return opt(user, hi, 1, argc-2, argv+2);
2072 static OPTION_FUNC(opt_info)
2076 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2078 hi->infoline = NULL;
2080 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2084 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2085 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2089 static OPTION_FUNC(opt_width)
2092 unsigned int new_width = strtoul(argv[1], NULL, 0);
2093 hi->screen_width = new_width;
2096 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2097 hi->screen_width = MIN_LINE_SIZE;
2098 else if (hi->screen_width > MAX_LINE_SIZE)
2099 hi->screen_width = MAX_LINE_SIZE;
2101 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2105 static OPTION_FUNC(opt_tablewidth)
2108 unsigned int new_width = strtoul(argv[1], NULL, 0);
2109 hi->table_width = new_width;
2112 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2113 hi->table_width = MIN_LINE_SIZE;
2114 else if (hi->screen_width > MAX_LINE_SIZE)
2115 hi->table_width = MAX_LINE_SIZE;
2117 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2121 static OPTION_FUNC(opt_color)
2124 if (enabled_string(argv[1]))
2125 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2126 else if (disabled_string(argv[1]))
2127 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2129 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2134 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2138 static OPTION_FUNC(opt_privmsg)
2141 if (enabled_string(argv[1]))
2142 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2143 else if (disabled_string(argv[1]))
2144 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2146 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2151 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2155 static OPTION_FUNC(opt_style)
2160 if (!irccasecmp(argv[1], "Zoot"))
2161 hi->userlist_style = HI_STYLE_ZOOT;
2162 else if (!irccasecmp(argv[1], "def"))
2163 hi->userlist_style = HI_STYLE_DEF;
2166 switch (hi->userlist_style) {
2175 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2179 static OPTION_FUNC(opt_announcements)
2184 if (enabled_string(argv[1]))
2185 hi->announcements = 'y';
2186 else if (disabled_string(argv[1]))
2187 hi->announcements = 'n';
2188 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2189 hi->announcements = '?';
2191 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2196 switch (hi->announcements) {
2197 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2198 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2199 case '?': choice = "default"; break;
2200 default: choice = "unknown"; break;
2202 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2206 static OPTION_FUNC(opt_password)
2209 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2214 cryptpass(argv[1], hi->passwd);
2216 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2220 static OPTION_FUNC(opt_flags)
2223 unsigned int ii, flen;
2226 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2231 nickserv_apply_flags(user, hi, argv[1]);
2233 for (ii = flen = 0; handle_flags[ii]; ii++)
2234 if (hi->flags & (1 << ii))
2235 flags[flen++] = handle_flags[ii];
2238 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2240 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2244 static OPTION_FUNC(opt_email)
2248 if (!is_valid_email_addr(argv[1])) {
2249 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2252 if ((str = sendmail_prohibited_address(argv[1]))) {
2253 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2256 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2257 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2259 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2261 nickserv_set_email_addr(hi, argv[1]);
2263 nickserv_eat_cookie(hi->cookie);
2264 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2267 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2271 static OPTION_FUNC(opt_maxlogins)
2275 maxlogins = strtoul(argv[1], NULL, 0);
2276 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2277 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2280 hi->maxlogins = maxlogins;
2282 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2283 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2287 static OPTION_FUNC(opt_language)
2289 struct language *lang;
2291 lang = language_find(argv[1]);
2292 if (irccasecmp(lang->name, argv[1]))
2293 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2294 hi->language = lang;
2296 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2301 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2302 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2304 if ((user->handle_info->opserv_level < target->opserv_level)
2305 || ((user->handle_info->opserv_level == target->opserv_level)
2306 && (user->handle_info->opserv_level < 1000))) {
2307 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2310 if ((user->handle_info->opserv_level < new_level)
2311 || ((user->handle_info->opserv_level == new_level)
2312 && (user->handle_info->opserv_level < 1000))) {
2313 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2316 if (user->handle_info == target) {
2317 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2320 if (target->opserv_level == new_level)
2322 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2323 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2324 target->opserv_level = new_level;
2328 static OPTION_FUNC(opt_level)
2333 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2337 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2338 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2342 static OPTION_FUNC(opt_epithet)
2345 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2349 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2350 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2353 if ((epithet[0] == '*') && !epithet[1])
2356 hi->epithet = strdup(epithet);
2360 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2362 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2366 static NICKSERV_FUNC(cmd_reclaim)
2368 struct handle_info *hi;
2369 struct nick_info *ni;
2370 struct userNode *victim;
2372 NICKSERV_MIN_PARMS(2);
2373 hi = user->handle_info;
2374 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2376 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2379 if (ni->owner != user->handle_info) {
2380 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2383 victim = GetUserH(ni->nick);
2385 reply("MSG_NICK_UNKNOWN", ni->nick);
2388 if (victim == user) {
2389 reply("NSMSG_NICK_USER_YOU");
2392 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2393 switch (nickserv_conf.reclaim_action) {
2394 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2395 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2396 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2397 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2402 static NICKSERV_FUNC(cmd_unregnick)
2405 struct handle_info *hi;
2406 struct nick_info *ni;
2408 hi = user->handle_info;
2409 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2410 ni = dict_find(nickserv_nick_dict, nick, NULL);
2412 reply("NSMSG_UNKNOWN_NICK", nick);
2415 if (hi != ni->owner) {
2416 reply("NSMSG_NOT_YOUR_NICK", nick);
2419 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2424 static NICKSERV_FUNC(cmd_ounregnick)
2426 struct nick_info *ni;
2428 NICKSERV_MIN_PARMS(2);
2429 if (!(ni = get_nick_info(argv[1]))) {
2430 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2433 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2434 reply("MSG_USER_OUTRANKED", ni->nick);
2437 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2442 static NICKSERV_FUNC(cmd_unregister)
2444 struct handle_info *hi;
2447 NICKSERV_MIN_PARMS(2);
2448 hi = user->handle_info;
2451 if (checkpass(passwd, hi->passwd)) {
2452 nickserv_unregister_handle(hi, user);
2455 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2456 reply("NSMSG_PASSWORD_INVALID");
2461 static NICKSERV_FUNC(cmd_ounregister)
2463 struct handle_info *hi;
2465 NICKSERV_MIN_PARMS(2);
2466 if (!(hi = get_victim_oper(user, argv[1])))
2468 nickserv_unregister_handle(hi, user);
2472 static NICKSERV_FUNC(cmd_status)
2474 if (nickserv_conf.disable_nicks) {
2475 reply("NSMSG_GLOBAL_STATS_NONICK",
2476 dict_size(nickserv_handle_dict));
2478 if (user->handle_info) {
2480 struct nick_info *ni;
2481 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2482 reply("NSMSG_HANDLE_STATS", cnt);
2484 reply("NSMSG_HANDLE_NONE");
2486 reply("NSMSG_GLOBAL_STATS",
2487 dict_size(nickserv_handle_dict),
2488 dict_size(nickserv_nick_dict));
2493 static NICKSERV_FUNC(cmd_ghost)
2495 struct userNode *target;
2496 char reason[MAXLEN];
2498 NICKSERV_MIN_PARMS(2);
2499 if (!(target = GetUserH(argv[1]))) {
2500 reply("MSG_NICK_UNKNOWN", argv[1]);
2503 if (target == user) {
2504 reply("NSMSG_CANNOT_GHOST_SELF");
2507 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2508 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2511 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2512 DelUser(target, nickserv, 1, reason);
2513 reply("NSMSG_GHOST_KILLED", argv[1]);
2517 static NICKSERV_FUNC(cmd_vacation)
2519 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2520 reply("NSMSG_ON_VACATION");
2525 nickserv_saxdb_write(struct saxdb_context *ctx) {
2527 struct handle_info *hi;
2530 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2532 #ifdef WITH_PROTOCOL_BAHAMUT
2535 saxdb_start_record(ctx, iter_key(it), 0);
2536 if (hi->announcements != '?') {
2537 flags[0] = hi->announcements;
2539 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2542 struct handle_cookie *cookie = hi->cookie;
2545 switch (cookie->type) {
2546 case ACTIVATION: type = KEY_ACTIVATION; break;
2547 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2548 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2549 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2550 default: type = NULL; break;
2553 saxdb_start_record(ctx, KEY_COOKIE, 0);
2554 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2555 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2557 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2558 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2559 saxdb_end_record(ctx);
2563 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2565 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2569 for (ii=flen=0; handle_flags[ii]; ++ii)
2570 if (hi->flags & (1 << ii))
2571 flags[flen++] = handle_flags[ii];
2573 saxdb_write_string(ctx, KEY_FLAGS, flags);
2575 #ifdef WITH_PROTOCOL_BAHAMUT
2576 saxdb_write_int(ctx, KEY_ID, hi->id);
2579 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2580 if (hi->last_quit_host[0])
2581 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2582 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2583 if (hi->masks->used)
2584 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2586 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2588 struct string_list *slist;
2589 struct nick_info *ni;
2591 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2592 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2593 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2597 if (hi->opserv_level)
2598 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2599 if (hi->language != lang_C)
2600 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2601 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2602 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2603 if (hi->screen_width)
2604 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2605 if (hi->table_width)
2606 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2607 flags[0] = hi->userlist_style;
2609 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2610 saxdb_end_record(ctx);
2615 static handle_merge_func_t *handle_merge_func_list;
2616 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2619 reg_handle_merge_func(handle_merge_func_t func)
2621 if (handle_merge_func_used == handle_merge_func_size) {
2622 if (handle_merge_func_size) {
2623 handle_merge_func_size <<= 1;
2624 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2626 handle_merge_func_size = 8;
2627 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2630 handle_merge_func_list[handle_merge_func_used++] = func;
2633 static NICKSERV_FUNC(cmd_merge)
2635 struct handle_info *hi_from, *hi_to;
2636 struct userNode *last_user;
2637 struct userData *cList, *cListNext;
2638 unsigned int ii, jj, n;
2639 char buffer[MAXLEN];
2641 NICKSERV_MIN_PARMS(3);
2643 if (!(hi_from = get_victim_oper(user, argv[1])))
2645 if (!(hi_to = get_victim_oper(user, argv[2])))
2647 if (hi_to == hi_from) {
2648 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2652 for (n=0; n<handle_merge_func_used; n++)
2653 handle_merge_func_list[n](user, hi_to, hi_from);
2655 /* Append "from" handle's nicks to "to" handle's nick list. */
2657 struct nick_info *last_ni;
2658 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2659 last_ni->next = hi_from->nicks;
2661 while (hi_from->nicks) {
2662 hi_from->nicks->owner = hi_to;
2663 hi_from->nicks = hi_from->nicks->next;
2666 /* Merge the hostmasks. */
2667 for (ii=0; ii<hi_from->masks->used; ii++) {
2668 char *mask = hi_from->masks->list[ii];
2669 for (jj=0; jj<hi_to->masks->used; jj++)
2670 if (match_ircglobs(hi_to->masks->list[jj], mask))
2672 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2673 string_list_append(hi_to->masks, strdup(mask));
2676 /* Merge the lists of authed users. */
2678 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2679 last_user->next_authed = hi_from->users;
2681 hi_to->users = hi_from->users;
2683 /* Repoint the old "from" handle's users. */
2684 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2685 last_user->handle_info = hi_to;
2687 hi_from->users = NULL;
2689 /* Merge channel userlists. */
2690 for (cList=hi_from->channels; cList; cList=cListNext) {
2691 struct userData *cList2;
2692 cListNext = cList->u_next;
2693 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2694 if (cList->channel == cList2->channel)
2696 log_module(NS_LOG, LOG_DEBUG, "Merging %s->%s@%s: before %p->%p->%-p, %p->%p->%p",
2697 hi_from->handle, hi_to->handle, cList->channel->channel->name,
2698 cList->u_prev, cList, cList->u_next,
2699 (cList2?cList2->u_prev:0), cList2, (cList2?cList2->u_next:0));
2700 if (cList2 && (cList2->access >= cList->access)) {
2701 /* keep cList2 in hi_to; remove cList from hi_from */
2702 log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList);
2703 del_channel_user(cList, 1);
2706 /* remove the lower-ranking cList2 from hi_to */
2707 log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList2);
2708 del_channel_user(cList2, 1);
2710 /* cList needs to be moved from hi_from to hi_to */
2711 cList->handle = hi_to;
2712 /* Remove from linked list for hi_from */
2713 assert(!cList->u_prev);
2714 hi_from->channels = cList->u_next;
2716 cList->u_next->u_prev = cList->u_prev;
2717 /* Add to linked list for hi_to */
2718 cList->u_prev = NULL;
2719 cList->u_next = hi_to->channels;
2720 if (hi_to->channels)
2721 hi_to->channels->u_prev = cList;
2722 hi_to->channels = cList;
2723 log_module(NS_LOG, LOG_DEBUG, "Now %p->%p->%p",
2724 cList->u_prev, cList, cList->u_next);
2728 /* Do they get an OpServ level promotion? */
2729 if (hi_from->opserv_level > hi_to->opserv_level)
2730 hi_to->opserv_level = hi_from->opserv_level;
2732 /* What about last seen time? */
2733 if (hi_from->lastseen > hi_to->lastseen)
2734 hi_to->lastseen = hi_from->lastseen;
2736 /* Notify of success. */
2737 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2738 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2739 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2741 /* Unregister the "from" handle. */
2742 nickserv_unregister_handle(hi_from, NULL);
2747 struct nickserv_discrim {
2748 unsigned int limit, min_level, max_level;
2749 unsigned long flags_on, flags_off;
2750 time_t min_registered, max_registered;
2752 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2753 const char *nickmask;
2754 const char *hostmask;
2755 const char *handlemask;
2756 const char *emailmask;
2759 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2761 struct discrim_apply_info {
2762 struct nickserv_discrim *discrim;
2763 discrim_search_func func;
2764 struct userNode *source;
2765 unsigned int matched;
2768 static struct nickserv_discrim *
2769 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2772 struct nickserv_discrim *discrim;
2774 discrim = malloc(sizeof(*discrim));
2775 memset(discrim, 0, sizeof(*discrim));
2776 discrim->min_level = 0;
2777 discrim->max_level = ~0;
2778 discrim->limit = 50;
2779 discrim->min_registered = 0;
2780 discrim->max_registered = INT_MAX;
2781 discrim->lastseen = now;
2783 for (i=0; i<argc; i++) {
2784 if (i == argc - 1) {
2785 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2788 if (!irccasecmp(argv[i], "limit")) {
2789 discrim->limit = atoi(argv[++i]);
2790 } else if (!irccasecmp(argv[i], "flags")) {
2791 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2792 } else if (!irccasecmp(argv[i], "registered")) {
2793 const char *cmp = argv[++i];
2794 if (cmp[0] == '<') {
2795 if (cmp[1] == '=') {
2796 discrim->min_registered = now - ParseInterval(cmp+2);
2798 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2800 } else if (cmp[0] == '=') {
2801 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2802 } else if (cmp[0] == '>') {
2803 if (cmp[1] == '=') {
2804 discrim->max_registered = now - ParseInterval(cmp+2);
2806 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2809 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2811 } else if (!irccasecmp(argv[i], "seen")) {
2812 discrim->lastseen = now - ParseInterval(argv[++i]);
2813 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2814 discrim->nickmask = argv[++i];
2815 } else if (!irccasecmp(argv[i], "hostmask")) {
2817 if (!irccasecmp(argv[i], "exact")) {
2818 if (i == argc - 1) {
2819 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2822 discrim->hostmask_type = EXACT;
2823 } else if (!irccasecmp(argv[i], "subset")) {
2824 if (i == argc - 1) {
2825 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2828 discrim->hostmask_type = SUBSET;
2829 } else if (!irccasecmp(argv[i], "superset")) {
2830 if (i == argc - 1) {
2831 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2834 discrim->hostmask_type = SUPERSET;
2835 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2836 if (i == argc - 1) {
2837 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2840 discrim->hostmask_type = LASTQUIT;
2843 discrim->hostmask_type = SUPERSET;
2845 discrim->hostmask = argv[++i];
2846 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2847 if (!irccasecmp(argv[++i], "*")) {
2848 discrim->handlemask = 0;
2850 discrim->handlemask = argv[i];
2852 } else if (!irccasecmp(argv[i], "email")) {
2853 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2854 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2856 } else if (!irccasecmp(argv[++i], "*")) {
2857 discrim->emailmask = 0;
2859 discrim->emailmask = argv[i];
2861 } else if (!irccasecmp(argv[i], "access")) {
2862 const char *cmp = argv[++i];
2863 if (cmp[0] == '<') {
2864 if (discrim->min_level == 0) discrim->min_level = 1;
2865 if (cmp[1] == '=') {
2866 discrim->max_level = strtoul(cmp+2, NULL, 0);
2868 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
2870 } else if (cmp[0] == '=') {
2871 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
2872 } else if (cmp[0] == '>') {
2873 if (cmp[1] == '=') {
2874 discrim->min_level = strtoul(cmp+2, NULL, 0);
2876 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
2879 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2882 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
2893 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
2895 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
2896 || (discrim->flags_off & hi->flags)
2897 || (discrim->min_registered > hi->registered)
2898 || (discrim->max_registered < hi->registered)
2899 || (discrim->lastseen < (hi->users?now:hi->lastseen))
2900 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
2901 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
2902 || (discrim->min_level > hi->opserv_level)
2903 || (discrim->max_level < hi->opserv_level)) {
2906 if (discrim->hostmask) {
2908 for (i=0; i<hi->masks->used; i++) {
2909 const char *mask = hi->masks->list[i];
2910 if ((discrim->hostmask_type == SUBSET)
2911 && (match_ircglobs(discrim->hostmask, mask))) break;
2912 else if ((discrim->hostmask_type == EXACT)
2913 && !irccasecmp(discrim->hostmask, mask)) break;
2914 else if ((discrim->hostmask_type == SUPERSET)
2915 && (match_ircglobs(mask, discrim->hostmask))) break;
2916 else if ((discrim->hostmask_type == LASTQUIT)
2917 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
2919 if (i==hi->masks->used) return 0;
2921 if (discrim->nickmask) {
2922 struct nick_info *nick = hi->nicks;
2924 if (match_ircglob(nick->nick, discrim->nickmask)) break;
2927 if (!nick) return 0;
2933 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
2935 dict_iterator_t it, next;
2936 unsigned int matched;
2938 for (it = dict_first(nickserv_handle_dict), matched = 0;
2939 it && (matched < discrim->limit);
2941 next = iter_next(it);
2942 if (nickserv_discrim_match(discrim, iter_data(it))) {
2943 dsf(source, iter_data(it));
2951 search_print_func(struct userNode *source, struct handle_info *match)
2953 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
2957 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
2962 search_unregister_func (struct userNode *source, struct handle_info *match)
2964 if (oper_has_access(source, nickserv, match->opserv_level, 0))
2965 nickserv_unregister_handle(match, source);
2969 nickserv_sort_accounts_by_access(const void *a, const void *b)
2971 const struct handle_info *hi_a = *(const struct handle_info**)a;
2972 const struct handle_info *hi_b = *(const struct handle_info**)b;
2973 if (hi_a->opserv_level != hi_b->opserv_level)
2974 return hi_b->opserv_level - hi_a->opserv_level;
2975 return irccasecmp(hi_a->handle, hi_b->handle);
2979 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
2981 struct handle_info_list hil;
2982 struct helpfile_table tbl;
2987 memset(&hil, 0, sizeof(hil));
2988 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2989 struct handle_info *hi = iter_data(it);
2990 if (hi->opserv_level)
2991 handle_info_list_append(&hil, hi);
2993 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
2994 tbl.length = hil.used + 1;
2996 tbl.flags = TABLE_NO_FREE;
2997 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
2998 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3001 for (ii = 0; ii < hil.used; ) {
3002 ary = malloc(tbl.width * sizeof(ary[0]));
3003 ary[0] = hil.list[ii]->handle;
3004 ary[1] = strtab(hil.list[ii]->opserv_level);
3005 tbl.contents[++ii] = ary;
3007 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3008 reply("MSG_MATCH_COUNT", hil.used);
3009 for (ii = 0; ii < hil.used; )
3010 free(tbl.contents[++ii]);
3015 static NICKSERV_FUNC(cmd_search)
3017 struct nickserv_discrim *discrim;
3018 discrim_search_func action;
3019 struct svccmd *subcmd;
3020 unsigned int matches;
3023 NICKSERV_MIN_PARMS(3);
3024 sprintf(buf, "search %s", argv[1]);
3025 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3026 if (!irccasecmp(argv[1], "print"))
3027 action = search_print_func;
3028 else if (!irccasecmp(argv[1], "count"))
3029 action = search_count_func;
3030 else if (!irccasecmp(argv[1], "unregister"))
3031 action = search_unregister_func;
3033 reply("NSMSG_INVALID_ACTION", argv[1]);
3037 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3040 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3044 if (action == search_print_func)
3045 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3046 else if (action == search_count_func)
3047 discrim->limit = INT_MAX;
3049 matches = nickserv_discrim_search(discrim, action, user);
3052 reply("MSG_MATCH_COUNT", matches);
3054 reply("MSG_NO_MATCHES");
3060 static MODCMD_FUNC(cmd_checkpass)
3062 struct handle_info *hi;
3064 NICKSERV_MIN_PARMS(3);
3065 if (!(hi = get_handle_info(argv[1]))) {
3066 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3069 if (checkpass(argv[2], hi->passwd))
3070 reply("CHECKPASS_YES");
3072 reply("CHECKPASS_NO");
3078 nickserv_db_read_handle(const char *handle, dict_t obj)
3081 struct string_list *masks, *slist;
3082 struct handle_info *hi;
3083 struct userNode *authed_users;
3084 unsigned long int id;
3088 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3089 id = str ? strtoul(str, NULL, 0) : 0;
3090 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3092 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3095 if ((hi = get_handle_info(handle))) {
3096 authed_users = hi->users;
3098 dict_remove(nickserv_handle_dict, hi->handle);
3100 authed_users = NULL;
3102 hi = register_handle(handle, str, id);
3104 hi->users = authed_users;
3105 while (authed_users) {
3106 authed_users->handle_info = hi;
3107 authed_users = authed_users->next_authed;
3110 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3111 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3112 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3113 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3114 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3115 hi->language = language_find(str ? str : "C");
3116 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3117 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3118 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3120 hi->infoline = strdup(str);
3121 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3122 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3123 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3124 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3125 /* We want to read the nicks even if disable_nicks is set. This is so
3126 * that we don't lose the nick data entirely. */
3127 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3129 for (ii=0; ii<slist->used; ii++)
3130 register_nick(slist->list[ii], hi);
3132 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3134 for (ii=0; str[ii]; ii++)
3135 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3137 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3138 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3139 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3140 hi->announcements = str ? str[0] : '?';
3141 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3142 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3143 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3144 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3145 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3147 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3149 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3150 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3152 nickserv_set_email_addr(hi, str);
3153 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3155 hi->epithet = strdup(str);
3156 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3158 const char *data, *type, *expires, *cookie_str;
3159 struct handle_cookie *cookie;
3161 cookie = calloc(1, sizeof(*cookie));
3162 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3163 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3164 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3165 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3166 if (!type || !expires || !cookie_str) {
3167 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3170 if (!irccasecmp(type, KEY_ACTIVATION))
3171 cookie->type = ACTIVATION;
3172 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3173 cookie->type = PASSWORD_CHANGE;
3174 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3175 cookie->type = EMAIL_CHANGE;
3176 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3177 cookie->type = ALLOWAUTH;
3179 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3182 cookie->expires = strtoul(expires, NULL, 0);
3183 if (cookie->expires < now)
3186 cookie->data = strdup(data);
3187 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3191 nickserv_bake_cookie(cookie);
3193 nickserv_free_cookie(cookie);
3198 nickserv_saxdb_read(dict_t db) {
3200 struct record_data *rd;
3202 for (it=dict_first(db); it; it=iter_next(it)) {
3204 nickserv_db_read_handle(iter_key(it), rd->d.object);
3209 static NICKSERV_FUNC(cmd_mergedb)
3211 struct timeval start, stop;
3214 NICKSERV_MIN_PARMS(2);
3215 gettimeofday(&start, NULL);
3216 if (!(db = parse_database(argv[1]))) {
3217 reply("NSMSG_DB_UNREADABLE", argv[1]);
3220 nickserv_saxdb_read(db);
3222 gettimeofday(&stop, NULL);
3223 stop.tv_sec -= start.tv_sec;
3224 stop.tv_usec -= start.tv_usec;
3225 if (stop.tv_usec < 0) {
3227 stop.tv_usec += 1000000;
3229 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3234 expire_handles(UNUSED_ARG(void *data))
3236 dict_iterator_t it, next;
3238 struct handle_info *hi;
3240 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3241 next = iter_next(it);
3243 if ((hi->opserv_level > 0)
3245 || HANDLE_FLAGGED(hi, FROZEN)
3246 || HANDLE_FLAGGED(hi, NODELETE)) {
3249 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3250 if ((now - hi->lastseen) > expiry) {
3251 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3252 nickserv_unregister_handle(hi, NULL);
3256 if (nickserv_conf.handle_expire_frequency)
3257 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3261 nickserv_load_dict(const char *fname)
3265 if (!(file = fopen(fname, "r"))) {
3266 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3269 while (!feof(file)) {
3270 fgets(line, sizeof(line), file);
3273 if (line[strlen(line)-1] == '\n')
3274 line[strlen(line)-1] = 0;
3275 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3278 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3281 static enum reclaim_action
3282 reclaim_action_from_string(const char *str) {
3284 return RECLAIM_NONE;
3285 else if (!irccasecmp(str, "warn"))
3286 return RECLAIM_WARN;
3287 else if (!irccasecmp(str, "svsnick"))
3288 return RECLAIM_SVSNICK;
3289 else if (!irccasecmp(str, "kill"))
3290 return RECLAIM_KILL;
3292 return RECLAIM_NONE;
3296 nickserv_conf_read(void)
3298 dict_t conf_node, child;
3302 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3303 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3306 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3308 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3309 if (nickserv_conf.valid_handle_regex_set) regfree(&nickserv_conf.valid_handle_regex);
3311 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3312 nickserv_conf.valid_handle_regex_set = !err;
3313 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3315 nickserv_conf.valid_handle_regex_set = 0;
3317 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3318 if (nickserv_conf.valid_nick_regex_set) regfree(&nickserv_conf.valid_nick_regex);
3320 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3321 nickserv_conf.valid_nick_regex_set = !err;
3322 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3324 nickserv_conf.valid_nick_regex_set = 0;
3326 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3328 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3329 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3330 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3331 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3332 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3333 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3334 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3335 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3336 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3337 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3338 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3339 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3340 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3341 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3342 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3343 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3344 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3345 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3346 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3347 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3348 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3350 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3351 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3352 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3354 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3355 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3356 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3358 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3359 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3360 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3361 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3362 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3363 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3364 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3365 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3366 if (!nickserv_conf.disable_nicks) {
3367 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3368 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3369 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3370 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3371 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3372 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3373 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3374 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3376 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3377 for (it=dict_first(child); it; it=iter_next(it)) {
3378 const char *key = iter_key(it), *value;
3382 if (!strncasecmp(key, "uc_", 3))
3383 flag = toupper(key[3]);
3384 else if (!strncasecmp(key, "lc_", 3))
3385 flag = tolower(key[3]);
3389 if ((pos = handle_inverse_flags[flag])) {
3390 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3391 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3394 if (nickserv_conf.weak_password_dict)
3395 dict_delete(nickserv_conf.weak_password_dict);
3396 nickserv_conf.weak_password_dict = dict_new();
3397 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3398 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3399 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3400 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3402 nickserv_load_dict(str);
3403 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3404 if (nickserv && str)
3405 NickChange(nickserv, str, 0);
3406 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3407 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3408 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3409 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3410 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3411 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3412 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3413 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3414 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3415 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3416 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3417 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3418 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3419 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3420 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3421 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3422 str = conf_get_data("server/network", RECDB_QSTRING);
3423 nickserv_conf.network_name = str ? str : "some IRC network";
3424 if (!nickserv_conf.auth_policer_params) {
3425 nickserv_conf.auth_policer_params = policer_params_new();
3426 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3427 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3429 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3430 for (it=dict_first(child); it; it=iter_next(it))
3431 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3435 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3436 char newnick[NICKLEN+1];
3445 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3447 case RECLAIM_SVSNICK:
3449 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3450 } while (GetUserH(newnick));
3451 irc_svsnick(nickserv, user, newnick);
3454 irc_kill(nickserv, user, "NSMSG_RECLAIM_KILL");
3460 nickserv_reclaim_p(void *data) {
3461 struct userNode *user = data;
3462 struct nick_info *ni = get_nick_info(user->nick);
3464 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3468 check_user_nick(struct userNode *user) {
3469 struct nick_info *ni;
3470 user->modes &= ~FLAGS_REGNICK;
3471 if (!(ni = get_nick_info(user->nick)))
3473 if (user->handle_info == ni->owner) {
3474 user->modes |= FLAGS_REGNICK;
3478 if (nickserv_conf.warn_nick_owned)
3479 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3480 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3482 if (nickserv_conf.auto_reclaim_delay)
3483 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3485 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3490 handle_new_user(struct userNode *user)
3492 return check_user_nick(user);
3496 handle_account(struct userNode *user, const char *stamp)
3498 struct handle_info *hi;
3500 #ifdef WITH_PROTOCOL_P10
3501 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3503 hi = dict_find(nickserv_id_dict, stamp, NULL);
3507 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3510 set_user_handle_info(user, hi, 0);
3512 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3517 handle_nick_change(struct userNode *user, const char *old_nick)
3519 struct handle_info *hi;
3521 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3522 dict_remove(nickserv_allow_auth_dict, old_nick);
3523 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3525 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3526 check_user_nick(user);
3530 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3532 dict_remove(nickserv_allow_auth_dict, user->nick);
3533 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3534 set_user_handle_info(user, NULL, 0);
3537 static struct modcmd *
3538 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3540 if (min_level > 0) {
3542 sprintf(buf, "%u", min_level);
3543 if (must_be_qualified) {
3544 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3546 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3548 } else if (min_level == 0) {
3549 if (must_be_qualified) {
3550 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3552 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3555 if (must_be_qualified) {
3556 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3558 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3564 nickserv_db_cleanup(void)
3566 unreg_del_user_func(nickserv_remove_user);
3567 userList_clean(&curr_helpers);
3568 policer_params_delete(nickserv_conf.auth_policer_params);
3569 dict_delete(nickserv_handle_dict);
3570 dict_delete(nickserv_nick_dict);
3571 dict_delete(nickserv_opt_dict);
3572 dict_delete(nickserv_allow_auth_dict);
3573 dict_delete(nickserv_email_dict);
3574 dict_delete(nickserv_id_dict);
3575 dict_delete(nickserv_conf.weak_password_dict);
3576 free(auth_func_list);
3577 free(unreg_func_list);
3579 free(allowauth_func_list);
3580 free(handle_merge_func_list);
3581 free(failpw_func_list);
3582 if (nickserv_conf.valid_handle_regex_set)
3583 regfree(&nickserv_conf.valid_handle_regex);
3584 if (nickserv_conf.valid_nick_regex_set)
3585 regfree(&nickserv_conf.valid_nick_regex);
3589 init_nickserv(const char *nick)
3592 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3593 reg_new_user_func(handle_new_user);
3594 reg_nick_change_func(handle_nick_change);
3595 reg_del_user_func(nickserv_remove_user);
3596 reg_account_func(handle_account);
3598 /* set up handle_inverse_flags */
3599 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3600 for (i=0; handle_flags[i]; i++) {
3601 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3602 flag_access_levels[i] = 0;
3605 conf_register_reload(nickserv_conf_read);
3606 nickserv_opt_dict = dict_new();
3607 nickserv_email_dict = dict_new();
3608 dict_set_free_keys(nickserv_email_dict, free);
3609 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3611 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3612 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3613 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3614 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3615 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3616 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3617 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3618 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3619 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3620 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3621 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3622 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3623 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3624 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3625 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3626 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3627 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3628 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3629 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3630 if (!nickserv_conf.disable_nicks) {
3631 /* nick management commands */
3632 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3633 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3634 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3635 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3636 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3637 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3639 if (nickserv_conf.email_enabled) {
3640 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3641 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3642 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3643 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3644 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3646 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3647 /* miscellaneous commands */
3648 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3649 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3650 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3651 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3652 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3654 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3655 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3656 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3657 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3658 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3659 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3660 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3661 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3662 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3663 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3664 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3665 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3666 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3667 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3668 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3670 nickserv_handle_dict = dict_new();
3671 dict_set_free_keys(nickserv_handle_dict, free);
3672 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3674 nickserv_id_dict = dict_new();
3675 dict_set_free_keys(nickserv_id_dict, free);
3677 nickserv_nick_dict = dict_new();
3678 dict_set_free_data(nickserv_nick_dict, free_nick_info);
3680 nickserv_allow_auth_dict = dict_new();
3682 userList_init(&curr_helpers);
3685 nickserv = AddService(nick, "Nick Services");
3686 nickserv_service = service_register(nickserv, 0);
3688 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3689 reg_exit_func(nickserv_db_cleanup);
3690 if(nickserv_conf.handle_expire_frequency)
3691 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3692 message_register_table(msgtab);