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_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
161 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
162 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
163 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
164 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
165 { "NSMSG_USE_AUTHCOOKIE", "Your hostmask is not valid for account $b%1$s$b. Please use the $bauthcookie$b command to grant yourself access. (/msg $S authcookie %1$s)" },
166 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
167 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
168 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
169 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
170 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
171 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
172 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
173 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
174 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
175 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
176 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
177 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
178 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
179 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
180 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
181 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
182 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
183 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
184 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
185 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
186 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
187 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
188 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
189 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
190 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
191 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
192 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
193 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
194 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
195 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
196 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
197 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
198 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
199 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
200 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
201 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
202 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
203 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
204 { "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)." },
205 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
206 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
207 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
208 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
209 { "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." },
210 { "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." },
211 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
212 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
213 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
214 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
215 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
216 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
217 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
218 { "NSMSG_PASS_SUCCESS", "Password changed." },
219 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
220 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
221 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
222 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
223 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
224 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
225 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
226 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
227 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
228 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
229 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
230 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
231 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
232 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
233 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
234 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
235 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
236 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
237 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
238 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
239 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
240 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
241 { "NSMSG_NO_ACCESS", "Access denied." },
242 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
243 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
244 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
245 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
246 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
247 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
248 { "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." },
249 { "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." },
250 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
251 { "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." },
252 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
253 { "NSMSG_SEARCH_MATCH", "Match: %s" },
254 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
255 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
256 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
257 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
258 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
259 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
260 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
261 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
262 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
263 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
264 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
265 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
266 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
267 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
268 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
269 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
270 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
271 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
272 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
273 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
274 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
275 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
276 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
277 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
278 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
279 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
280 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
281 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
282 { "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\nThis command is only used once to complete your account registration, and never again. Once you have run this command, you will need to authenticate everytime you reconnect to the network. To do this, you will have to type this command every time you reconnect:\n /msg %3$s@%4$s AUTH %5$s your-password\n Please remember to fill in 'your-password' with the actual password you gave to us when you registered.\n\nIf you did NOT request this account, you do not need to do anything. Please contact the %1$s staff if you have questions, and be sure to check our website." },
283 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
284 { "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." },
285 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
286 { "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." },
287 { "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." },
288 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
289 { "NSEMAIL_EMAIL_VERIFY_BODY", "This email has been sent to verify that this address belongs to the same person as %5$s on %1$s. Your cookie is %2$s.\nTo verify your address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request this email address to be associated with this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
290 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
291 { "NSEMAIL_ALLOWAUTH_BODY", "This email has been sent to let you authenticate (auth) to account %5$s on %1$s. Your cookie is %2$s.\nTo auth to that account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request this authorization, you do not need to do anything. Please contact the %1$s staff if you have questions." },
292 { "CHECKPASS_YES", "Yes." },
293 { "CHECKPASS_NO", "No." },
297 enum reclaim_action {
303 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
304 static void nickserv_reclaim_p(void *data);
307 unsigned int disable_nicks : 1;
308 unsigned int valid_handle_regex_set : 1;
309 unsigned int valid_nick_regex_set : 1;
310 unsigned int autogag_enabled : 1;
311 unsigned int email_enabled : 1;
312 unsigned int email_required : 1;
313 unsigned int default_hostmask : 1;
314 unsigned int warn_nick_owned : 1;
315 unsigned int warn_clone_auth : 1;
316 unsigned long nicks_per_handle;
317 unsigned long password_min_length;
318 unsigned long password_min_digits;
319 unsigned long password_min_upper;
320 unsigned long password_min_lower;
321 unsigned long db_backup_frequency;
322 unsigned long handle_expire_frequency;
323 unsigned long autogag_duration;
324 unsigned long email_visible_level;
325 unsigned long cookie_timeout;
326 unsigned long handle_expire_delay;
327 unsigned long nochan_handle_expire_delay;
328 unsigned long modoper_level;
329 unsigned long set_epithet_level;
330 unsigned long handles_per_email;
331 unsigned long email_search_level;
332 const char *network_name;
333 const char *titlehost_suffix;
334 regex_t valid_handle_regex;
335 regex_t valid_nick_regex;
336 dict_t weak_password_dict;
337 struct policer_params *auth_policer_params;
338 enum reclaim_action reclaim_action;
339 enum reclaim_action auto_reclaim_action;
340 unsigned long auto_reclaim_delay;
341 unsigned char default_maxlogins;
342 unsigned char hard_maxlogins;
345 /* We have 2^32 unique account IDs to use. */
346 unsigned long int highest_id = 0;
349 canonicalize_hostmask(char *mask)
351 char *out = mask, *temp;
352 if ((temp = strchr(mask, '!'))) {
354 while (*temp) *out++ = *temp++;
360 static struct handle_info *
361 register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
363 struct handle_info *hi;
365 #ifdef WITH_PROTOCOL_BAHAMUT
366 char id_base64[IDLEN + 1];
369 /* Assign a unique account ID to the account; note that 0 is
370 an invalid account ID. 1 is therefore the first account ID. */
372 id = 1 + highest_id++;
374 /* Note: highest_id is and must always be the highest ID. */
375 if(id > highest_id) {
379 inttobase64(id_base64, id, IDLEN);
381 /* Make sure an account with the same ID doesn't exist. If a
382 duplicate is found, log some details and assign a new one.
383 This should be impossible, but it never hurts to expect it. */
384 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
385 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
391 hi = calloc(1, sizeof(*hi));
392 hi->userlist_style = HI_DEFAULT_STYLE;
393 hi->announcements = '?';
394 hi->handle = strdup(handle);
395 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
397 dict_insert(nickserv_handle_dict, hi->handle, hi);
399 #ifdef WITH_PROTOCOL_BAHAMUT
401 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
408 register_nick(const char *nick, struct handle_info *owner)
410 struct nick_info *ni;
411 ni = malloc(sizeof(struct nick_info));
412 safestrncpy(ni->nick, nick, sizeof(ni->nick));
414 ni->next = owner->nicks;
416 dict_insert(nickserv_nick_dict, ni->nick, ni);
420 free_nick_info(void *vni)
422 struct nick_info *ni = vni;
427 delete_nick(struct nick_info *ni)
429 struct nick_info *last, *next;
430 struct userNode *user;
431 /* Check to see if we should mark a user as unregistered. */
432 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
433 user->modes &= ~FLAGS_REGNICK;
436 /* Remove ni from the nick_info linked list. */
437 if (ni == ni->owner->nicks) {
438 ni->owner->nicks = ni->next;
440 last = ni->owner->nicks;
446 last->next = next->next;
448 dict_remove(nickserv_nick_dict, ni->nick);
451 static unreg_func_t *unreg_func_list;
452 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
455 reg_unreg_func(unreg_func_t func)
457 if (unreg_func_used == unreg_func_size) {
458 if (unreg_func_size) {
459 unreg_func_size <<= 1;
460 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
463 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
466 unreg_func_list[unreg_func_used++] = func;
470 nickserv_free_cookie(void *data)
472 struct handle_cookie *cookie = data;
473 if (cookie->hi) cookie->hi->cookie = NULL;
474 if (cookie->data) free(cookie->data);
479 free_handle_info(void *vhi)
481 struct handle_info *hi = vhi;
483 #ifdef WITH_PROTOCOL_BAHAMUT
486 inttobase64(id, hi->id, IDLEN);
487 dict_remove(nickserv_id_dict, id);
490 free_string_list(hi->masks);
494 delete_nick(hi->nicks);
498 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
499 nickserv_free_cookie(hi->cookie);
501 if (hi->email_addr) {
502 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
503 handle_info_list_remove(hil, hi);
505 dict_remove(nickserv_email_dict, hi->email_addr);
510 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
513 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
517 for (n=0; n<unreg_func_used; n++)
518 unreg_func_list[n](notify, hi);
520 set_user_handle_info(hi->users, NULL, 0);
522 if (nickserv_conf.disable_nicks)
523 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
525 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
527 dict_remove(nickserv_handle_dict, hi->handle);
531 get_handle_info(const char *handle)
533 return dict_find(nickserv_handle_dict, handle, 0);
537 get_nick_info(const char *nick)
539 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
543 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
548 for (nn=0; nn<channel->members.used; ++nn) {
549 mn = channel->members.list[nn];
550 if ((mn->user != except) && (mn->user->handle_info == handle))
557 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
558 if (!user->handle_info) {
560 send_message(user, bot, "MSG_AUTHENTICATE");
564 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
566 send_message(user, bot, "NSMSG_NO_ACCESS");
570 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
572 send_message(user, bot, "MSG_OPER_SUSPENDED");
576 if (user->handle_info->opserv_level < min_level) {
578 send_message(user, bot, "NSMSG_NO_ACCESS");
586 is_valid_handle(const char *handle)
588 struct userNode *user;
589 /* cant register a juped nick/service nick as handle, to prevent confusion */
590 user = GetUserH(handle);
591 if (user && IsLocal(user))
593 /* check against maximum length */
594 if (strlen(handle) > NICKSERV_HANDLE_LEN)
596 /* for consistency, only allow account names that could be nicks */
597 if (!is_valid_nick(handle))
599 /* disallow account names that look like bad words */
600 if (opserv_bad_channel(handle))
602 /* test either regex or containing all valid chars */
603 if (nickserv_conf.valid_handle_regex_set) {
604 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
607 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
608 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
612 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
617 is_registerable_nick(const char *nick)
619 /* make sure it could be used as an account name */
620 if (!is_valid_handle(nick))
623 if (strlen(nick) > NICKLEN)
625 /* test either regex or as valid handle */
626 if (nickserv_conf.valid_nick_regex_set) {
627 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
630 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
631 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
639 is_valid_email_addr(const char *email)
641 return strchr(email, '@') != NULL;
645 visible_email_addr(struct userNode *user, struct handle_info *hi)
647 if (hi->email_addr) {
648 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
649 return hi->email_addr;
659 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
661 struct handle_info *hi;
662 struct userNode *target;
666 if (!(hi = get_handle_info(++name))) {
667 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
672 if (!(target = GetUserH(name))) {
673 send_message(user, service, "MSG_NICK_UNKNOWN", name);
676 if (IsLocal(target)) {
677 if (IsService(target))
678 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
680 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
683 if (!(hi = target->handle_info)) {
684 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
692 oper_outranks(struct userNode *user, struct handle_info *hi) {
693 if (user->handle_info->opserv_level > hi->opserv_level)
695 if (user->handle_info->opserv_level == hi->opserv_level) {
696 if ((user->handle_info->opserv_level == 1000)
697 || (user->handle_info == hi)
698 || ((user->handle_info->opserv_level == 0)
699 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
700 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
704 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
708 static struct handle_info *
709 get_victim_oper(struct userNode *user, const char *target)
711 struct handle_info *hi;
712 if (!(hi = smart_get_handle_info(nickserv, user, target)))
714 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
715 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
718 return oper_outranks(user, hi) ? hi : NULL;
722 valid_user_for(struct userNode *user, struct handle_info *hi)
726 /* If no hostmasks on the account, allow it. */
727 if (!hi->masks->used)
729 /* If any hostmask matches, allow it. */
730 for (ii=0; ii<hi->masks->used; ii++)
731 if (user_matches_glob(user, hi->masks->list[ii], 0))
733 /* If they are allowauthed to this account, allow it (removing the aa). */
734 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
735 dict_remove(nickserv_allow_auth_dict, user->nick);
738 /* The user is not allowed to use this account. */
743 is_secure_password(const char *handle, const char *pass, struct userNode *user)
746 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
748 if (len < nickserv_conf.password_min_length) {
750 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
753 if (!irccasecmp(pass, handle)) {
755 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
758 dict_find(nickserv_conf.weak_password_dict, pass, &i);
761 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
764 for (i=0; i<len; i++) {
765 if (isdigit(pass[i]))
767 if (isupper(pass[i]))
769 if (islower(pass[i]))
772 if ((cnt_lower < nickserv_conf.password_min_lower)
773 || (cnt_upper < nickserv_conf.password_min_upper)
774 || (cnt_digits < nickserv_conf.password_min_digits)) {
776 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
782 static auth_func_t *auth_func_list;
783 static unsigned int auth_func_size = 0, auth_func_used = 0;
786 reg_auth_func(auth_func_t func)
788 if (auth_func_used == auth_func_size) {
789 if (auth_func_size) {
790 auth_func_size <<= 1;
791 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
794 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
797 auth_func_list[auth_func_used++] = func;
800 static handle_rename_func_t *rf_list;
801 static unsigned int rf_list_size, rf_list_used;
804 reg_handle_rename_func(handle_rename_func_t func)
806 if (rf_list_used == rf_list_size) {
809 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
812 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
815 rf_list[rf_list_used++] = func;
819 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
822 struct handle_info *old_info;
824 /* This can happen if somebody uses COOKIE while authed, or if
825 * they re-auth to their current handle (which is silly, but users
827 if (user->handle_info == hi)
830 if (user->handle_info) {
831 struct userNode *other;
834 userList_remove(&curr_helpers, user);
836 /* remove from next_authed linked list */
837 if (user->handle_info->users == user) {
838 user->handle_info->users = user->next_authed;
840 for (other = user->handle_info->users;
841 other->next_authed != user;
842 other = other->next_authed) ;
843 other->next_authed = user->next_authed;
845 /* if nobody left on old handle, and they're not an oper, remove !god */
846 if (!user->handle_info->users && !user->handle_info->opserv_level)
847 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
848 /* record them as being last seen at this time */
849 user->handle_info->lastseen = now;
850 /* and record their hostmask */
851 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
853 old_info = user->handle_info;
854 user->handle_info = hi;
855 if (hi && !hi->users && !hi->opserv_level)
856 HANDLE_CLEAR_FLAG(hi, HELPING);
857 for (n=0; n<auth_func_used; n++)
858 auth_func_list[n](user, old_info);
860 struct nick_info *ni;
862 HANDLE_CLEAR_FLAG(hi, FROZEN);
863 if (nickserv_conf.warn_clone_auth) {
864 struct userNode *other;
865 for (other = hi->users; other; other = other->next_authed)
866 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
868 user->next_authed = hi->users;
872 userList_append(&curr_helpers, user);
875 #ifdef WITH_PROTOCOL_BAHAMUT
876 /* Stamp users with their account ID. */
878 inttobase64(id, hi->id, IDLEN);
879 #elif WITH_PROTOCOL_P10
880 /* Stamp users with their account name. */
881 char *id = hi->handle;
883 const char *id = "???";
885 if (!nickserv_conf.disable_nicks) {
886 struct nick_info *ni;
887 for (ni = hi->nicks; ni; ni = ni->next) {
888 if (!irccasecmp(user->nick, ni->nick)) {
889 user->modes |= FLAGS_REGNICK;
897 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
898 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
900 /* We cannot clear the user's account ID, unfortunately. */
901 user->next_authed = NULL;
905 static struct handle_info*
906 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
908 struct handle_info *hi;
909 struct nick_info *ni;
910 char crypted[MD5_CRYPT_LENGTH];
912 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
913 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
917 if (!is_secure_password(handle, passwd, user))
920 cryptpass(passwd, crypted);
921 hi = register_handle(handle, crypted, 0);
922 hi->masks = alloc_string_list(1);
924 hi->language = lang_C;
925 hi->registered = now;
927 hi->flags = HI_DEFAULT_FLAGS;
928 if (settee && !no_auth)
929 set_user_handle_info(settee, hi, 1);
932 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
933 else if (nickserv_conf.disable_nicks)
934 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
935 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
936 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
938 register_nick(user->nick, hi);
939 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
941 if (settee && (user != settee))
942 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
947 nickserv_bake_cookie(struct handle_cookie *cookie)
949 cookie->hi->cookie = cookie;
950 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
954 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
956 struct handle_cookie *cookie;
957 char subject[128], body[4096], *misc;
958 const char *netname, *fmt;
962 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
966 cookie = calloc(1, sizeof(*cookie));
969 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
970 cookie->expires = now + nickserv_conf.cookie_timeout;
971 inttobase64(cookie->cookie, rand(), 5);
972 inttobase64(cookie->cookie+5, rand(), 5);
974 netname = nickserv_conf.network_name;
977 switch (cookie->type) {
979 hi->passwd[0] = 0; /* invalidate password */
980 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
981 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
982 snprintf(subject, sizeof(subject), fmt, netname);
983 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
984 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
987 case PASSWORD_CHANGE:
988 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
989 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
990 snprintf(subject, sizeof(subject), fmt, netname);
991 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
992 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
995 misc = hi->email_addr;
996 hi->email_addr = cookie->data;
998 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
999 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1000 snprintf(subject, sizeof(subject), fmt, netname);
1001 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1002 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1003 sendmail(nickserv, hi, subject, body, 1);
1004 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1005 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1007 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1008 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1009 snprintf(subject, sizeof(subject), fmt, netname);
1010 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1011 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1012 sendmail(nickserv, hi, subject, body, 1);
1015 hi->email_addr = misc;
1018 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1019 snprintf(subject, sizeof(subject), fmt, netname);
1020 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1021 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1022 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1025 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1029 sendmail(nickserv, hi, subject, body, first_time);
1030 nickserv_bake_cookie(cookie);
1034 nickserv_eat_cookie(struct handle_cookie *cookie)
1036 cookie->hi->cookie = NULL;
1037 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1038 nickserv_free_cookie(cookie);
1042 nickserv_free_email_addr(void *data)
1044 handle_info_list_clean(data);
1049 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1051 struct handle_info_list *hil;
1052 /* Remove from old handle_info_list ... */
1053 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1054 handle_info_list_remove(hil, hi);
1055 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1056 hi->email_addr = NULL;
1058 /* Add to the new list.. */
1059 if (new_email_addr) {
1060 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1061 hil = calloc(1, sizeof(*hil));
1062 hil->tag = strdup(new_email_addr);
1063 handle_info_list_init(hil);
1064 dict_insert(nickserv_email_dict, hil->tag, hil);
1066 handle_info_list_append(hil, hi);
1067 hi->email_addr = hil->tag;
1071 static NICKSERV_FUNC(cmd_register)
1073 struct handle_info *hi;
1074 const char *email_addr, *password;
1077 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1078 /* Require the first handle registered to belong to someone +o. */
1079 reply("NSMSG_REQUIRE_OPER");
1083 if (user->handle_info) {
1084 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1088 if (IsRegistering(user)) {
1089 reply("NSMSG_ALREADY_REGISTERING");
1093 if (IsStamped(user)) {
1094 /* Unauthenticated users might still have been stamped
1095 previously and could therefore have a hidden host;
1096 do not allow them to register a new account. */
1097 reply("NSMSG_STAMPED_REGISTER");
1101 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1103 if (!is_valid_handle(argv[1])) {
1104 reply("NSMSG_BAD_HANDLE", argv[1]);
1108 if ((argc >= 4) && nickserv_conf.email_enabled) {
1109 struct handle_info_list *hil;
1112 /* Remember email address. */
1113 email_addr = argv[3];
1115 /* Check that the email address looks valid.. */
1116 if (!is_valid_email_addr(email_addr)) {
1117 reply("NSMSG_BAD_EMAIL_ADDR");
1121 /* .. and that we are allowed to send to it. */
1122 if ((str = sendmail_prohibited_address(email_addr))) {
1123 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1127 /* If we do email verify, make sure we don't spam the address. */
1128 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1130 for (nn=0; nn<hil->used; nn++) {
1131 if (hil->list[nn]->cookie) {
1132 reply("NSMSG_EMAIL_UNACTIVATED");
1136 if (hil->used >= nickserv_conf.handles_per_email) {
1137 reply("NSMSG_EMAIL_OVERUSED");
1150 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1152 /* Add any masks they should get. */
1153 if (nickserv_conf.default_hostmask) {
1154 string_list_append(hi->masks, strdup("*@*"));
1156 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1157 if (user->ip.s_addr && user->hostname[strspn(user->hostname, "0123456789.")])
1158 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1161 /* If they're the first to register, give them level 1000. */
1162 if (dict_size(nickserv_handle_dict) == 1) {
1163 hi->opserv_level = 1000;
1164 reply("NSMSG_ROOT_HANDLE", argv[1]);
1167 /* Set their email address. */
1169 nickserv_set_email_addr(hi, email_addr);
1171 /* If they need to do email verification, tell them. */
1173 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1175 /* Set registering flag.. */
1176 user->modes |= FLAGS_REGISTERING;
1181 static NICKSERV_FUNC(cmd_oregister)
1184 struct userNode *settee;
1185 struct handle_info *hi;
1187 NICKSERV_MIN_PARMS(4);
1189 if (!is_valid_handle(argv[1])) {
1190 reply("NSMSG_BAD_HANDLE", argv[1]);
1194 if (strchr(argv[3], '@')) {
1195 mask = canonicalize_hostmask(strdup(argv[3]));
1197 settee = GetUserH(argv[4]);
1199 reply("MSG_NICK_UNKNOWN", argv[4]);
1206 } else if ((settee = GetUserH(argv[3]))) {
1207 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1209 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1212 if (settee && settee->handle_info) {
1213 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1217 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1221 string_list_append(hi->masks, mask);
1225 static NICKSERV_FUNC(cmd_handleinfo)
1228 unsigned int i, pos=0, herelen;
1229 struct userNode *target, *next_un;
1230 struct handle_info *hi;
1231 const char *nsmsg_none;
1234 if (!(hi = user->handle_info)) {
1235 reply("NSMSG_MUST_AUTH");
1238 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1242 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1243 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1244 #ifdef WITH_PROTOCOL_BAHAMUT
1245 reply("NSMSG_HANDLEINFO_ID", hi->id);
1247 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1250 intervalString(buff, now - hi->lastseen, user->handle_info);
1251 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1253 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1256 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1257 if (HANDLE_FLAGGED(hi, FROZEN))
1258 reply("NSMSG_HANDLEINFO_VACATION");
1260 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1261 struct do_not_register *dnr;
1262 if ((dnr = chanserv_is_dnr(NULL, hi)))
1263 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1264 if (!oper_outranks(user, hi))
1266 } else if (hi != user->handle_info)
1269 if (nickserv_conf.email_enabled)
1270 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1274 switch (hi->cookie->type) {
1275 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1276 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1277 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1278 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1279 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1285 unsigned long flen = 1;
1286 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1288 for (i=0, flen=1; handle_flags[i]; i++)
1289 if (hi->flags & 1 << i)
1290 flags[flen++] = handle_flags[i];
1292 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1294 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1297 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1298 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1299 || (hi->opserv_level > 0)) {
1300 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1303 if (hi->last_quit_host[0])
1304 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1306 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1308 if (nickserv_conf.disable_nicks) {
1309 /* nicks disabled; don't show anything about registered nicks */
1310 } else if (hi->nicks) {
1311 struct nick_info *ni, *next_ni;
1312 for (ni = hi->nicks; ni; ni = next_ni) {
1313 herelen = strlen(ni->nick);
1314 if (pos + herelen + 1 > ArrayLength(buff)) {
1316 goto print_nicks_buff;
1320 memcpy(buff+pos, ni->nick, herelen);
1321 pos += herelen; buff[pos++] = ' ';
1325 reply("NSMSG_HANDLEINFO_NICKS", buff);
1330 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1333 if (hi->masks->used) {
1334 for (i=0; i < hi->masks->used; i++) {
1335 herelen = strlen(hi->masks->list[i]);
1336 if (pos + herelen + 1 > ArrayLength(buff)) {
1338 goto print_mask_buff;
1340 memcpy(buff+pos, hi->masks->list[i], herelen);
1341 pos += herelen; buff[pos++] = ' ';
1342 if (i+1 == hi->masks->used) {
1345 reply("NSMSG_HANDLEINFO_MASKS", buff);
1350 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1354 struct userData *channel, *next;
1357 for (channel = hi->channels; channel; channel = next) {
1358 next = channel->u_next;
1359 name = channel->channel->channel->name;
1360 herelen = strlen(name);
1361 if (pos + herelen + 7 > ArrayLength(buff)) {
1363 goto print_chans_buff;
1365 if (IsUserSuspended(channel))
1367 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1371 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1376 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1379 for (target = hi->users; target; target = next_un) {
1380 herelen = strlen(target->nick);
1381 if (pos + herelen + 1 > ArrayLength(buff)) {
1383 goto print_cnick_buff;
1385 next_un = target->next_authed;
1387 memcpy(buff+pos, target->nick, herelen);
1388 pos += herelen; buff[pos++] = ' ';
1392 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1400 static NICKSERV_FUNC(cmd_userinfo)
1402 struct userNode *target;
1404 NICKSERV_MIN_PARMS(2);
1405 if (!(target = GetUserH(argv[1]))) {
1406 reply("MSG_NICK_UNKNOWN", argv[1]);
1409 if (target->handle_info)
1410 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1412 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1416 static NICKSERV_FUNC(cmd_nickinfo)
1418 struct nick_info *ni;
1420 NICKSERV_MIN_PARMS(2);
1421 if (!(ni = get_nick_info(argv[1]))) {
1422 reply("MSG_NICK_UNKNOWN", argv[1]);
1425 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1429 static NICKSERV_FUNC(cmd_rename_handle)
1431 struct handle_info *hi;
1432 char msgbuf[MAXLEN], *old_handle;
1435 NICKSERV_MIN_PARMS(3);
1436 if (!(hi = get_victim_oper(user, argv[1])))
1438 if (!is_valid_handle(argv[2])) {
1439 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1442 if (get_handle_info(argv[2])) {
1443 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1447 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1448 hi->handle = strdup(argv[2]);
1449 dict_insert(nickserv_handle_dict, hi->handle, hi);
1450 for (nn=0; nn<rf_list_used; nn++)
1451 rf_list[nn](hi, old_handle);
1452 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1453 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1454 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1459 static failpw_func_t *failpw_func_list;
1460 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1463 reg_failpw_func(failpw_func_t func)
1465 if (failpw_func_used == failpw_func_size) {
1466 if (failpw_func_size) {
1467 failpw_func_size <<= 1;
1468 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1470 failpw_func_size = 8;
1471 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1474 failpw_func_list[failpw_func_used++] = func;
1477 static NICKSERV_FUNC(cmd_auth)
1479 int pw_arg, used, maxlogins;
1480 struct handle_info *hi;
1482 struct userNode *other;
1484 if (user->handle_info) {
1485 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1488 if (IsStamped(user)) {
1489 /* Unauthenticated users might still have been stamped
1490 previously and could therefore have a hidden host;
1491 do not allow them to authenticate. */
1492 reply("NSMSG_STAMPED_AUTH");
1496 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1498 } else if (argc == 2) {
1499 if (nickserv_conf.disable_nicks) {
1500 if (!(hi = get_handle_info(user->nick))) {
1501 reply("NSMSG_HANDLE_NOT_FOUND");
1505 /* try to look up their handle from their nick */
1506 struct nick_info *ni;
1507 ni = get_nick_info(user->nick);
1509 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1516 reply("MSG_MISSING_PARAMS", argv[0]);
1517 svccmd_send_help(user, nickserv, cmd);
1521 reply("NSMSG_HANDLE_NOT_FOUND");
1524 /* Responses from here on look up the language used by the handle they asked about. */
1525 passwd = argv[pw_arg];
1526 if (!valid_user_for(user, hi)) {
1527 if (hi->email_addr && nickserv_conf.email_enabled)
1528 send_message_type(4, user, cmd->parent->bot,
1529 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1532 send_message_type(4, user, cmd->parent->bot,
1533 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1535 argv[pw_arg] = "BADMASK";
1538 if (!checkpass(passwd, hi->passwd)) {
1540 send_message_type(4, user, cmd->parent->bot,
1541 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1542 argv[pw_arg] = "BADPASS";
1543 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1544 if (nickserv_conf.autogag_enabled) {
1545 if (!user->auth_policer.params) {
1546 user->auth_policer.last_req = now;
1547 user->auth_policer.params = nickserv_conf.auth_policer_params;
1549 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1551 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1552 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1553 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1555 argv[pw_arg] = "GAGGED";
1560 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1561 send_message_type(4, user, cmd->parent->bot,
1562 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1563 argv[pw_arg] = "SUSPENDED";
1566 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1567 for (used = 0, other = hi->users; other; other = other->next_authed) {
1568 if (++used >= maxlogins) {
1569 send_message_type(4, user, cmd->parent->bot,
1570 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1572 argv[pw_arg] = "MAXLOGINS";
1577 set_user_handle_info(user, hi, 1);
1578 if (nickserv_conf.email_required && !hi->email_addr)
1579 reply("NSMSG_PLEASE_SET_EMAIL");
1580 if (!is_secure_password(hi->handle, passwd, NULL))
1581 reply("NSMSG_WEAK_PASSWORD");
1582 if (hi->passwd[0] != '$')
1583 cryptpass(passwd, hi->passwd);
1584 reply("NSMSG_AUTH_SUCCESS");
1585 argv[pw_arg] = "****";
1589 static allowauth_func_t *allowauth_func_list;
1590 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1593 reg_allowauth_func(allowauth_func_t func)
1595 if (allowauth_func_used == allowauth_func_size) {
1596 if (allowauth_func_size) {
1597 allowauth_func_size <<= 1;
1598 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1600 allowauth_func_size = 8;
1601 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1604 allowauth_func_list[allowauth_func_used++] = func;
1607 static NICKSERV_FUNC(cmd_allowauth)
1609 struct userNode *target;
1610 struct handle_info *hi;
1613 NICKSERV_MIN_PARMS(2);
1614 if (!(target = GetUserH(argv[1]))) {
1615 reply("MSG_NICK_UNKNOWN", argv[1]);
1618 if (target->handle_info) {
1619 reply("NSMSG_USER_PREV_AUTH", target->nick);
1622 if (IsStamped(target)) {
1623 /* Unauthenticated users might still have been stamped
1624 previously and could therefore have a hidden host;
1625 do not allow them to authenticate to an account. */
1626 send_message(target, nickserv, "NSMSG_USER_PREV_STAMP", target->nick);
1631 else if (!(hi = get_handle_info(argv[2]))) {
1632 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1636 if (hi->opserv_level > user->handle_info->opserv_level) {
1637 reply("MSG_USER_OUTRANKED", hi->handle);
1640 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1641 || (hi->opserv_level > 0))
1642 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1643 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1646 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1647 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1648 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1649 if (nickserv_conf.email_enabled)
1650 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1652 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1653 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1655 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1657 for (n=0; n<allowauth_func_used; n++)
1658 allowauth_func_list[n](user, target, hi);
1662 static NICKSERV_FUNC(cmd_authcookie)
1664 struct handle_info *hi;
1666 NICKSERV_MIN_PARMS(2);
1667 if (user->handle_info) {
1668 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1671 if (IsStamped(user)) {
1672 /* Unauthenticated users might still have been stamped
1673 previously and could therefore have a hidden host;
1674 do not allow them to authenticate to an account. */
1675 reply("NSMSG_STAMPED_AUTHCOOKIE");
1678 if (!(hi = get_handle_info(argv[1]))) {
1679 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1682 if (!hi->email_addr) {
1683 reply("MSG_SET_EMAIL_ADDR");
1686 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1690 static NICKSERV_FUNC(cmd_delcookie)
1692 struct handle_info *hi;
1694 hi = user->handle_info;
1696 reply("NSMSG_NO_COOKIE");
1699 switch (hi->cookie->type) {
1702 reply("NSMSG_MUST_TIME_OUT");
1705 nickserv_eat_cookie(hi->cookie);
1706 reply("NSMSG_ATE_COOKIE");
1712 static NICKSERV_FUNC(cmd_resetpass)
1714 struct handle_info *hi;
1715 char crypted[MD5_CRYPT_LENGTH];
1717 NICKSERV_MIN_PARMS(3);
1718 if (user->handle_info) {
1719 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1722 if (IsStamped(user)) {
1723 /* Unauthenticated users might still have been stamped
1724 previously and could therefore have a hidden host;
1725 do not allow them to activate an account. */
1726 reply("NSMSG_STAMPED_RESETPASS");
1729 if (!(hi = get_handle_info(argv[1]))) {
1730 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1733 if (!hi->email_addr) {
1734 reply("MSG_SET_EMAIL_ADDR");
1737 cryptpass(argv[2], crypted);
1739 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1743 static NICKSERV_FUNC(cmd_cookie)
1745 struct handle_info *hi;
1748 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1751 NICKSERV_MIN_PARMS(3);
1752 if (!(hi = get_handle_info(argv[1]))) {
1753 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1759 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1760 reply("NSMSG_HANDLE_SUSPENDED");
1765 reply("NSMSG_NO_COOKIE");
1769 /* Check validity of operation before comparing cookie to
1770 * prohibit guessing by authed users. */
1771 if (user->handle_info
1772 && (hi->cookie->type != EMAIL_CHANGE)
1773 && (hi->cookie->type != PASSWORD_CHANGE)) {
1774 reply("NSMSG_CANNOT_COOKIE");
1778 if (strcmp(cookie, hi->cookie->cookie)) {
1779 reply("NSMSG_BAD_COOKIE");
1783 switch (hi->cookie->type) {
1785 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1786 set_user_handle_info(user, hi, 1);
1787 reply("NSMSG_HANDLE_ACTIVATED");
1789 case PASSWORD_CHANGE:
1790 set_user_handle_info(user, hi, 1);
1791 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1792 reply("NSMSG_PASSWORD_CHANGED");
1795 nickserv_set_email_addr(hi, hi->cookie->data);
1796 reply("NSMSG_EMAIL_CHANGED");
1799 set_user_handle_info(user, hi, 1);
1800 reply("NSMSG_AUTH_SUCCESS");
1803 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1804 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1808 nickserv_eat_cookie(hi->cookie);
1813 static NICKSERV_FUNC(cmd_oregnick) {
1815 struct handle_info *target;
1816 struct nick_info *ni;
1818 NICKSERV_MIN_PARMS(3);
1819 if (!(target = modcmd_get_handle_info(user, argv[1])))
1822 if (!is_registerable_nick(nick)) {
1823 reply("NSMSG_BAD_NICK", nick);
1826 ni = dict_find(nickserv_nick_dict, nick, NULL);
1828 reply("NSMSG_NICK_EXISTS", nick);
1831 register_nick(nick, target);
1832 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1836 static NICKSERV_FUNC(cmd_regnick) {
1838 struct nick_info *ni;
1840 if (!is_registerable_nick(user->nick)) {
1841 reply("NSMSG_BAD_NICK", user->nick);
1844 /* count their nicks, see if it's too many */
1845 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1846 if (n >= nickserv_conf.nicks_per_handle) {
1847 reply("NSMSG_TOO_MANY_NICKS");
1850 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1852 reply("NSMSG_NICK_EXISTS", user->nick);
1855 register_nick(user->nick, user->handle_info);
1856 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1860 static NICKSERV_FUNC(cmd_pass)
1862 struct handle_info *hi;
1863 const char *old_pass, *new_pass;
1865 NICKSERV_MIN_PARMS(3);
1866 hi = user->handle_info;
1870 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1871 if (!checkpass(old_pass, hi->passwd)) {
1872 argv[1] = "BADPASS";
1873 reply("NSMSG_PASSWORD_INVALID");
1876 cryptpass(new_pass, hi->passwd);
1878 reply("NSMSG_PASS_SUCCESS");
1883 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1886 char *new_mask = canonicalize_hostmask(strdup(mask));
1887 for (i=0; i<hi->masks->used; i++) {
1888 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1889 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1894 string_list_append(hi->masks, new_mask);
1895 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
1899 static NICKSERV_FUNC(cmd_addmask)
1902 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1903 int res = nickserv_addmask(user, user->handle_info, mask);
1907 if (!is_gline(argv[1])) {
1908 reply("NSMSG_MASK_INVALID", argv[1]);
1911 return nickserv_addmask(user, user->handle_info, argv[1]);
1915 static NICKSERV_FUNC(cmd_oaddmask)
1917 struct handle_info *hi;
1919 NICKSERV_MIN_PARMS(3);
1920 if (!(hi = get_victim_oper(user, argv[1])))
1922 return nickserv_addmask(user, hi, argv[2]);
1926 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
1929 for (i=0; i<hi->masks->used; i++) {
1930 if (!strcmp(del_mask, hi->masks->list[i])) {
1931 char *old_mask = hi->masks->list[i];
1932 if (hi->masks->used == 1) {
1933 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
1936 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
1937 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
1942 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
1946 static NICKSERV_FUNC(cmd_delmask)
1948 NICKSERV_MIN_PARMS(2);
1949 return nickserv_delmask(user, user->handle_info, argv[1]);
1952 static NICKSERV_FUNC(cmd_odelmask)
1954 struct handle_info *hi;
1955 NICKSERV_MIN_PARMS(3);
1956 if (!(hi = get_victim_oper(user, argv[1])))
1958 return nickserv_delmask(user, hi, argv[2]);
1962 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
1963 unsigned int nn, add = 1, pos;
1964 unsigned long added, removed, flag;
1966 for (added=removed=nn=0; str[nn]; nn++) {
1968 case '+': add = 1; break;
1969 case '-': add = 0; break;
1971 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
1972 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
1975 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
1976 /* cheesy avoidance of looking up the flag name.. */
1977 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
1980 flag = 1 << (pos - 1);
1982 added |= flag, removed &= ~flag;
1984 removed |= flag, added &= ~flag;
1989 *premoved = removed;
1994 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
1996 unsigned long before, after, added, removed;
1997 struct userNode *uNode;
1999 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2000 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2002 hi->flags = (hi->flags | added) & ~removed;
2003 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2005 /* Strip helping flag if they're only a support helper and not
2006 * currently in #support. */
2007 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2008 struct channelList *schannels;
2010 schannels = chanserv_support_channels();
2011 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2012 for (ii = 0; ii < schannels->used; ++ii)
2013 if (GetUserMode(schannels->list[ii], uNode))
2015 if (ii < schannels->used)
2019 HANDLE_CLEAR_FLAG(hi, HELPING);
2022 if (after && !before) {
2023 /* Add user to current helper list. */
2024 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2025 userList_append(&curr_helpers, uNode);
2026 } else if (!after && before) {
2027 /* Remove user from current helper list. */
2028 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2029 userList_remove(&curr_helpers, uNode);
2036 set_list(struct userNode *user, struct handle_info *hi, int override)
2040 char *set_display[] = {
2041 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2042 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2045 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2047 /* Do this so options are presented in a consistent order. */
2048 for (i = 0; i < ArrayLength(set_display); ++i)
2049 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2050 opt(user, hi, override, 0, NULL);
2053 static NICKSERV_FUNC(cmd_set)
2055 struct handle_info *hi;
2058 hi = user->handle_info;
2060 set_list(user, hi, 0);
2063 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2064 reply("NSMSG_INVALID_OPTION", argv[1]);
2067 return opt(user, hi, 0, argc-1, argv+1);
2070 static NICKSERV_FUNC(cmd_oset)
2072 struct handle_info *hi;
2075 NICKSERV_MIN_PARMS(2);
2077 if (!(hi = get_victim_oper(user, argv[1])))
2081 set_list(user, hi, 0);
2085 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2086 reply("NSMSG_INVALID_OPTION", argv[2]);
2090 return opt(user, hi, 1, argc-2, argv+2);
2093 static OPTION_FUNC(opt_info)
2097 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2099 hi->infoline = NULL;
2101 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2105 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2106 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2110 static OPTION_FUNC(opt_width)
2113 hi->screen_width = strtoul(argv[1], NULL, 0);
2115 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2116 hi->screen_width = MIN_LINE_SIZE;
2117 else if (hi->screen_width > MAX_LINE_SIZE)
2118 hi->screen_width = MAX_LINE_SIZE;
2120 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2124 static OPTION_FUNC(opt_tablewidth)
2127 hi->table_width = strtoul(argv[1], NULL, 0);
2129 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2130 hi->table_width = MIN_LINE_SIZE;
2131 else if (hi->screen_width > MAX_LINE_SIZE)
2132 hi->table_width = MAX_LINE_SIZE;
2134 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2138 static OPTION_FUNC(opt_color)
2141 if (enabled_string(argv[1]))
2142 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2143 else if (disabled_string(argv[1]))
2144 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2146 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2151 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2155 static OPTION_FUNC(opt_privmsg)
2158 if (enabled_string(argv[1]))
2159 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2160 else if (disabled_string(argv[1]))
2161 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2163 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2168 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2172 static OPTION_FUNC(opt_style)
2177 if (!irccasecmp(argv[1], "Zoot"))
2178 hi->userlist_style = HI_STYLE_ZOOT;
2179 else if (!irccasecmp(argv[1], "def"))
2180 hi->userlist_style = HI_STYLE_DEF;
2183 switch (hi->userlist_style) {
2192 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2196 static OPTION_FUNC(opt_announcements)
2201 if (enabled_string(argv[1]))
2202 hi->announcements = 'y';
2203 else if (disabled_string(argv[1]))
2204 hi->announcements = 'n';
2205 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2206 hi->announcements = '?';
2208 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2213 switch (hi->announcements) {
2214 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2215 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2216 case '?': choice = "default"; break;
2217 default: choice = "unknown"; break;
2219 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2223 static OPTION_FUNC(opt_password)
2226 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2231 cryptpass(argv[1], hi->passwd);
2233 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2237 static OPTION_FUNC(opt_flags)
2240 unsigned int ii, flen;
2243 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2248 nickserv_apply_flags(user, hi, argv[1]);
2250 for (ii = flen = 0; handle_flags[ii]; ii++)
2251 if (hi->flags & (1 << ii))
2252 flags[flen++] = handle_flags[ii];
2255 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2257 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2261 static OPTION_FUNC(opt_email)
2265 if (!is_valid_email_addr(argv[1])) {
2266 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2269 if ((str = sendmail_prohibited_address(argv[1]))) {
2270 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2273 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2274 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2276 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2278 nickserv_set_email_addr(hi, argv[1]);
2280 nickserv_eat_cookie(hi->cookie);
2281 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2284 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2288 static OPTION_FUNC(opt_maxlogins)
2290 unsigned char maxlogins;
2292 maxlogins = strtoul(argv[1], NULL, 0);
2293 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2294 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2297 hi->maxlogins = maxlogins;
2299 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2300 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2304 static OPTION_FUNC(opt_language)
2306 struct language *lang;
2308 lang = language_find(argv[1]);
2309 if (irccasecmp(lang->name, argv[1]))
2310 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2311 hi->language = lang;
2313 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2318 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2319 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2321 if ((user->handle_info->opserv_level < target->opserv_level)
2322 || ((user->handle_info->opserv_level == target->opserv_level)
2323 && (user->handle_info->opserv_level < 1000))) {
2324 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2327 if ((user->handle_info->opserv_level < new_level)
2328 || ((user->handle_info->opserv_level == new_level)
2329 && (user->handle_info->opserv_level < 1000))) {
2330 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2333 if (user->handle_info == target) {
2334 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2337 if (target->opserv_level == new_level)
2339 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2340 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2341 target->opserv_level = new_level;
2345 static OPTION_FUNC(opt_level)
2350 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2354 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2355 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2359 static OPTION_FUNC(opt_epithet)
2362 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2366 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2367 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2370 if ((epithet[0] == '*') && !epithet[1])
2373 hi->epithet = strdup(epithet);
2377 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2379 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2383 static NICKSERV_FUNC(cmd_reclaim)
2385 struct handle_info *hi;
2386 struct nick_info *ni;
2387 struct userNode *victim;
2389 NICKSERV_MIN_PARMS(2);
2390 hi = user->handle_info;
2391 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2393 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2396 if (ni->owner != user->handle_info) {
2397 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2400 victim = GetUserH(ni->nick);
2402 reply("MSG_NICK_UNKNOWN", ni->nick);
2405 if (victim == user) {
2406 reply("NSMSG_NICK_USER_YOU");
2409 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2410 switch (nickserv_conf.reclaim_action) {
2411 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2412 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2413 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2414 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2419 static NICKSERV_FUNC(cmd_unregnick)
2422 struct handle_info *hi;
2423 struct nick_info *ni;
2425 hi = user->handle_info;
2426 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2427 ni = dict_find(nickserv_nick_dict, nick, NULL);
2429 reply("NSMSG_UNKNOWN_NICK", nick);
2432 if (hi != ni->owner) {
2433 reply("NSMSG_NOT_YOUR_NICK", nick);
2436 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2441 static NICKSERV_FUNC(cmd_ounregnick)
2443 struct nick_info *ni;
2445 NICKSERV_MIN_PARMS(2);
2446 if (!(ni = get_nick_info(argv[1]))) {
2447 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2450 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2451 reply("MSG_USER_OUTRANKED", ni->nick);
2454 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2459 static NICKSERV_FUNC(cmd_unregister)
2461 struct handle_info *hi;
2464 NICKSERV_MIN_PARMS(2);
2465 hi = user->handle_info;
2468 if (checkpass(passwd, hi->passwd)) {
2469 nickserv_unregister_handle(hi, user);
2472 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2473 reply("NSMSG_PASSWORD_INVALID");
2478 static NICKSERV_FUNC(cmd_ounregister)
2480 struct handle_info *hi;
2482 NICKSERV_MIN_PARMS(2);
2483 if (!(hi = get_victim_oper(user, argv[1])))
2485 nickserv_unregister_handle(hi, user);
2489 static NICKSERV_FUNC(cmd_status)
2491 if (nickserv_conf.disable_nicks) {
2492 reply("NSMSG_GLOBAL_STATS_NONICK",
2493 dict_size(nickserv_handle_dict));
2495 if (user->handle_info) {
2497 struct nick_info *ni;
2498 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2499 reply("NSMSG_HANDLE_STATS", cnt);
2501 reply("NSMSG_HANDLE_NONE");
2503 reply("NSMSG_GLOBAL_STATS",
2504 dict_size(nickserv_handle_dict),
2505 dict_size(nickserv_nick_dict));
2510 static NICKSERV_FUNC(cmd_ghost)
2512 struct userNode *target;
2513 char reason[MAXLEN];
2515 NICKSERV_MIN_PARMS(2);
2516 if (!(target = GetUserH(argv[1]))) {
2517 reply("MSG_NICK_UNKNOWN", argv[1]);
2520 if (target == user) {
2521 reply("NSMSG_CANNOT_GHOST_SELF");
2524 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2525 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2528 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2529 DelUser(target, nickserv, 1, reason);
2530 reply("NSMSG_GHOST_KILLED", argv[1]);
2534 static NICKSERV_FUNC(cmd_vacation)
2536 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2537 reply("NSMSG_ON_VACATION");
2542 nickserv_saxdb_write(struct saxdb_context *ctx) {
2544 struct handle_info *hi;
2547 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2549 #ifdef WITH_PROTOCOL_BAHAMUT
2552 saxdb_start_record(ctx, iter_key(it), 0);
2553 if (hi->announcements != '?') {
2554 flags[0] = hi->announcements;
2556 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2559 struct handle_cookie *cookie = hi->cookie;
2562 switch (cookie->type) {
2563 case ACTIVATION: type = KEY_ACTIVATION; break;
2564 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2565 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2566 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2567 default: type = NULL; break;
2570 saxdb_start_record(ctx, KEY_COOKIE, 0);
2571 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2572 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2574 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2575 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2576 saxdb_end_record(ctx);
2580 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2582 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2586 for (ii=flen=0; handle_flags[ii]; ++ii)
2587 if (hi->flags & (1 << ii))
2588 flags[flen++] = handle_flags[ii];
2590 saxdb_write_string(ctx, KEY_FLAGS, flags);
2592 #ifdef WITH_PROTOCOL_BAHAMUT
2593 saxdb_write_int(ctx, KEY_ID, hi->id);
2596 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2597 if (hi->last_quit_host[0])
2598 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2599 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2600 if (hi->masks->used)
2601 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2603 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2605 struct string_list *slist;
2606 struct nick_info *ni;
2608 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2609 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2610 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2614 if (hi->opserv_level)
2615 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2616 if (hi->language != lang_C)
2617 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2618 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2619 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2620 if (hi->screen_width)
2621 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2622 if (hi->table_width)
2623 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2624 flags[0] = hi->userlist_style;
2626 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2627 saxdb_end_record(ctx);
2632 static handle_merge_func_t *handle_merge_func_list;
2633 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2636 reg_handle_merge_func(handle_merge_func_t func)
2638 if (handle_merge_func_used == handle_merge_func_size) {
2639 if (handle_merge_func_size) {
2640 handle_merge_func_size <<= 1;
2641 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2643 handle_merge_func_size = 8;
2644 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2647 handle_merge_func_list[handle_merge_func_used++] = func;
2650 static NICKSERV_FUNC(cmd_merge)
2652 struct handle_info *hi_from, *hi_to;
2653 struct userNode *last_user;
2654 struct userData *cList, *cListNext;
2655 unsigned int ii, jj, n;
2656 char buffer[MAXLEN];
2658 NICKSERV_MIN_PARMS(3);
2660 if (!(hi_from = get_victim_oper(user, argv[1])))
2662 if (!(hi_to = get_victim_oper(user, argv[2])))
2664 if (hi_to == hi_from) {
2665 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2669 for (n=0; n<handle_merge_func_used; n++)
2670 handle_merge_func_list[n](user, hi_to, hi_from);
2672 /* Append "from" handle's nicks to "to" handle's nick list. */
2674 struct nick_info *last_ni;
2675 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2676 last_ni->next = hi_from->nicks;
2678 while (hi_from->nicks) {
2679 hi_from->nicks->owner = hi_to;
2680 hi_from->nicks = hi_from->nicks->next;
2683 /* Merge the hostmasks. */
2684 for (ii=0; ii<hi_from->masks->used; ii++) {
2685 char *mask = hi_from->masks->list[ii];
2686 for (jj=0; jj<hi_to->masks->used; jj++)
2687 if (match_ircglobs(hi_to->masks->list[jj], mask))
2689 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2690 string_list_append(hi_to->masks, strdup(mask));
2693 /* Merge the lists of authed users. */
2695 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2696 last_user->next_authed = hi_from->users;
2698 hi_to->users = hi_from->users;
2700 /* Repoint the old "from" handle's users. */
2701 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2702 last_user->handle_info = hi_to;
2704 hi_from->users = NULL;
2706 /* Merge channel userlists. */
2707 for (cList=hi_from->channels; cList; cList=cListNext) {
2708 struct userData *cList2;
2709 cListNext = cList->u_next;
2710 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2711 if (cList->channel == cList2->channel)
2713 if (cList2 && (cList2->access >= cList->access)) {
2714 log_module(NS_LOG, LOG_INFO, "Merge: %s had only %d access in %s (versus %d for %s)", hi_from->handle, cList->access, cList->channel->channel->name, cList2->access, hi_to->handle);
2715 /* keep cList2 in hi_to; remove cList from hi_from */
2716 del_channel_user(cList, 1);
2719 log_module(NS_LOG, LOG_INFO, "Merge: %s had only %d access in %s (versus %d for %s)", hi_to->handle, cList2->access, cList->channel->channel->name, cList->access, hi_from->handle);
2720 /* remove the lower-ranking cList2 from hi_to */
2721 del_channel_user(cList2, 1);
2723 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
2725 /* cList needs to be moved from hi_from to hi_to */
2726 cList->handle = hi_to;
2727 /* Remove from linked list for hi_from */
2728 assert(!cList->u_prev);
2729 hi_from->channels = cList->u_next;
2731 cList->u_next->u_prev = cList->u_prev;
2732 /* Add to linked list for hi_to */
2733 cList->u_prev = NULL;
2734 cList->u_next = hi_to->channels;
2735 if (hi_to->channels)
2736 hi_to->channels->u_prev = cList;
2737 hi_to->channels = cList;
2741 /* Do they get an OpServ level promotion? */
2742 if (hi_from->opserv_level > hi_to->opserv_level)
2743 hi_to->opserv_level = hi_from->opserv_level;
2745 /* What about last seen time? */
2746 if (hi_from->lastseen > hi_to->lastseen)
2747 hi_to->lastseen = hi_from->lastseen;
2749 /* Notify of success. */
2750 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2751 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2752 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2754 /* Unregister the "from" handle. */
2755 nickserv_unregister_handle(hi_from, NULL);
2760 struct nickserv_discrim {
2761 unsigned int limit, min_level, max_level;
2762 unsigned long flags_on, flags_off;
2763 time_t min_registered, max_registered;
2765 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2766 const char *nickmask;
2767 const char *hostmask;
2768 const char *handlemask;
2769 const char *emailmask;
2772 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2774 struct discrim_apply_info {
2775 struct nickserv_discrim *discrim;
2776 discrim_search_func func;
2777 struct userNode *source;
2778 unsigned int matched;
2781 static struct nickserv_discrim *
2782 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2785 struct nickserv_discrim *discrim;
2787 discrim = malloc(sizeof(*discrim));
2788 memset(discrim, 0, sizeof(*discrim));
2789 discrim->min_level = 0;
2790 discrim->max_level = ~0;
2791 discrim->limit = 50;
2792 discrim->min_registered = 0;
2793 discrim->max_registered = INT_MAX;
2794 discrim->lastseen = now;
2796 for (i=0; i<argc; i++) {
2797 if (i == argc - 1) {
2798 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2801 if (!irccasecmp(argv[i], "limit")) {
2802 discrim->limit = strtoul(argv[++i], NULL, 0);
2803 } else if (!irccasecmp(argv[i], "flags")) {
2804 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2805 } else if (!irccasecmp(argv[i], "registered")) {
2806 const char *cmp = argv[++i];
2807 if (cmp[0] == '<') {
2808 if (cmp[1] == '=') {
2809 discrim->min_registered = now - ParseInterval(cmp+2);
2811 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2813 } else if (cmp[0] == '=') {
2814 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2815 } else if (cmp[0] == '>') {
2816 if (cmp[1] == '=') {
2817 discrim->max_registered = now - ParseInterval(cmp+2);
2819 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2822 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2824 } else if (!irccasecmp(argv[i], "seen")) {
2825 discrim->lastseen = now - ParseInterval(argv[++i]);
2826 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2827 discrim->nickmask = argv[++i];
2828 } else if (!irccasecmp(argv[i], "hostmask")) {
2830 if (!irccasecmp(argv[i], "exact")) {
2831 if (i == argc - 1) {
2832 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2835 discrim->hostmask_type = EXACT;
2836 } else if (!irccasecmp(argv[i], "subset")) {
2837 if (i == argc - 1) {
2838 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2841 discrim->hostmask_type = SUBSET;
2842 } else if (!irccasecmp(argv[i], "superset")) {
2843 if (i == argc - 1) {
2844 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2847 discrim->hostmask_type = SUPERSET;
2848 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2849 if (i == argc - 1) {
2850 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2853 discrim->hostmask_type = LASTQUIT;
2856 discrim->hostmask_type = SUPERSET;
2858 discrim->hostmask = argv[++i];
2859 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2860 if (!irccasecmp(argv[++i], "*")) {
2861 discrim->handlemask = 0;
2863 discrim->handlemask = argv[i];
2865 } else if (!irccasecmp(argv[i], "email")) {
2866 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2867 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2869 } else if (!irccasecmp(argv[++i], "*")) {
2870 discrim->emailmask = 0;
2872 discrim->emailmask = argv[i];
2874 } else if (!irccasecmp(argv[i], "access")) {
2875 const char *cmp = argv[++i];
2876 if (cmp[0] == '<') {
2877 if (discrim->min_level == 0) discrim->min_level = 1;
2878 if (cmp[1] == '=') {
2879 discrim->max_level = strtoul(cmp+2, NULL, 0);
2881 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
2883 } else if (cmp[0] == '=') {
2884 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
2885 } else if (cmp[0] == '>') {
2886 if (cmp[1] == '=') {
2887 discrim->min_level = strtoul(cmp+2, NULL, 0);
2889 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
2892 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2895 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
2906 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
2908 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
2909 || (discrim->flags_off & hi->flags)
2910 || (discrim->min_registered > hi->registered)
2911 || (discrim->max_registered < hi->registered)
2912 || (discrim->lastseen < (hi->users?now:hi->lastseen))
2913 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
2914 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
2915 || (discrim->min_level > hi->opserv_level)
2916 || (discrim->max_level < hi->opserv_level)) {
2919 if (discrim->hostmask) {
2921 for (i=0; i<hi->masks->used; i++) {
2922 const char *mask = hi->masks->list[i];
2923 if ((discrim->hostmask_type == SUBSET)
2924 && (match_ircglobs(discrim->hostmask, mask))) break;
2925 else if ((discrim->hostmask_type == EXACT)
2926 && !irccasecmp(discrim->hostmask, mask)) break;
2927 else if ((discrim->hostmask_type == SUPERSET)
2928 && (match_ircglobs(mask, discrim->hostmask))) break;
2929 else if ((discrim->hostmask_type == LASTQUIT)
2930 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
2932 if (i==hi->masks->used) return 0;
2934 if (discrim->nickmask) {
2935 struct nick_info *nick = hi->nicks;
2937 if (match_ircglob(nick->nick, discrim->nickmask)) break;
2940 if (!nick) return 0;
2946 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
2948 dict_iterator_t it, next;
2949 unsigned int matched;
2951 for (it = dict_first(nickserv_handle_dict), matched = 0;
2952 it && (matched < discrim->limit);
2954 next = iter_next(it);
2955 if (nickserv_discrim_match(discrim, iter_data(it))) {
2956 dsf(source, iter_data(it));
2964 search_print_func(struct userNode *source, struct handle_info *match)
2966 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
2970 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
2975 search_unregister_func (struct userNode *source, struct handle_info *match)
2977 if (oper_has_access(source, nickserv, match->opserv_level, 0))
2978 nickserv_unregister_handle(match, source);
2982 nickserv_sort_accounts_by_access(const void *a, const void *b)
2984 const struct handle_info *hi_a = *(const struct handle_info**)a;
2985 const struct handle_info *hi_b = *(const struct handle_info**)b;
2986 if (hi_a->opserv_level != hi_b->opserv_level)
2987 return hi_b->opserv_level - hi_a->opserv_level;
2988 return irccasecmp(hi_a->handle, hi_b->handle);
2992 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
2994 struct handle_info_list hil;
2995 struct helpfile_table tbl;
3000 memset(&hil, 0, sizeof(hil));
3001 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3002 struct handle_info *hi = iter_data(it);
3003 if (hi->opserv_level)
3004 handle_info_list_append(&hil, hi);
3006 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3007 tbl.length = hil.used + 1;
3009 tbl.flags = TABLE_NO_FREE;
3010 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3011 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3014 for (ii = 0; ii < hil.used; ) {
3015 ary = malloc(tbl.width * sizeof(ary[0]));
3016 ary[0] = hil.list[ii]->handle;
3017 ary[1] = strtab(hil.list[ii]->opserv_level);
3018 tbl.contents[++ii] = ary;
3020 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3021 reply("MSG_MATCH_COUNT", hil.used);
3022 for (ii = 0; ii < hil.used; ii++)
3023 free(tbl.contents[ii]);
3028 static NICKSERV_FUNC(cmd_search)
3030 struct nickserv_discrim *discrim;
3031 discrim_search_func action;
3032 struct svccmd *subcmd;
3033 unsigned int matches;
3036 NICKSERV_MIN_PARMS(3);
3037 sprintf(buf, "search %s", argv[1]);
3038 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3039 if (!irccasecmp(argv[1], "print"))
3040 action = search_print_func;
3041 else if (!irccasecmp(argv[1], "count"))
3042 action = search_count_func;
3043 else if (!irccasecmp(argv[1], "unregister"))
3044 action = search_unregister_func;
3046 reply("NSMSG_INVALID_ACTION", argv[1]);
3050 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3053 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3057 if (action == search_print_func)
3058 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3059 else if (action == search_count_func)
3060 discrim->limit = INT_MAX;
3062 matches = nickserv_discrim_search(discrim, action, user);
3065 reply("MSG_MATCH_COUNT", matches);
3067 reply("MSG_NO_MATCHES");
3073 static MODCMD_FUNC(cmd_checkpass)
3075 struct handle_info *hi;
3077 NICKSERV_MIN_PARMS(3);
3078 if (!(hi = get_handle_info(argv[1]))) {
3079 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3082 if (checkpass(argv[2], hi->passwd))
3083 reply("CHECKPASS_YES");
3085 reply("CHECKPASS_NO");
3091 nickserv_db_read_handle(const char *handle, dict_t obj)
3094 struct string_list *masks, *slist;
3095 struct handle_info *hi;
3096 struct userNode *authed_users;
3097 unsigned long int id;
3101 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3102 id = str ? strtoul(str, NULL, 0) : 0;
3103 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3105 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3108 if ((hi = get_handle_info(handle))) {
3109 authed_users = hi->users;
3111 dict_remove(nickserv_handle_dict, hi->handle);
3113 authed_users = NULL;
3115 hi = register_handle(handle, str, id);
3117 hi->users = authed_users;
3118 while (authed_users) {
3119 authed_users->handle_info = hi;
3120 authed_users = authed_users->next_authed;
3123 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3124 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3125 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3126 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3127 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3128 hi->language = language_find(str ? str : "C");
3129 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3130 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3131 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3133 hi->infoline = strdup(str);
3134 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3135 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3136 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3137 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3138 /* We want to read the nicks even if disable_nicks is set. This is so
3139 * that we don't lose the nick data entirely. */
3140 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3142 for (ii=0; ii<slist->used; ii++)
3143 register_nick(slist->list[ii], hi);
3145 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3147 for (ii=0; str[ii]; ii++)
3148 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3150 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3151 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3152 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3153 hi->announcements = str ? str[0] : '?';
3154 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3155 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3156 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3157 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3158 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3160 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3162 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3163 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3165 nickserv_set_email_addr(hi, str);
3166 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3168 hi->epithet = strdup(str);
3169 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3171 const char *data, *type, *expires, *cookie_str;
3172 struct handle_cookie *cookie;
3174 cookie = calloc(1, sizeof(*cookie));
3175 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3176 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3177 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3178 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3179 if (!type || !expires || !cookie_str) {
3180 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3183 if (!irccasecmp(type, KEY_ACTIVATION))
3184 cookie->type = ACTIVATION;
3185 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3186 cookie->type = PASSWORD_CHANGE;
3187 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3188 cookie->type = EMAIL_CHANGE;
3189 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3190 cookie->type = ALLOWAUTH;
3192 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3195 cookie->expires = strtoul(expires, NULL, 0);
3196 if (cookie->expires < now)
3199 cookie->data = strdup(data);
3200 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3204 nickserv_bake_cookie(cookie);
3206 nickserv_free_cookie(cookie);
3211 nickserv_saxdb_read(dict_t db) {
3213 struct record_data *rd;
3215 for (it=dict_first(db); it; it=iter_next(it)) {
3217 nickserv_db_read_handle(iter_key(it), rd->d.object);
3222 static NICKSERV_FUNC(cmd_mergedb)
3224 struct timeval start, stop;
3227 NICKSERV_MIN_PARMS(2);
3228 gettimeofday(&start, NULL);
3229 if (!(db = parse_database(argv[1]))) {
3230 reply("NSMSG_DB_UNREADABLE", argv[1]);
3233 nickserv_saxdb_read(db);
3235 gettimeofday(&stop, NULL);
3236 stop.tv_sec -= start.tv_sec;
3237 stop.tv_usec -= start.tv_usec;
3238 if (stop.tv_usec < 0) {
3240 stop.tv_usec += 1000000;
3242 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3247 expire_handles(UNUSED_ARG(void *data))
3249 dict_iterator_t it, next;
3251 struct handle_info *hi;
3253 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3254 next = iter_next(it);
3256 if ((hi->opserv_level > 0)
3258 || HANDLE_FLAGGED(hi, FROZEN)
3259 || HANDLE_FLAGGED(hi, NODELETE)) {
3262 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3263 if ((now - hi->lastseen) > expiry) {
3264 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3265 nickserv_unregister_handle(hi, NULL);
3269 if (nickserv_conf.handle_expire_frequency)
3270 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3274 nickserv_load_dict(const char *fname)
3278 if (!(file = fopen(fname, "r"))) {
3279 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3282 while (!feof(file)) {
3283 fgets(line, sizeof(line), file);
3286 if (line[strlen(line)-1] == '\n')
3287 line[strlen(line)-1] = 0;
3288 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3291 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3294 static enum reclaim_action
3295 reclaim_action_from_string(const char *str) {
3297 return RECLAIM_NONE;
3298 else if (!irccasecmp(str, "warn"))
3299 return RECLAIM_WARN;
3300 else if (!irccasecmp(str, "svsnick"))
3301 return RECLAIM_SVSNICK;
3302 else if (!irccasecmp(str, "kill"))
3303 return RECLAIM_KILL;
3305 return RECLAIM_NONE;
3309 nickserv_conf_read(void)
3311 dict_t conf_node, child;
3315 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3316 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3319 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3321 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3322 if (nickserv_conf.valid_handle_regex_set) regfree(&nickserv_conf.valid_handle_regex);
3324 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3325 nickserv_conf.valid_handle_regex_set = !err;
3326 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3328 nickserv_conf.valid_handle_regex_set = 0;
3330 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3331 if (nickserv_conf.valid_nick_regex_set) regfree(&nickserv_conf.valid_nick_regex);
3333 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3334 nickserv_conf.valid_nick_regex_set = !err;
3335 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3337 nickserv_conf.valid_nick_regex_set = 0;
3339 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3341 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3342 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3343 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3344 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3345 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3346 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3347 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3348 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3349 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3350 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3351 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3352 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3353 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3354 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3355 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3356 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3357 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3358 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3359 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3360 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3361 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3363 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3364 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3365 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3367 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3368 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3369 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3371 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3372 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3373 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3374 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3375 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3376 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3377 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3378 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3379 if (!nickserv_conf.disable_nicks) {
3380 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3381 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3382 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3383 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3384 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3385 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3386 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3387 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3389 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3390 for (it=dict_first(child); it; it=iter_next(it)) {
3391 const char *key = iter_key(it), *value;
3395 if (!strncasecmp(key, "uc_", 3))
3396 flag = toupper(key[3]);
3397 else if (!strncasecmp(key, "lc_", 3))
3398 flag = tolower(key[3]);
3402 if ((pos = handle_inverse_flags[flag])) {
3403 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3404 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3407 if (nickserv_conf.weak_password_dict)
3408 dict_delete(nickserv_conf.weak_password_dict);
3409 nickserv_conf.weak_password_dict = dict_new();
3410 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3411 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3412 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3413 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3415 nickserv_load_dict(str);
3416 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3417 if (nickserv && str)
3418 NickChange(nickserv, str, 0);
3419 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3420 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3421 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3422 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3423 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3424 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3425 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3426 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3427 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3428 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3429 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3430 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3431 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3432 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3433 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3434 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3435 str = conf_get_data("server/network", RECDB_QSTRING);
3436 nickserv_conf.network_name = str ? str : "some IRC network";
3437 if (!nickserv_conf.auth_policer_params) {
3438 nickserv_conf.auth_policer_params = policer_params_new();
3439 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3440 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3442 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3443 for (it=dict_first(child); it; it=iter_next(it))
3444 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3448 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3450 char newnick[NICKLEN+1];
3459 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3461 case RECLAIM_SVSNICK:
3463 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3464 } while (GetUserH(newnick));
3465 irc_svsnick(nickserv, user, newnick);
3468 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3469 irc_kill(nickserv, user, msg);
3475 nickserv_reclaim_p(void *data) {
3476 struct userNode *user = data;
3477 struct nick_info *ni = get_nick_info(user->nick);
3479 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3483 check_user_nick(struct userNode *user) {
3484 struct nick_info *ni;
3485 user->modes &= ~FLAGS_REGNICK;
3486 if (!(ni = get_nick_info(user->nick)))
3488 if (user->handle_info == ni->owner) {
3489 user->modes |= FLAGS_REGNICK;
3493 if (nickserv_conf.warn_nick_owned)
3494 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3495 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3497 if (nickserv_conf.auto_reclaim_delay)
3498 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3500 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3505 handle_new_user(struct userNode *user)
3507 return check_user_nick(user);
3511 handle_account(struct userNode *user, const char *stamp)
3513 struct handle_info *hi;
3515 #ifdef WITH_PROTOCOL_P10
3516 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3518 hi = dict_find(nickserv_id_dict, stamp, NULL);
3522 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3525 set_user_handle_info(user, hi, 0);
3527 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3532 handle_nick_change(struct userNode *user, const char *old_nick)
3534 struct handle_info *hi;
3536 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3537 dict_remove(nickserv_allow_auth_dict, old_nick);
3538 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3540 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3541 check_user_nick(user);
3545 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3547 dict_remove(nickserv_allow_auth_dict, user->nick);
3548 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3549 set_user_handle_info(user, NULL, 0);
3552 static struct modcmd *
3553 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3555 if (min_level > 0) {
3557 sprintf(buf, "%u", min_level);
3558 if (must_be_qualified) {
3559 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3561 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3563 } else if (min_level == 0) {
3564 if (must_be_qualified) {
3565 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3567 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3570 if (must_be_qualified) {
3571 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3573 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3579 nickserv_db_cleanup(void)
3581 unreg_del_user_func(nickserv_remove_user);
3582 userList_clean(&curr_helpers);
3583 policer_params_delete(nickserv_conf.auth_policer_params);
3584 dict_delete(nickserv_handle_dict);
3585 dict_delete(nickserv_nick_dict);
3586 dict_delete(nickserv_opt_dict);
3587 dict_delete(nickserv_allow_auth_dict);
3588 dict_delete(nickserv_email_dict);
3589 dict_delete(nickserv_id_dict);
3590 dict_delete(nickserv_conf.weak_password_dict);
3591 free(auth_func_list);
3592 free(unreg_func_list);
3594 free(allowauth_func_list);
3595 free(handle_merge_func_list);
3596 free(failpw_func_list);
3597 if (nickserv_conf.valid_handle_regex_set)
3598 regfree(&nickserv_conf.valid_handle_regex);
3599 if (nickserv_conf.valid_nick_regex_set)
3600 regfree(&nickserv_conf.valid_nick_regex);
3604 init_nickserv(const char *nick)
3607 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3608 reg_new_user_func(handle_new_user);
3609 reg_nick_change_func(handle_nick_change);
3610 reg_del_user_func(nickserv_remove_user);
3611 reg_account_func(handle_account);
3613 /* set up handle_inverse_flags */
3614 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3615 for (i=0; handle_flags[i]; i++) {
3616 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3617 flag_access_levels[i] = 0;
3620 conf_register_reload(nickserv_conf_read);
3621 nickserv_opt_dict = dict_new();
3622 nickserv_email_dict = dict_new();
3623 dict_set_free_keys(nickserv_email_dict, free);
3624 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3626 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3627 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3628 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3629 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3630 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3631 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3632 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3633 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3634 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3635 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3636 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3637 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3638 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3639 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3640 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3641 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3642 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3643 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3644 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3645 if (!nickserv_conf.disable_nicks) {
3646 /* nick management commands */
3647 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3648 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3649 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3650 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3651 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3652 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3654 if (nickserv_conf.email_enabled) {
3655 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3656 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3657 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3658 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3659 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3661 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3662 /* miscellaneous commands */
3663 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3664 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3665 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3666 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3667 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3669 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3670 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3671 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3672 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3673 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3674 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3675 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3676 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3677 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3678 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3679 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3680 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3681 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3682 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3683 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3685 nickserv_handle_dict = dict_new();
3686 dict_set_free_keys(nickserv_handle_dict, free);
3687 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3689 nickserv_id_dict = dict_new();
3690 dict_set_free_keys(nickserv_id_dict, free);
3692 nickserv_nick_dict = dict_new();
3693 dict_set_free_data(nickserv_nick_dict, free_nick_info);
3695 nickserv_allow_auth_dict = dict_new();
3697 userList_init(&curr_helpers);
3700 nickserv = AddService(nick, "Nick Services", NULL);
3701 nickserv_service = service_register(nickserv);
3703 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3704 reg_exit_func(nickserv_db_cleanup);
3705 if(nickserv_conf.handle_expire_frequency)
3706 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3707 message_register_table(msgtab);