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 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 lang = hi->language ? hi->language : lang_C;
2297 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", lang->name);
2302 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2303 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2305 if ((user->handle_info->opserv_level < target->opserv_level)
2306 || ((user->handle_info->opserv_level == target->opserv_level)
2307 && (user->handle_info->opserv_level < 1000))) {
2308 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2311 if ((user->handle_info->opserv_level < new_level)
2312 || ((user->handle_info->opserv_level == new_level)
2313 && (user->handle_info->opserv_level < 1000))) {
2314 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2317 if (user->handle_info == target) {
2318 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2321 if (target->opserv_level == new_level)
2323 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2324 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2325 target->opserv_level = new_level;
2329 static OPTION_FUNC(opt_level)
2334 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2338 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2339 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2343 static OPTION_FUNC(opt_epithet)
2346 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2350 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2351 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2354 if ((epithet[0] == '*') && !epithet[1])
2357 hi->epithet = strdup(epithet);
2361 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2363 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2367 static NICKSERV_FUNC(cmd_reclaim)
2369 struct handle_info *hi;
2370 struct nick_info *ni;
2371 struct userNode *victim;
2373 NICKSERV_MIN_PARMS(2);
2374 hi = user->handle_info;
2375 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2377 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2380 if (ni->owner != user->handle_info) {
2381 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2384 victim = GetUserH(ni->nick);
2386 reply("MSG_NICK_UNKNOWN", ni->nick);
2389 if (victim == user) {
2390 reply("NSMSG_NICK_USER_YOU");
2393 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2394 switch (nickserv_conf.reclaim_action) {
2395 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2396 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2397 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2398 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2403 static NICKSERV_FUNC(cmd_unregnick)
2406 struct handle_info *hi;
2407 struct nick_info *ni;
2409 hi = user->handle_info;
2410 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2411 ni = dict_find(nickserv_nick_dict, nick, NULL);
2413 reply("NSMSG_UNKNOWN_NICK", nick);
2416 if (hi != ni->owner) {
2417 reply("NSMSG_NOT_YOUR_NICK", nick);
2420 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2425 static NICKSERV_FUNC(cmd_ounregnick)
2427 struct nick_info *ni;
2429 NICKSERV_MIN_PARMS(2);
2430 if (!(ni = get_nick_info(argv[1]))) {
2431 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2434 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2435 reply("MSG_USER_OUTRANKED", ni->nick);
2438 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2443 static NICKSERV_FUNC(cmd_unregister)
2445 struct handle_info *hi;
2448 NICKSERV_MIN_PARMS(2);
2449 hi = user->handle_info;
2452 if (checkpass(passwd, hi->passwd)) {
2453 nickserv_unregister_handle(hi, user);
2456 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2457 reply("NSMSG_PASSWORD_INVALID");
2462 static NICKSERV_FUNC(cmd_ounregister)
2464 struct handle_info *hi;
2466 NICKSERV_MIN_PARMS(2);
2467 if (!(hi = get_victim_oper(user, argv[1])))
2469 nickserv_unregister_handle(hi, user);
2473 static NICKSERV_FUNC(cmd_status)
2475 if (nickserv_conf.disable_nicks) {
2476 reply("NSMSG_GLOBAL_STATS_NONICK",
2477 dict_size(nickserv_handle_dict));
2479 if (user->handle_info) {
2481 struct nick_info *ni;
2482 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2483 reply("NSMSG_HANDLE_STATS", cnt);
2485 reply("NSMSG_HANDLE_NONE");
2487 reply("NSMSG_GLOBAL_STATS",
2488 dict_size(nickserv_handle_dict),
2489 dict_size(nickserv_nick_dict));
2494 static NICKSERV_FUNC(cmd_ghost)
2496 struct userNode *target;
2497 char reason[MAXLEN];
2499 NICKSERV_MIN_PARMS(2);
2500 if (!(target = GetUserH(argv[1]))) {
2501 reply("MSG_NICK_UNKNOWN", argv[1]);
2504 if (target == user) {
2505 reply("NSMSG_CANNOT_GHOST_SELF");
2508 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2509 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2512 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2513 DelUser(target, nickserv, 1, reason);
2514 reply("NSMSG_GHOST_KILLED", argv[1]);
2518 static NICKSERV_FUNC(cmd_vacation)
2520 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2521 reply("NSMSG_ON_VACATION");
2526 nickserv_saxdb_write(struct saxdb_context *ctx) {
2528 struct handle_info *hi;
2531 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2533 #ifdef WITH_PROTOCOL_BAHAMUT
2536 saxdb_start_record(ctx, iter_key(it), 0);
2537 if (hi->announcements != '?') {
2538 flags[0] = hi->announcements;
2540 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2543 struct handle_cookie *cookie = hi->cookie;
2546 switch (cookie->type) {
2547 case ACTIVATION: type = KEY_ACTIVATION; break;
2548 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2549 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2550 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2551 default: type = NULL; break;
2554 saxdb_start_record(ctx, KEY_COOKIE, 0);
2555 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2556 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2558 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2559 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2560 saxdb_end_record(ctx);
2564 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2566 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2570 for (ii=flen=0; handle_flags[ii]; ++ii)
2571 if (hi->flags & (1 << ii))
2572 flags[flen++] = handle_flags[ii];
2574 saxdb_write_string(ctx, KEY_FLAGS, flags);
2576 #ifdef WITH_PROTOCOL_BAHAMUT
2577 saxdb_write_int(ctx, KEY_ID, hi->id);
2580 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2581 if (hi->last_quit_host[0])
2582 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2583 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2584 if (hi->masks->used)
2585 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2587 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2589 struct string_list *slist;
2590 struct nick_info *ni;
2592 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2593 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2594 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2598 if (hi->opserv_level)
2599 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2600 if (hi->language && (hi->language != lang_C))
2601 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2602 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2603 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2604 if (hi->screen_width)
2605 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2606 if (hi->table_width)
2607 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2608 flags[0] = hi->userlist_style;
2610 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2611 saxdb_end_record(ctx);
2616 static handle_merge_func_t *handle_merge_func_list;
2617 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2620 reg_handle_merge_func(handle_merge_func_t func)
2622 if (handle_merge_func_used == handle_merge_func_size) {
2623 if (handle_merge_func_size) {
2624 handle_merge_func_size <<= 1;
2625 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2627 handle_merge_func_size = 8;
2628 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2631 handle_merge_func_list[handle_merge_func_used++] = func;
2634 static NICKSERV_FUNC(cmd_merge)
2636 struct handle_info *hi_from, *hi_to;
2637 struct userNode *last_user;
2638 struct userData *cList, *cListNext;
2639 unsigned int ii, jj, n;
2640 char buffer[MAXLEN];
2642 NICKSERV_MIN_PARMS(3);
2644 if (!(hi_from = get_victim_oper(user, argv[1])))
2646 if (!(hi_to = get_victim_oper(user, argv[2])))
2648 if (hi_to == hi_from) {
2649 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2653 for (n=0; n<handle_merge_func_used; n++)
2654 handle_merge_func_list[n](user, hi_to, hi_from);
2656 /* Append "from" handle's nicks to "to" handle's nick list. */
2658 struct nick_info *last_ni;
2659 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2660 last_ni->next = hi_from->nicks;
2662 while (hi_from->nicks) {
2663 hi_from->nicks->owner = hi_to;
2664 hi_from->nicks = hi_from->nicks->next;
2667 /* Merge the hostmasks. */
2668 for (ii=0; ii<hi_from->masks->used; ii++) {
2669 char *mask = hi_from->masks->list[ii];
2670 for (jj=0; jj<hi_to->masks->used; jj++)
2671 if (match_ircglobs(hi_to->masks->list[jj], mask))
2673 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2674 string_list_append(hi_to->masks, strdup(mask));
2677 /* Merge the lists of authed users. */
2679 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2680 last_user->next_authed = hi_from->users;
2682 hi_to->users = hi_from->users;
2684 /* Repoint the old "from" handle's users. */
2685 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2686 last_user->handle_info = hi_to;
2688 hi_from->users = NULL;
2690 /* Merge channel userlists. */
2691 for (cList=hi_from->channels; cList; cList=cListNext) {
2692 struct userData *cList2;
2693 cListNext = cList->u_next;
2694 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2695 if (cList->channel == cList2->channel)
2697 log_module(NS_LOG, LOG_DEBUG, "Merging %s->%s@%s: before %p->%p->%-p, %p->%p->%p",
2698 hi_from->handle, hi_to->handle, cList->channel->channel->name,
2699 cList->u_prev, cList, cList->u_next,
2700 (cList2?cList2->u_prev:0), cList2, (cList2?cList2->u_next:0));
2701 if (cList2 && (cList2->access >= cList->access)) {
2702 /* keep cList2 in hi_to; remove cList from hi_from */
2703 log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList);
2704 del_channel_user(cList, 1);
2707 /* remove the lower-ranking cList2 from hi_to */
2708 log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList2);
2709 del_channel_user(cList2, 1);
2711 /* cList needs to be moved from hi_from to hi_to */
2712 cList->handle = hi_to;
2713 /* Remove from linked list for hi_from */
2714 assert(!cList->u_prev);
2715 hi_from->channels = cList->u_next;
2717 cList->u_next->u_prev = cList->u_prev;
2718 /* Add to linked list for hi_to */
2719 cList->u_prev = NULL;
2720 cList->u_next = hi_to->channels;
2721 if (hi_to->channels)
2722 hi_to->channels->u_prev = cList;
2723 hi_to->channels = cList;
2724 log_module(NS_LOG, LOG_DEBUG, "Now %p->%p->%p",
2725 cList->u_prev, cList, cList->u_next);
2729 /* Do they get an OpServ level promotion? */
2730 if (hi_from->opserv_level > hi_to->opserv_level)
2731 hi_to->opserv_level = hi_from->opserv_level;
2733 /* What about last seen time? */
2734 if (hi_from->lastseen > hi_to->lastseen)
2735 hi_to->lastseen = hi_from->lastseen;
2737 /* Notify of success. */
2738 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2739 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2740 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2742 /* Unregister the "from" handle. */
2743 nickserv_unregister_handle(hi_from, NULL);
2748 struct nickserv_discrim {
2749 unsigned int limit, min_level, max_level;
2750 unsigned long flags_on, flags_off;
2751 time_t min_registered, max_registered;
2753 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2754 const char *nickmask;
2755 const char *hostmask;
2756 const char *handlemask;
2757 const char *emailmask;
2760 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2762 struct discrim_apply_info {
2763 struct nickserv_discrim *discrim;
2764 discrim_search_func func;
2765 struct userNode *source;
2766 unsigned int matched;
2769 static struct nickserv_discrim *
2770 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2773 struct nickserv_discrim *discrim;
2775 discrim = malloc(sizeof(*discrim));
2776 memset(discrim, 0, sizeof(*discrim));
2777 discrim->min_level = 0;
2778 discrim->max_level = ~0;
2779 discrim->limit = 50;
2780 discrim->min_registered = 0;
2781 discrim->max_registered = INT_MAX;
2782 discrim->lastseen = now;
2784 for (i=0; i<argc; i++) {
2785 if (i == argc - 1) {
2786 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2789 if (!irccasecmp(argv[i], "limit")) {
2790 discrim->limit = atoi(argv[++i]);
2791 } else if (!irccasecmp(argv[i], "flags")) {
2792 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2793 } else if (!irccasecmp(argv[i], "registered")) {
2794 const char *cmp = argv[++i];
2795 if (cmp[0] == '<') {
2796 if (cmp[1] == '=') {
2797 discrim->min_registered = now - ParseInterval(cmp+2);
2799 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2801 } else if (cmp[0] == '=') {
2802 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2803 } else if (cmp[0] == '>') {
2804 if (cmp[1] == '=') {
2805 discrim->max_registered = now - ParseInterval(cmp+2);
2807 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2810 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2812 } else if (!irccasecmp(argv[i], "seen")) {
2813 discrim->lastseen = now - ParseInterval(argv[++i]);
2814 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2815 discrim->nickmask = argv[++i];
2816 } else if (!irccasecmp(argv[i], "hostmask")) {
2818 if (!irccasecmp(argv[i], "exact")) {
2819 if (i == argc - 1) {
2820 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2823 discrim->hostmask_type = EXACT;
2824 } else if (!irccasecmp(argv[i], "subset")) {
2825 if (i == argc - 1) {
2826 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2829 discrim->hostmask_type = SUBSET;
2830 } else if (!irccasecmp(argv[i], "superset")) {
2831 if (i == argc - 1) {
2832 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2835 discrim->hostmask_type = SUPERSET;
2836 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2837 if (i == argc - 1) {
2838 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2841 discrim->hostmask_type = LASTQUIT;
2844 discrim->hostmask_type = SUPERSET;
2846 discrim->hostmask = argv[++i];
2847 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2848 if (!irccasecmp(argv[++i], "*")) {
2849 discrim->handlemask = 0;
2851 discrim->handlemask = argv[i];
2853 } else if (!irccasecmp(argv[i], "email")) {
2854 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2855 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2857 } else if (!irccasecmp(argv[++i], "*")) {
2858 discrim->emailmask = 0;
2860 discrim->emailmask = argv[i];
2862 } else if (!irccasecmp(argv[i], "access")) {
2863 const char *cmp = argv[++i];
2864 if (cmp[0] == '<') {
2865 if (discrim->min_level == 0) discrim->min_level = 1;
2866 if (cmp[1] == '=') {
2867 discrim->max_level = strtoul(cmp+2, NULL, 0);
2869 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
2871 } else if (cmp[0] == '=') {
2872 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
2873 } else if (cmp[0] == '>') {
2874 if (cmp[1] == '=') {
2875 discrim->min_level = strtoul(cmp+2, NULL, 0);
2877 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
2880 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2883 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
2894 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
2896 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
2897 || (discrim->flags_off & hi->flags)
2898 || (discrim->min_registered > hi->registered)
2899 || (discrim->max_registered < hi->registered)
2900 || (discrim->lastseen < (hi->users?now:hi->lastseen))
2901 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
2902 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
2903 || (discrim->min_level > hi->opserv_level)
2904 || (discrim->max_level < hi->opserv_level)) {
2907 if (discrim->hostmask) {
2909 for (i=0; i<hi->masks->used; i++) {
2910 const char *mask = hi->masks->list[i];
2911 if ((discrim->hostmask_type == SUBSET)
2912 && (match_ircglobs(discrim->hostmask, mask))) break;
2913 else if ((discrim->hostmask_type == EXACT)
2914 && !irccasecmp(discrim->hostmask, mask)) break;
2915 else if ((discrim->hostmask_type == SUPERSET)
2916 && (match_ircglobs(mask, discrim->hostmask))) break;
2917 else if ((discrim->hostmask_type == LASTQUIT)
2918 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
2920 if (i==hi->masks->used) return 0;
2922 if (discrim->nickmask) {
2923 struct nick_info *nick = hi->nicks;
2925 if (match_ircglob(nick->nick, discrim->nickmask)) break;
2928 if (!nick) return 0;
2934 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
2936 dict_iterator_t it, next;
2937 unsigned int matched;
2939 for (it = dict_first(nickserv_handle_dict), matched = 0;
2940 it && (matched < discrim->limit);
2942 next = iter_next(it);
2943 if (nickserv_discrim_match(discrim, iter_data(it))) {
2944 dsf(source, iter_data(it));
2952 search_print_func(struct userNode *source, struct handle_info *match)
2954 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
2958 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
2963 search_unregister_func (struct userNode *source, struct handle_info *match)
2965 if (oper_has_access(source, nickserv, match->opserv_level, 0))
2966 nickserv_unregister_handle(match, source);
2970 nickserv_sort_accounts_by_access(const void *a, const void *b)
2972 const struct handle_info *hi_a = *(const struct handle_info**)a;
2973 const struct handle_info *hi_b = *(const struct handle_info**)b;
2974 if (hi_a->opserv_level != hi_b->opserv_level)
2975 return hi_b->opserv_level - hi_a->opserv_level;
2976 return irccasecmp(hi_a->handle, hi_b->handle);
2980 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
2982 struct handle_info_list hil;
2983 struct helpfile_table tbl;
2988 memset(&hil, 0, sizeof(hil));
2989 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2990 struct handle_info *hi = iter_data(it);
2991 if (hi->opserv_level)
2992 handle_info_list_append(&hil, hi);
2994 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
2995 tbl.length = hil.used + 1;
2997 tbl.flags = TABLE_NO_FREE;
2998 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
2999 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3002 for (ii = 0; ii < hil.used; ) {
3003 ary = malloc(tbl.width * sizeof(ary[0]));
3004 ary[0] = hil.list[ii]->handle;
3005 ary[1] = strtab(hil.list[ii]->opserv_level);
3006 tbl.contents[++ii] = ary;
3008 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3009 reply("MSG_MATCH_COUNT", hil.used);
3010 for (ii = 0; ii < hil.used; )
3011 free(tbl.contents[++ii]);
3016 static NICKSERV_FUNC(cmd_search)
3018 struct nickserv_discrim *discrim;
3019 discrim_search_func action;
3020 struct svccmd *subcmd;
3021 unsigned int matches;
3024 NICKSERV_MIN_PARMS(3);
3025 sprintf(buf, "search %s", argv[1]);
3026 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3027 if (!irccasecmp(argv[1], "print"))
3028 action = search_print_func;
3029 else if (!irccasecmp(argv[1], "count"))
3030 action = search_count_func;
3031 else if (!irccasecmp(argv[1], "unregister"))
3032 action = search_unregister_func;
3034 reply("NSMSG_INVALID_ACTION", argv[1]);
3038 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3041 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3045 if (action == search_print_func)
3046 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3047 else if (action == search_count_func)
3048 discrim->limit = INT_MAX;
3050 matches = nickserv_discrim_search(discrim, action, user);
3053 reply("MSG_MATCH_COUNT", matches);
3055 reply("MSG_NO_MATCHES");
3061 static MODCMD_FUNC(cmd_checkpass)
3063 struct handle_info *hi;
3065 NICKSERV_MIN_PARMS(3);
3066 if (!(hi = get_handle_info(argv[1]))) {
3067 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3070 if (checkpass(argv[2], hi->passwd))
3071 reply("CHECKPASS_YES");
3073 reply("CHECKPASS_NO");
3079 nickserv_db_read_handle(const char *handle, dict_t obj)
3082 struct string_list *masks, *slist;
3083 struct handle_info *hi;
3084 struct userNode *authed_users;
3085 unsigned long int id;
3089 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3090 id = str ? strtoul(str, NULL, 0) : 0;
3091 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3093 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3096 if ((hi = get_handle_info(handle))) {
3097 authed_users = hi->users;
3099 dict_remove(nickserv_handle_dict, hi->handle);
3101 authed_users = NULL;
3103 hi = register_handle(handle, str, id);
3105 hi->users = authed_users;
3106 while (authed_users) {
3107 authed_users->handle_info = hi;
3108 authed_users = authed_users->next_authed;
3111 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3112 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3113 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3114 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3115 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3116 hi->language = language_find(str ? str : "C");
3117 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3118 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3119 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3120 if (str) 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);
3146 if (!str) str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3147 if (str) safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3148 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3149 if (str) nickserv_set_email_addr(hi, str);
3150 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3151 if (str) hi->epithet = strdup(str);
3152 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3154 const char *data, *type, *expires, *cookie_str;
3155 struct handle_cookie *cookie;
3157 cookie = calloc(1, sizeof(*cookie));
3158 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3159 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3160 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3161 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3162 if (!type || !expires || !cookie_str) {
3163 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3166 if (!irccasecmp(type, KEY_ACTIVATION))
3167 cookie->type = ACTIVATION;
3168 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3169 cookie->type = PASSWORD_CHANGE;
3170 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3171 cookie->type = EMAIL_CHANGE;
3172 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3173 cookie->type = ALLOWAUTH;
3175 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3178 cookie->expires = strtoul(expires, NULL, 0);
3179 if (cookie->expires < now)
3182 cookie->data = strdup(data);
3183 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3187 nickserv_bake_cookie(cookie);
3189 nickserv_free_cookie(cookie);
3194 nickserv_saxdb_read(dict_t db) {
3196 struct record_data *rd;
3198 for (it=dict_first(db); it; it=iter_next(it)) {
3200 nickserv_db_read_handle(iter_key(it), rd->d.object);
3205 static NICKSERV_FUNC(cmd_mergedb)
3207 struct timeval start, stop;
3210 NICKSERV_MIN_PARMS(2);
3211 gettimeofday(&start, NULL);
3212 if (!(db = parse_database(argv[1]))) {
3213 reply("NSMSG_DB_UNREADABLE", argv[1]);
3216 nickserv_saxdb_read(db);
3218 gettimeofday(&stop, NULL);
3219 stop.tv_sec -= start.tv_sec;
3220 stop.tv_usec -= start.tv_usec;
3221 if (stop.tv_usec < 0) {
3223 stop.tv_usec += 1000000;
3225 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3230 expire_handles(UNUSED_ARG(void *data))
3232 dict_iterator_t it, next;
3234 struct handle_info *hi;
3236 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3237 next = iter_next(it);
3239 if ((hi->opserv_level > 0)
3241 || HANDLE_FLAGGED(hi, FROZEN)
3242 || HANDLE_FLAGGED(hi, NODELETE)) {
3245 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3246 if ((now - hi->lastseen) > expiry) {
3247 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3248 nickserv_unregister_handle(hi, NULL);
3252 if (nickserv_conf.handle_expire_frequency)
3253 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3257 nickserv_load_dict(const char *fname)
3261 if (!(file = fopen(fname, "r"))) {
3262 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3265 while (!feof(file)) {
3266 fgets(line, sizeof(line), file);
3269 if (line[strlen(line)-1] == '\n')
3270 line[strlen(line)-1] = 0;
3271 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3274 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3277 static enum reclaim_action
3278 reclaim_action_from_string(const char *str) {
3280 return RECLAIM_NONE;
3281 else if (!irccasecmp(str, "warn"))
3282 return RECLAIM_WARN;
3283 else if (!irccasecmp(str, "svsnick"))
3284 return RECLAIM_SVSNICK;
3285 else if (!irccasecmp(str, "kill"))
3286 return RECLAIM_KILL;
3288 return RECLAIM_NONE;
3292 nickserv_conf_read(void)
3294 dict_t conf_node, child;
3298 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3299 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3302 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3303 if (!str) str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3304 if (nickserv_conf.valid_handle_regex_set) regfree(&nickserv_conf.valid_handle_regex);
3306 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3307 nickserv_conf.valid_handle_regex_set = !err;
3308 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3310 nickserv_conf.valid_handle_regex_set = 0;
3312 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3313 if (nickserv_conf.valid_nick_regex_set) regfree(&nickserv_conf.valid_nick_regex);
3315 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3316 nickserv_conf.valid_nick_regex_set = !err;
3317 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3319 nickserv_conf.valid_nick_regex_set = 0;
3321 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3322 if (!str) str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3323 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3324 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3325 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3326 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3327 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3328 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3329 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3330 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3331 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3332 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3333 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3334 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3335 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3336 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3337 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3338 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3339 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3340 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3341 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3342 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3343 if (!str) str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3344 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3345 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3346 if (!str) str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3347 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3348 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3349 if (!str) str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3350 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3351 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3352 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3353 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3354 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3355 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3356 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3357 if (!nickserv_conf.disable_nicks) {
3358 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3359 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3360 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3361 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3362 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3363 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3364 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3365 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3367 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3368 for (it=dict_first(child); it; it=iter_next(it)) {
3369 const char *key = iter_key(it), *value;
3373 if (!strncasecmp(key, "uc_", 3))
3374 flag = toupper(key[3]);
3375 else if (!strncasecmp(key, "lc_", 3))
3376 flag = tolower(key[3]);
3380 if ((pos = handle_inverse_flags[flag])) {
3381 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3382 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3385 if (nickserv_conf.weak_password_dict)
3386 dict_delete(nickserv_conf.weak_password_dict);
3387 nickserv_conf.weak_password_dict = dict_new();
3388 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3389 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3390 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3391 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3393 nickserv_load_dict(str);
3394 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3395 if (nickserv && str)
3396 NickChange(nickserv, str, 0);
3397 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3398 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3399 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3400 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3401 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3402 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3403 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3404 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3405 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3406 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3407 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3408 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3409 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3410 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3411 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3412 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3413 str = conf_get_data("server/network", RECDB_QSTRING);
3414 nickserv_conf.network_name = str ? str : "some IRC network";
3415 if (!nickserv_conf.auth_policer_params) {
3416 nickserv_conf.auth_policer_params = policer_params_new();
3417 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3418 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3420 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3421 for (it=dict_first(child); it; it=iter_next(it))
3422 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3426 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3427 char newnick[NICKLEN+1];
3436 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3438 case RECLAIM_SVSNICK:
3440 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3441 } while (GetUserH(newnick));
3442 irc_svsnick(nickserv, user, newnick);
3445 irc_kill(nickserv, user, "NSMSG_RECLAIM_KILL");
3451 nickserv_reclaim_p(void *data) {
3452 struct userNode *user = data;
3453 struct nick_info *ni = get_nick_info(user->nick);
3455 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3459 check_user_nick(struct userNode *user) {
3460 struct nick_info *ni;
3461 user->modes &= ~FLAGS_REGNICK;
3462 if (!(ni = get_nick_info(user->nick)))
3464 if (user->handle_info == ni->owner) {
3465 user->modes |= FLAGS_REGNICK;
3469 if (nickserv_conf.warn_nick_owned)
3470 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3471 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3473 if (nickserv_conf.auto_reclaim_delay)
3474 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3476 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3481 handle_new_user(struct userNode *user)
3483 return check_user_nick(user);
3487 handle_account(struct userNode *user, const char *stamp)
3489 struct handle_info *hi;
3491 #ifdef WITH_PROTOCOL_P10
3492 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3494 hi = dict_find(nickserv_id_dict, stamp, NULL);
3498 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3501 set_user_handle_info(user, hi, 0);
3503 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3508 handle_nick_change(struct userNode *user, const char *old_nick)
3510 struct handle_info *hi;
3512 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3513 dict_remove(nickserv_allow_auth_dict, old_nick);
3514 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3516 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3517 check_user_nick(user);
3521 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3523 dict_remove(nickserv_allow_auth_dict, user->nick);
3524 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3525 set_user_handle_info(user, NULL, 0);
3528 static struct modcmd *
3529 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3531 if (min_level > 0) {
3533 sprintf(buf, "%u", min_level);
3534 if (must_be_qualified) {
3535 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3537 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3539 } else if (min_level == 0) {
3540 if (must_be_qualified) {
3541 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3543 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3546 if (must_be_qualified) {
3547 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3549 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3555 nickserv_db_cleanup(void)
3557 unreg_del_user_func(nickserv_remove_user);
3558 userList_clean(&curr_helpers);
3559 policer_params_delete(nickserv_conf.auth_policer_params);
3560 dict_delete(nickserv_handle_dict);
3561 dict_delete(nickserv_nick_dict);
3562 dict_delete(nickserv_opt_dict);
3563 dict_delete(nickserv_allow_auth_dict);
3564 dict_delete(nickserv_email_dict);
3565 dict_delete(nickserv_id_dict);
3566 dict_delete(nickserv_conf.weak_password_dict);
3567 free(auth_func_list);
3568 free(unreg_func_list);
3570 free(allowauth_func_list);
3571 free(handle_merge_func_list);
3572 free(failpw_func_list);
3573 if (nickserv_conf.valid_handle_regex_set)
3574 regfree(&nickserv_conf.valid_handle_regex);
3575 if (nickserv_conf.valid_nick_regex_set)
3576 regfree(&nickserv_conf.valid_nick_regex);
3580 init_nickserv(const char *nick)
3583 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3584 reg_new_user_func(handle_new_user);
3585 reg_nick_change_func(handle_nick_change);
3586 reg_del_user_func(nickserv_remove_user);
3587 reg_account_func(handle_account);
3589 /* set up handle_inverse_flags */
3590 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3591 for (i=0; handle_flags[i]; i++) {
3592 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3593 flag_access_levels[i] = 0;
3596 conf_register_reload(nickserv_conf_read);
3597 nickserv_opt_dict = dict_new();
3598 nickserv_email_dict = dict_new();
3599 dict_set_free_keys(nickserv_email_dict, free);
3600 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3602 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3603 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3604 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3605 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3606 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3607 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3608 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3609 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3610 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3611 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3612 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3613 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3614 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3615 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3616 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3617 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3618 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3619 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3620 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3621 if (!nickserv_conf.disable_nicks) {
3622 /* nick management commands */
3623 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3624 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3625 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3626 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3627 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3628 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3630 if (nickserv_conf.email_enabled) {
3631 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3632 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3633 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3634 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3635 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3637 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3638 /* miscellaneous commands */
3639 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3640 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3641 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3642 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3643 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3645 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3646 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3647 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3648 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3649 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3650 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3651 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3652 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3653 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3654 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3655 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3656 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3657 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3658 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3659 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3661 nickserv_handle_dict = dict_new();
3662 dict_set_free_keys(nickserv_handle_dict, free);
3663 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3665 nickserv_id_dict = dict_new();
3666 dict_set_free_keys(nickserv_id_dict, free);
3668 nickserv_nick_dict = dict_new();
3669 dict_set_free_data(nickserv_nick_dict, free_nick_info);
3671 nickserv_allow_auth_dict = dict_new();
3673 userList_init(&curr_helpers);
3676 nickserv = AddService(nick, "Nick Services");
3677 nickserv_service = service_register(nickserv, 0);
3679 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3680 reg_exit_func(nickserv_db_cleanup);
3681 if(nickserv_conf.handle_expire_frequency)
3682 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3683 message_register_table(msgtab);