1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2006 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() */
33 # include "rx/rxposix.h"
36 #define NICKSERV_CONF_NAME "services/nickserv"
38 #define KEY_DISABLE_NICKS "disable_nicks"
39 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
40 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
41 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
42 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
43 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
44 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
45 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
46 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
47 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
48 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
49 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
50 #define KEY_MODOPER_LEVEL "modoper_level"
51 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
52 #define KEY_SET_TITLE_LEVEL "set_title_level"
53 #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
54 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
55 #define KEY_FLAG_LEVELS "flag_levels"
56 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
57 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
58 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
59 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
60 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
61 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
62 #define KEY_DICT_FILE "dict_file"
63 #define KEY_NICK "nick"
64 #define KEY_LANGUAGE "language"
65 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
66 #define KEY_AUTOGAG_DURATION "autogag_duration"
67 #define KEY_AUTH_POLICER "auth_policer"
68 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
69 #define KEY_EMAIL_ENABLED "email_enabled"
70 #define KEY_EMAIL_REQUIRED "email_required"
71 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
72 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
73 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
76 #define KEY_PASSWD "passwd"
77 #define KEY_NICKS "nicks"
78 #define KEY_MASKS "masks"
79 #define KEY_OPSERV_LEVEL "opserv_level"
80 #define KEY_FLAGS "flags"
81 #define KEY_REGISTER_ON "register"
82 #define KEY_LAST_SEEN "lastseen"
83 #define KEY_INFO "info"
84 #define KEY_USERLIST_STYLE "user_style"
85 #define KEY_SCREEN_WIDTH "screen_width"
86 #define KEY_LAST_AUTHED_HOST "last_authed_host"
87 #define KEY_LAST_QUIT_HOST "last_quit_host"
88 #define KEY_EMAIL_ADDR "email_addr"
89 #define KEY_COOKIE "cookie"
90 #define KEY_COOKIE_DATA "data"
91 #define KEY_COOKIE_TYPE "type"
92 #define KEY_COOKIE_EXPIRES "expires"
93 #define KEY_ACTIVATION "activation"
94 #define KEY_PASSWORD_CHANGE "password change"
95 #define KEY_EMAIL_CHANGE "email change"
96 #define KEY_ALLOWAUTH "allowauth"
97 #define KEY_EPITHET "epithet"
98 #define KEY_TABLE_WIDTH "table_width"
99 #define KEY_ANNOUNCEMENTS "announcements"
100 #define KEY_MAXLOGINS "maxlogins"
101 #define KEY_FAKEHOST "fakehost"
102 #define KEY_NOTES "notes"
103 #define KEY_NOTE_EXPIRES "expires"
104 #define KEY_NOTE_SET "set"
105 #define KEY_NOTE_SETTER "setter"
106 #define KEY_NOTE_NOTE "note"
108 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
110 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
111 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
112 typedef OPTION_FUNC(option_func_t);
114 DEFINE_LIST(handle_info_list, struct handle_info*);
116 #define NICKSERV_MIN_PARMS(N) do { \
118 reply("MSG_MISSING_PARAMS", argv[0]); \
119 svccmd_send_help(user, nickserv, cmd); \
123 struct userNode *nickserv;
124 struct userList curr_helpers;
125 const char *handle_flags = HANDLE_FLAGS;
127 static struct module *nickserv_module;
128 static struct service *nickserv_service;
129 static struct log_type *NS_LOG;
130 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
131 static dict_t nickserv_id_dict; /* contains struct handle_info* */
132 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
133 static dict_t nickserv_opt_dict; /* contains option_func_t* */
134 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
135 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
136 static char handle_inverse_flags[256];
137 static unsigned int flag_access_levels[32];
138 static const struct message_entry msgtab[] = {
139 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
140 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
141 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
142 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
143 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
144 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
145 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
146 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
147 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
148 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
149 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
150 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
151 { "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." },
152 { "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." },
153 { "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." },
154 { "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." },
155 { "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." },
156 { "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." },
157 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
158 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
159 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
160 { "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." },
161 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
162 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
163 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
164 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
165 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
166 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
167 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
168 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
169 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
170 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
171 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
172 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
173 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
174 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
175 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
176 { "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)" },
177 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
178 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
179 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
180 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
181 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
182 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
183 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
184 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
185 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
186 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
187 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
188 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
189 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
190 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
191 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
192 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
193 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
194 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
195 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
196 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
197 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
198 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
199 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
200 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
201 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
202 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
203 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
204 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
205 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
206 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
207 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
208 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
209 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
210 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
211 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
212 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
213 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
214 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
215 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
216 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
217 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
218 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
219 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
220 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
221 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
222 { "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)." },
223 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
224 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
225 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
226 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
227 { "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." },
228 { "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." },
229 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
230 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
231 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
232 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
233 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
234 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
235 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
236 { "NSMSG_PASS_SUCCESS", "Password changed." },
237 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
238 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
239 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
240 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
241 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
242 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
243 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
244 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
245 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
246 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
247 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
248 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
249 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
250 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
251 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
252 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
253 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
254 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
255 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
256 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
257 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
258 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
259 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
260 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
261 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
262 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
263 { "NSMSG_NO_ACCESS", "Access denied." },
264 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
265 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
266 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
267 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
268 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
269 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
270 { "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." },
271 { "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." },
272 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
273 { "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." },
274 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
275 { "NSMSG_SEARCH_MATCH", "Match: %s" },
276 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
277 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
278 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
279 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
280 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
281 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
282 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
283 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
284 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
285 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
286 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
287 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
288 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
289 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
290 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
291 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
292 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
293 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
294 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
295 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
296 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
297 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
298 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
299 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
300 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
301 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
302 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
303 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
304 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
305 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
306 { "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." },
307 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
308 { "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." },
309 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
310 { "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." },
311 { "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." },
312 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
313 { "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." },
314 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
315 { "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." },
316 { "CHECKPASS_YES", "Yes." },
317 { "CHECKPASS_NO", "No." },
321 enum reclaim_action {
327 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
328 static void nickserv_reclaim_p(void *data);
329 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
332 unsigned int disable_nicks : 1;
333 unsigned int valid_handle_regex_set : 1;
334 unsigned int valid_nick_regex_set : 1;
335 unsigned int autogag_enabled : 1;
336 unsigned int email_enabled : 1;
337 unsigned int email_required : 1;
338 unsigned int default_hostmask : 1;
339 unsigned int warn_nick_owned : 1;
340 unsigned int warn_clone_auth : 1;
341 unsigned long nicks_per_handle;
342 unsigned long password_min_length;
343 unsigned long password_min_digits;
344 unsigned long password_min_upper;
345 unsigned long password_min_lower;
346 unsigned long db_backup_frequency;
347 unsigned long handle_expire_frequency;
348 unsigned long autogag_duration;
349 unsigned long email_visible_level;
350 unsigned long cookie_timeout;
351 unsigned long handle_expire_delay;
352 unsigned long nochan_handle_expire_delay;
353 unsigned long modoper_level;
354 unsigned long set_epithet_level;
355 unsigned long set_title_level;
356 unsigned long set_fakehost_level;
357 unsigned long handles_per_email;
358 unsigned long email_search_level;
359 const char *network_name;
360 const char *titlehost_suffix;
361 regex_t valid_handle_regex;
362 regex_t valid_nick_regex;
363 dict_t weak_password_dict;
364 struct policer_params *auth_policer_params;
365 enum reclaim_action reclaim_action;
366 enum reclaim_action auto_reclaim_action;
367 unsigned long auto_reclaim_delay;
368 unsigned char default_maxlogins;
369 unsigned char hard_maxlogins;
372 /* We have 2^32 unique account IDs to use. */
373 unsigned long int highest_id = 0;
375 #define WALK_NOTES(HANDLE, PREV, NOTE) \
376 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
377 if (NOTE->expires && NOTE->expires < now) { \
378 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
380 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
384 canonicalize_hostmask(char *mask)
386 char *out = mask, *temp;
387 if ((temp = strchr(mask, '!'))) {
389 while (*temp) *out++ = *temp++;
395 static struct handle_info *
396 register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
398 struct handle_info *hi;
400 #ifdef WITH_PROTOCOL_BAHAMUT
401 char id_base64[IDLEN + 1];
404 /* Assign a unique account ID to the account; note that 0 is
405 an invalid account ID. 1 is therefore the first account ID. */
407 id = 1 + highest_id++;
409 /* Note: highest_id is and must always be the highest ID. */
410 if (id > highest_id) {
414 inttobase64(id_base64, id, IDLEN);
416 /* Make sure an account with the same ID doesn't exist. If a
417 duplicate is found, log some details and assign a new one.
418 This should be impossible, but it never hurts to expect it. */
419 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
420 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
426 hi = calloc(1, sizeof(*hi));
427 hi->userlist_style = HI_DEFAULT_STYLE;
428 hi->announcements = '?';
429 hi->handle = strdup(handle);
430 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
432 dict_insert(nickserv_handle_dict, hi->handle, hi);
434 #ifdef WITH_PROTOCOL_BAHAMUT
436 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
443 register_nick(const char *nick, struct handle_info *owner)
445 struct nick_info *ni;
446 ni = malloc(sizeof(struct nick_info));
447 safestrncpy(ni->nick, nick, sizeof(ni->nick));
449 ni->next = owner->nicks;
451 dict_insert(nickserv_nick_dict, ni->nick, ni);
455 delete_nick(struct nick_info *ni)
457 struct nick_info *last, *next;
458 struct userNode *user;
459 /* Check to see if we should mark a user as unregistered. */
460 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
461 user->modes &= ~FLAGS_REGNICK;
464 /* Remove ni from the nick_info linked list. */
465 if (ni == ni->owner->nicks) {
466 ni->owner->nicks = ni->next;
468 last = ni->owner->nicks;
474 last->next = next->next;
476 dict_remove(nickserv_nick_dict, ni->nick);
479 static unreg_func_t *unreg_func_list;
480 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
483 reg_unreg_func(unreg_func_t func)
485 if (unreg_func_used == unreg_func_size) {
486 if (unreg_func_size) {
487 unreg_func_size <<= 1;
488 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
491 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
494 unreg_func_list[unreg_func_used++] = func;
498 nickserv_free_cookie(void *data)
500 struct handle_cookie *cookie = data;
501 if (cookie->hi) cookie->hi->cookie = NULL;
502 if (cookie->data) free(cookie->data);
507 free_handle_info(void *vhi)
509 struct handle_info *hi = vhi;
511 #ifdef WITH_PROTOCOL_BAHAMUT
514 inttobase64(id, hi->id, IDLEN);
515 dict_remove(nickserv_id_dict, id);
518 free_string_list(hi->masks);
522 delete_nick(hi->nicks);
527 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
528 nickserv_free_cookie(hi->cookie);
531 struct handle_note *note = hi->notes;
532 hi->notes = note->next;
535 if (hi->email_addr) {
536 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
537 handle_info_list_remove(hil, hi);
539 dict_remove(nickserv_email_dict, hi->email_addr);
544 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
547 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
551 for (n=0; n<unreg_func_used; n++)
552 unreg_func_list[n](notify, hi);
554 set_user_handle_info(hi->users, NULL, 0);
556 if (nickserv_conf.disable_nicks)
557 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
559 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
561 dict_remove(nickserv_handle_dict, hi->handle);
565 get_handle_info(const char *handle)
567 return dict_find(nickserv_handle_dict, handle, 0);
571 get_nick_info(const char *nick)
573 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
577 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
582 for (nn=0; nn<channel->members.used; ++nn) {
583 mn = channel->members.list[nn];
584 if ((mn->user != except) && (mn->user->handle_info == handle))
591 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
592 if (!user->handle_info) {
594 send_message(user, bot, "MSG_AUTHENTICATE");
598 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
600 send_message(user, bot, "NSMSG_NO_ACCESS");
604 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
606 send_message(user, bot, "MSG_OPER_SUSPENDED");
610 if (user->handle_info->opserv_level < min_level) {
612 send_message(user, bot, "NSMSG_NO_ACCESS");
620 is_valid_handle(const char *handle)
622 struct userNode *user;
623 /* cant register a juped nick/service nick as handle, to prevent confusion */
624 user = GetUserH(handle);
625 if (user && IsLocal(user))
627 /* check against maximum length */
628 if (strlen(handle) > NICKSERV_HANDLE_LEN)
630 /* for consistency, only allow account names that could be nicks */
631 if (!is_valid_nick(handle))
633 /* disallow account names that look like bad words */
634 if (opserv_bad_channel(handle))
636 /* test either regex or containing all valid chars */
637 if (nickserv_conf.valid_handle_regex_set) {
638 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
641 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
642 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
646 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
651 is_registerable_nick(const char *nick)
653 /* make sure it could be used as an account name */
654 if (!is_valid_handle(nick))
657 if (strlen(nick) > NICKLEN)
659 /* test either regex or as valid handle */
660 if (nickserv_conf.valid_nick_regex_set) {
661 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
664 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
665 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
673 is_valid_email_addr(const char *email)
675 return strchr(email, '@') != NULL;
679 visible_email_addr(struct userNode *user, struct handle_info *hi)
681 if (hi->email_addr) {
682 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
683 return hi->email_addr;
693 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
695 struct handle_info *hi;
696 struct userNode *target;
700 if (!(hi = get_handle_info(++name))) {
701 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
706 if (!(target = GetUserH(name))) {
707 send_message(user, service, "MSG_NICK_UNKNOWN", name);
710 if (IsLocal(target)) {
711 if (IsService(target))
712 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
714 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
717 if (!(hi = target->handle_info)) {
718 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
726 oper_outranks(struct userNode *user, struct handle_info *hi) {
727 if (user->handle_info->opserv_level > hi->opserv_level)
729 if (user->handle_info->opserv_level == hi->opserv_level) {
730 if ((user->handle_info->opserv_level == 1000)
731 || (user->handle_info == hi)
732 || ((user->handle_info->opserv_level == 0)
733 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
734 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
738 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
742 static struct handle_info *
743 get_victim_oper(struct userNode *user, const char *target)
745 struct handle_info *hi;
746 if (!(hi = smart_get_handle_info(nickserv, user, target)))
748 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
749 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
752 return oper_outranks(user, hi) ? hi : NULL;
756 valid_user_for(struct userNode *user, struct handle_info *hi)
760 /* If no hostmasks on the account, allow it. */
761 if (!hi->masks->used)
763 /* If any hostmask matches, allow it. */
764 for (ii=0; ii<hi->masks->used; ii++)
765 if (user_matches_glob(user, hi->masks->list[ii], 0))
767 /* If they are allowauthed to this account, allow it (removing the aa). */
768 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
769 dict_remove(nickserv_allow_auth_dict, user->nick);
772 /* The user is not allowed to use this account. */
777 is_secure_password(const char *handle, const char *pass, struct userNode *user)
780 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
784 if (len < nickserv_conf.password_min_length) {
786 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
789 if (!irccasecmp(pass, handle)) {
791 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
794 dict_find(nickserv_conf.weak_password_dict, pass, &p);
797 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
800 for (i=0; i<len; i++) {
801 if (isdigit(pass[i]))
803 if (isupper(pass[i]))
805 if (islower(pass[i]))
808 if ((cnt_lower < nickserv_conf.password_min_lower)
809 || (cnt_upper < nickserv_conf.password_min_upper)
810 || (cnt_digits < nickserv_conf.password_min_digits)) {
812 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
818 static auth_func_t *auth_func_list;
819 static unsigned int auth_func_size = 0, auth_func_used = 0;
822 reg_auth_func(auth_func_t func)
824 if (auth_func_used == auth_func_size) {
825 if (auth_func_size) {
826 auth_func_size <<= 1;
827 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
830 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
833 auth_func_list[auth_func_used++] = func;
836 static handle_rename_func_t *rf_list;
837 static unsigned int rf_list_size, rf_list_used;
840 reg_handle_rename_func(handle_rename_func_t func)
842 if (rf_list_used == rf_list_size) {
845 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
848 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
851 rf_list[rf_list_used++] = func;
855 generate_fakehost(struct handle_info *handle)
857 extern const char *hidden_host_suffix;
858 static char buffer[HOSTLEN+1];
860 if (!handle->fakehost) {
861 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
863 } else if (handle->fakehost[0] == '.') {
864 /* A leading dot indicates the stored value is actually a title. */
865 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
868 return handle->fakehost;
872 apply_fakehost(struct handle_info *handle)
874 struct userNode *target;
879 fake = generate_fakehost(handle);
880 for (target = handle->users; target; target = target->next_authed)
881 assign_fakehost(target, fake, 1);
885 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
888 struct handle_info *old_info;
890 /* This can happen if somebody uses COOKIE while authed, or if
891 * they re-auth to their current handle (which is silly, but users
893 if (user->handle_info == hi)
896 if (user->handle_info) {
897 struct userNode *other;
900 userList_remove(&curr_helpers, user);
902 /* remove from next_authed linked list */
903 if (user->handle_info->users == user) {
904 user->handle_info->users = user->next_authed;
906 for (other = user->handle_info->users;
907 other->next_authed != user;
908 other = other->next_authed) ;
909 other->next_authed = user->next_authed;
911 /* if nobody left on old handle, and they're not an oper, remove !god */
912 if (!user->handle_info->users && !user->handle_info->opserv_level)
913 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
914 /* record them as being last seen at this time */
915 user->handle_info->lastseen = now;
916 /* and record their hostmask */
917 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
919 old_info = user->handle_info;
920 user->handle_info = hi;
921 if (hi && !hi->users && !hi->opserv_level)
922 HANDLE_CLEAR_FLAG(hi, HELPING);
923 for (n=0; n<auth_func_used; n++)
924 auth_func_list[n](user, old_info);
926 struct nick_info *ni;
928 HANDLE_CLEAR_FLAG(hi, FROZEN);
929 if (nickserv_conf.warn_clone_auth) {
930 struct userNode *other;
931 for (other = hi->users; other; other = other->next_authed)
932 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
934 user->next_authed = hi->users;
938 userList_append(&curr_helpers, user);
940 if (hi->fakehost || old_info)
944 #ifdef WITH_PROTOCOL_BAHAMUT
945 /* Stamp users with their account ID. */
947 inttobase64(id, hi->id, IDLEN);
948 #elif WITH_PROTOCOL_P10
949 /* Stamp users with their account name. */
950 char *id = hi->handle;
952 const char *id = "???";
954 if (!nickserv_conf.disable_nicks) {
955 struct nick_info *ni;
956 for (ni = hi->nicks; ni; ni = ni->next) {
957 if (!irccasecmp(user->nick, ni->nick)) {
958 user->modes |= FLAGS_REGNICK;
966 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
967 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
969 /* We cannot clear the user's account ID, unfortunately. */
970 user->next_authed = NULL;
974 static struct handle_info*
975 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
977 struct handle_info *hi;
978 struct nick_info *ni;
979 char crypted[MD5_CRYPT_LENGTH];
981 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
982 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
986 if (!is_secure_password(handle, passwd, user))
989 cryptpass(passwd, crypted);
990 hi = register_handle(handle, crypted, 0);
991 hi->masks = alloc_string_list(1);
993 hi->language = lang_C;
994 hi->registered = now;
996 hi->flags = HI_DEFAULT_FLAGS;
997 if (settee && !no_auth)
998 set_user_handle_info(settee, hi, 1);
1001 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1002 else if (nickserv_conf.disable_nicks)
1003 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1004 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1005 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1007 register_nick(user->nick, hi);
1008 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1010 if (settee && (user != settee))
1011 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1016 nickserv_bake_cookie(struct handle_cookie *cookie)
1018 cookie->hi->cookie = cookie;
1019 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1023 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1025 struct handle_cookie *cookie;
1026 char subject[128], body[4096], *misc;
1027 const char *netname, *fmt;
1031 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1035 cookie = calloc(1, sizeof(*cookie));
1037 cookie->type = type;
1038 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1039 cookie->expires = now + nickserv_conf.cookie_timeout;
1040 inttobase64(cookie->cookie, rand(), 5);
1041 inttobase64(cookie->cookie+5, rand(), 5);
1043 netname = nickserv_conf.network_name;
1046 switch (cookie->type) {
1048 hi->passwd[0] = 0; /* invalidate password */
1049 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1050 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1051 snprintf(subject, sizeof(subject), fmt, netname);
1052 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1053 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1056 case PASSWORD_CHANGE:
1057 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1058 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1059 snprintf(subject, sizeof(subject), fmt, netname);
1060 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1061 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1064 misc = hi->email_addr;
1065 hi->email_addr = cookie->data;
1067 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1068 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1069 snprintf(subject, sizeof(subject), fmt, netname);
1070 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1071 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1072 mail_send(nickserv, hi, subject, body, 1);
1073 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1074 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1076 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1077 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1078 snprintf(subject, sizeof(subject), fmt, netname);
1079 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1080 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1081 mail_send(nickserv, hi, subject, body, 1);
1084 hi->email_addr = misc;
1087 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1088 snprintf(subject, sizeof(subject), fmt, netname);
1089 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1090 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1091 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1094 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1098 mail_send(nickserv, hi, subject, body, first_time);
1099 nickserv_bake_cookie(cookie);
1103 nickserv_eat_cookie(struct handle_cookie *cookie)
1105 cookie->hi->cookie = NULL;
1106 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1107 nickserv_free_cookie(cookie);
1111 nickserv_free_email_addr(void *data)
1113 handle_info_list_clean(data);
1118 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1120 struct handle_info_list *hil;
1121 /* Remove from old handle_info_list ... */
1122 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1123 handle_info_list_remove(hil, hi);
1124 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1125 hi->email_addr = NULL;
1127 /* Add to the new list.. */
1128 if (new_email_addr) {
1129 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1130 hil = calloc(1, sizeof(*hil));
1131 hil->tag = strdup(new_email_addr);
1132 handle_info_list_init(hil);
1133 dict_insert(nickserv_email_dict, hil->tag, hil);
1135 handle_info_list_append(hil, hi);
1136 hi->email_addr = hil->tag;
1140 static NICKSERV_FUNC(cmd_register)
1143 struct handle_info *hi;
1144 const char *email_addr, *password;
1147 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1148 /* Require the first handle registered to belong to someone +o. */
1149 reply("NSMSG_REQUIRE_OPER");
1153 if (user->handle_info) {
1154 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1158 if (IsRegistering(user)) {
1159 reply("NSMSG_ALREADY_REGISTERING");
1163 if (IsStamped(user)) {
1164 /* Unauthenticated users might still have been stamped
1165 previously and could therefore have a hidden host;
1166 do not allow them to register a new account. */
1167 reply("NSMSG_STAMPED_REGISTER");
1171 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1173 if (!is_valid_handle(argv[1])) {
1174 reply("NSMSG_BAD_HANDLE", argv[1]);
1178 if ((argc >= 4) && nickserv_conf.email_enabled) {
1179 struct handle_info_list *hil;
1182 /* Remember email address. */
1183 email_addr = argv[3];
1185 /* Check that the email address looks valid.. */
1186 if (!is_valid_email_addr(email_addr)) {
1187 reply("NSMSG_BAD_EMAIL_ADDR");
1191 /* .. and that we are allowed to send to it. */
1192 if ((str = mail_prohibited_address(email_addr))) {
1193 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1197 /* If we do email verify, make sure we don't spam the address. */
1198 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1200 for (nn=0; nn<hil->used; nn++) {
1201 if (hil->list[nn]->cookie) {
1202 reply("NSMSG_EMAIL_UNACTIVATED");
1206 if (hil->used >= nickserv_conf.handles_per_email) {
1207 reply("NSMSG_EMAIL_OVERUSED");
1220 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1222 /* Add any masks they should get. */
1223 if (nickserv_conf.default_hostmask) {
1224 string_list_append(hi->masks, strdup("*@*"));
1226 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1227 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1228 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1231 /* If they're the first to register, give them level 1000. */
1232 if (dict_size(nickserv_handle_dict) == 1) {
1233 hi->opserv_level = 1000;
1234 reply("NSMSG_ROOT_HANDLE", argv[1]);
1237 /* Set their email address. */
1239 nickserv_set_email_addr(hi, email_addr);
1241 /* If they need to do email verification, tell them. */
1243 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1245 /* Set registering flag.. */
1246 user->modes |= FLAGS_REGISTERING;
1251 static NICKSERV_FUNC(cmd_oregister)
1254 struct userNode *settee;
1255 struct handle_info *hi;
1257 NICKSERV_MIN_PARMS(3);
1259 if (!is_valid_handle(argv[1])) {
1260 reply("NSMSG_BAD_HANDLE", argv[1]);
1267 } else if (strchr(argv[3], '@')) {
1268 mask = canonicalize_hostmask(strdup(argv[3]));
1270 settee = GetUserH(argv[4]);
1272 reply("MSG_NICK_UNKNOWN", argv[4]);
1279 } else if ((settee = GetUserH(argv[3]))) {
1280 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1282 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1285 if (settee && settee->handle_info) {
1286 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1290 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1295 string_list_append(hi->masks, mask);
1299 static NICKSERV_FUNC(cmd_handleinfo)
1302 unsigned int i, pos=0, herelen;
1303 struct userNode *target, *next_un;
1304 struct handle_info *hi;
1305 const char *nsmsg_none;
1308 if (!(hi = user->handle_info)) {
1309 reply("NSMSG_MUST_AUTH");
1312 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1316 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1317 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1318 #ifdef WITH_PROTOCOL_BAHAMUT
1319 reply("NSMSG_HANDLEINFO_ID", hi->id);
1321 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1324 intervalString(buff, now - hi->lastseen, user->handle_info);
1325 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1327 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1330 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1331 if (HANDLE_FLAGGED(hi, FROZEN))
1332 reply("NSMSG_HANDLEINFO_VACATION");
1334 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1335 struct do_not_register *dnr;
1336 if ((dnr = chanserv_is_dnr(NULL, hi)))
1337 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1338 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1340 } else if (hi != user->handle_info)
1343 if (nickserv_conf.email_enabled)
1344 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1348 switch (hi->cookie->type) {
1349 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1350 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1351 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1352 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1353 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1359 reply("NSMSG_HANDLEINFO_NO_NOTES");
1361 struct handle_note *prev, *note;
1363 WALK_NOTES(hi, prev, note) {
1364 char set_time[INTERVALLEN];
1365 intervalString(set_time, now - note->set, user->handle_info);
1366 if (note->expires) {
1367 char exp_time[INTERVALLEN];
1368 intervalString(exp_time, note->expires - now, user->handle_info);
1369 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1371 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1377 unsigned long flen = 1;
1378 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1380 for (i=0, flen=1; handle_flags[i]; i++)
1381 if (hi->flags & 1 << i)
1382 flags[flen++] = handle_flags[i];
1384 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1386 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1389 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1390 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1391 || (hi->opserv_level > 0)) {
1392 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1396 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1398 if (hi->last_quit_host[0])
1399 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1401 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1403 if (nickserv_conf.disable_nicks) {
1404 /* nicks disabled; don't show anything about registered nicks */
1405 } else if (hi->nicks) {
1406 struct nick_info *ni, *next_ni;
1407 for (ni = hi->nicks; ni; ni = next_ni) {
1408 herelen = strlen(ni->nick);
1409 if (pos + herelen + 1 > ArrayLength(buff)) {
1411 goto print_nicks_buff;
1415 memcpy(buff+pos, ni->nick, herelen);
1416 pos += herelen; buff[pos++] = ' ';
1420 reply("NSMSG_HANDLEINFO_NICKS", buff);
1425 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1428 if (hi->masks->used) {
1429 for (i=0; i < hi->masks->used; i++) {
1430 herelen = strlen(hi->masks->list[i]);
1431 if (pos + herelen + 1 > ArrayLength(buff)) {
1433 goto print_mask_buff;
1435 memcpy(buff+pos, hi->masks->list[i], herelen);
1436 pos += herelen; buff[pos++] = ' ';
1437 if (i+1 == hi->masks->used) {
1440 reply("NSMSG_HANDLEINFO_MASKS", buff);
1445 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1449 struct userData *channel, *next;
1452 for (channel = hi->channels; channel; channel = next) {
1453 next = channel->u_next;
1454 name = channel->channel->channel->name;
1455 herelen = strlen(name);
1456 if (pos + herelen + 7 > ArrayLength(buff)) {
1458 goto print_chans_buff;
1460 if (IsUserSuspended(channel))
1462 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1466 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1471 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1474 for (target = hi->users; target; target = next_un) {
1475 herelen = strlen(target->nick);
1476 if (pos + herelen + 1 > ArrayLength(buff)) {
1478 goto print_cnick_buff;
1480 next_un = target->next_authed;
1482 memcpy(buff+pos, target->nick, herelen);
1483 pos += herelen; buff[pos++] = ' ';
1487 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1495 static NICKSERV_FUNC(cmd_userinfo)
1497 struct userNode *target;
1499 NICKSERV_MIN_PARMS(2);
1500 if (!(target = GetUserH(argv[1]))) {
1501 reply("MSG_NICK_UNKNOWN", argv[1]);
1504 if (target->handle_info)
1505 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1507 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1511 static NICKSERV_FUNC(cmd_nickinfo)
1513 struct nick_info *ni;
1515 NICKSERV_MIN_PARMS(2);
1516 if (!(ni = get_nick_info(argv[1]))) {
1517 reply("MSG_NICK_UNKNOWN", argv[1]);
1520 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1524 static NICKSERV_FUNC(cmd_rename_handle)
1526 struct handle_info *hi;
1527 char msgbuf[MAXLEN], *old_handle;
1530 NICKSERV_MIN_PARMS(3);
1531 if (!(hi = get_victim_oper(user, argv[1])))
1533 if (!is_valid_handle(argv[2])) {
1534 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1537 if (get_handle_info(argv[2])) {
1538 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1542 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1543 hi->handle = strdup(argv[2]);
1544 dict_insert(nickserv_handle_dict, hi->handle, hi);
1545 for (nn=0; nn<rf_list_used; nn++)
1546 rf_list[nn](hi, old_handle);
1547 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1548 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1549 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1554 static failpw_func_t *failpw_func_list;
1555 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1558 reg_failpw_func(failpw_func_t func)
1560 if (failpw_func_used == failpw_func_size) {
1561 if (failpw_func_size) {
1562 failpw_func_size <<= 1;
1563 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1565 failpw_func_size = 8;
1566 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1569 failpw_func_list[failpw_func_used++] = func;
1572 static NICKSERV_FUNC(cmd_auth)
1574 int pw_arg, used, maxlogins;
1575 struct handle_info *hi;
1577 struct userNode *other;
1579 if (user->handle_info) {
1580 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1583 if (IsStamped(user)) {
1584 /* Unauthenticated users might still have been stamped
1585 previously and could therefore have a hidden host;
1586 do not allow them to authenticate. */
1587 reply("NSMSG_STAMPED_AUTH");
1591 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1593 } else if (argc == 2) {
1594 if (nickserv_conf.disable_nicks) {
1595 if (!(hi = get_handle_info(user->nick))) {
1596 reply("NSMSG_HANDLE_NOT_FOUND");
1600 /* try to look up their handle from their nick */
1601 struct nick_info *ni;
1602 ni = get_nick_info(user->nick);
1604 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1611 reply("MSG_MISSING_PARAMS", argv[0]);
1612 svccmd_send_help(user, nickserv, cmd);
1616 reply("NSMSG_HANDLE_NOT_FOUND");
1619 /* Responses from here on look up the language used by the handle they asked about. */
1620 passwd = argv[pw_arg];
1621 if (!valid_user_for(user, hi)) {
1622 if (hi->email_addr && nickserv_conf.email_enabled)
1623 send_message_type(4, user, cmd->parent->bot,
1624 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1627 send_message_type(4, user, cmd->parent->bot,
1628 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1630 argv[pw_arg] = "BADMASK";
1633 if (!checkpass(passwd, hi->passwd)) {
1635 send_message_type(4, user, cmd->parent->bot,
1636 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1637 argv[pw_arg] = "BADPASS";
1638 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1639 if (nickserv_conf.autogag_enabled) {
1640 if (!user->auth_policer.params) {
1641 user->auth_policer.last_req = now;
1642 user->auth_policer.params = nickserv_conf.auth_policer_params;
1644 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1646 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1647 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1648 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1650 argv[pw_arg] = "GAGGED";
1655 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1656 send_message_type(4, user, cmd->parent->bot,
1657 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1658 argv[pw_arg] = "SUSPENDED";
1661 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1662 for (used = 0, other = hi->users; other; other = other->next_authed) {
1663 if (++used >= maxlogins) {
1664 send_message_type(4, user, cmd->parent->bot,
1665 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1667 argv[pw_arg] = "MAXLOGINS";
1672 set_user_handle_info(user, hi, 1);
1673 if (nickserv_conf.email_required && !hi->email_addr)
1674 reply("NSMSG_PLEASE_SET_EMAIL");
1675 if (!is_secure_password(hi->handle, passwd, NULL))
1676 reply("NSMSG_WEAK_PASSWORD");
1677 if (hi->passwd[0] != '$')
1678 cryptpass(passwd, hi->passwd);
1679 if (!hi->masks->used) {
1681 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1682 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1683 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1685 argv[pw_arg] = "****";
1686 reply("NSMSG_AUTH_SUCCESS");
1690 static allowauth_func_t *allowauth_func_list;
1691 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1694 reg_allowauth_func(allowauth_func_t func)
1696 if (allowauth_func_used == allowauth_func_size) {
1697 if (allowauth_func_size) {
1698 allowauth_func_size <<= 1;
1699 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1701 allowauth_func_size = 8;
1702 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1705 allowauth_func_list[allowauth_func_used++] = func;
1708 static NICKSERV_FUNC(cmd_allowauth)
1710 struct userNode *target;
1711 struct handle_info *hi;
1714 NICKSERV_MIN_PARMS(2);
1715 if (!(target = GetUserH(argv[1]))) {
1716 reply("MSG_NICK_UNKNOWN", argv[1]);
1719 if (target->handle_info) {
1720 reply("NSMSG_USER_PREV_AUTH", target->nick);
1723 if (IsStamped(target)) {
1724 /* Unauthenticated users might still have been stamped
1725 previously and could therefore have a hidden host;
1726 do not allow them to authenticate to an account. */
1727 reply("NSMSG_USER_PREV_STAMP", target->nick);
1732 else if (!(hi = get_handle_info(argv[2]))) {
1733 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1737 if (hi->opserv_level > user->handle_info->opserv_level) {
1738 reply("MSG_USER_OUTRANKED", hi->handle);
1741 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1742 || (hi->opserv_level > 0))
1743 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1744 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1747 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1748 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1749 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1750 if (nickserv_conf.email_enabled)
1751 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1753 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1754 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1756 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1758 for (n=0; n<allowauth_func_used; n++)
1759 allowauth_func_list[n](user, target, hi);
1763 static NICKSERV_FUNC(cmd_authcookie)
1765 struct handle_info *hi;
1767 NICKSERV_MIN_PARMS(2);
1768 if (user->handle_info) {
1769 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1772 if (IsStamped(user)) {
1773 /* Unauthenticated users might still have been stamped
1774 previously and could therefore have a hidden host;
1775 do not allow them to authenticate to an account. */
1776 reply("NSMSG_STAMPED_AUTHCOOKIE");
1779 if (!(hi = get_handle_info(argv[1]))) {
1780 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1783 if (!hi->email_addr) {
1784 reply("MSG_SET_EMAIL_ADDR");
1787 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1791 static NICKSERV_FUNC(cmd_delcookie)
1793 struct handle_info *hi;
1795 hi = user->handle_info;
1797 reply("NSMSG_NO_COOKIE");
1800 switch (hi->cookie->type) {
1803 reply("NSMSG_MUST_TIME_OUT");
1806 nickserv_eat_cookie(hi->cookie);
1807 reply("NSMSG_ATE_COOKIE");
1813 static NICKSERV_FUNC(cmd_resetpass)
1815 struct handle_info *hi;
1816 char crypted[MD5_CRYPT_LENGTH];
1818 NICKSERV_MIN_PARMS(3);
1819 if (user->handle_info) {
1820 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1823 if (IsStamped(user)) {
1824 /* Unauthenticated users might still have been stamped
1825 previously and could therefore have a hidden host;
1826 do not allow them to activate an account. */
1827 reply("NSMSG_STAMPED_RESETPASS");
1830 if (!(hi = get_handle_info(argv[1]))) {
1831 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1834 if (!hi->email_addr) {
1835 reply("MSG_SET_EMAIL_ADDR");
1838 cryptpass(argv[2], crypted);
1840 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1844 static NICKSERV_FUNC(cmd_cookie)
1846 struct handle_info *hi;
1849 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1852 NICKSERV_MIN_PARMS(3);
1853 if (!(hi = get_handle_info(argv[1]))) {
1854 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1860 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1861 reply("NSMSG_HANDLE_SUSPENDED");
1866 reply("NSMSG_NO_COOKIE");
1870 /* Check validity of operation before comparing cookie to
1871 * prohibit guessing by authed users. */
1872 if (user->handle_info
1873 && (hi->cookie->type != EMAIL_CHANGE)
1874 && (hi->cookie->type != PASSWORD_CHANGE)) {
1875 reply("NSMSG_CANNOT_COOKIE");
1879 if (strcmp(cookie, hi->cookie->cookie)) {
1880 reply("NSMSG_BAD_COOKIE");
1884 switch (hi->cookie->type) {
1886 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1887 set_user_handle_info(user, hi, 1);
1888 reply("NSMSG_HANDLE_ACTIVATED");
1890 case PASSWORD_CHANGE:
1891 set_user_handle_info(user, hi, 1);
1892 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1893 reply("NSMSG_PASSWORD_CHANGED");
1896 nickserv_set_email_addr(hi, hi->cookie->data);
1897 reply("NSMSG_EMAIL_CHANGED");
1900 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1901 set_user_handle_info(user, hi, 1);
1902 nickserv_addmask(user, hi, mask);
1903 reply("NSMSG_AUTH_SUCCESS");
1908 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1909 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1913 nickserv_eat_cookie(hi->cookie);
1918 static NICKSERV_FUNC(cmd_oregnick) {
1920 struct handle_info *target;
1921 struct nick_info *ni;
1923 NICKSERV_MIN_PARMS(3);
1924 if (!(target = modcmd_get_handle_info(user, argv[1])))
1927 if (!is_registerable_nick(nick)) {
1928 reply("NSMSG_BAD_NICK", nick);
1931 ni = dict_find(nickserv_nick_dict, nick, NULL);
1933 reply("NSMSG_NICK_EXISTS", nick);
1936 register_nick(nick, target);
1937 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1941 static NICKSERV_FUNC(cmd_regnick) {
1943 struct nick_info *ni;
1945 if (!is_registerable_nick(user->nick)) {
1946 reply("NSMSG_BAD_NICK", user->nick);
1949 /* count their nicks, see if it's too many */
1950 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1951 if (n >= nickserv_conf.nicks_per_handle) {
1952 reply("NSMSG_TOO_MANY_NICKS");
1955 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1957 reply("NSMSG_NICK_EXISTS", user->nick);
1960 register_nick(user->nick, user->handle_info);
1961 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1965 static NICKSERV_FUNC(cmd_pass)
1967 struct handle_info *hi;
1968 const char *old_pass, *new_pass;
1970 NICKSERV_MIN_PARMS(3);
1971 hi = user->handle_info;
1975 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1976 if (!checkpass(old_pass, hi->passwd)) {
1977 argv[1] = "BADPASS";
1978 reply("NSMSG_PASSWORD_INVALID");
1981 cryptpass(new_pass, hi->passwd);
1983 reply("NSMSG_PASS_SUCCESS");
1988 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1991 char *new_mask = canonicalize_hostmask(strdup(mask));
1992 for (i=0; i<hi->masks->used; i++) {
1993 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1994 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1999 string_list_append(hi->masks, new_mask);
2000 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2004 static NICKSERV_FUNC(cmd_addmask)
2007 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2008 int res = nickserv_addmask(user, user->handle_info, mask);
2012 if (!is_gline(argv[1])) {
2013 reply("NSMSG_MASK_INVALID", argv[1]);
2016 return nickserv_addmask(user, user->handle_info, argv[1]);
2020 static NICKSERV_FUNC(cmd_oaddmask)
2022 struct handle_info *hi;
2024 NICKSERV_MIN_PARMS(3);
2025 if (!(hi = get_victim_oper(user, argv[1])))
2027 return nickserv_addmask(user, hi, argv[2]);
2031 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2034 for (i=0; i<hi->masks->used; i++) {
2035 if (!strcmp(del_mask, hi->masks->list[i])) {
2036 char *old_mask = hi->masks->list[i];
2037 if (hi->masks->used == 1 && !force) {
2038 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2041 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2042 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2047 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2051 static NICKSERV_FUNC(cmd_delmask)
2053 NICKSERV_MIN_PARMS(2);
2054 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2057 static NICKSERV_FUNC(cmd_odelmask)
2059 struct handle_info *hi;
2060 NICKSERV_MIN_PARMS(3);
2061 if (!(hi = get_victim_oper(user, argv[1])))
2063 return nickserv_delmask(user, hi, argv[2], 1);
2067 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2068 unsigned int nn, add = 1, pos;
2069 unsigned long added, removed, flag;
2071 for (added=removed=nn=0; str[nn]; nn++) {
2073 case '+': add = 1; break;
2074 case '-': add = 0; break;
2076 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2077 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2080 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2081 /* cheesy avoidance of looking up the flag name.. */
2082 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2085 flag = 1 << (pos - 1);
2087 added |= flag, removed &= ~flag;
2089 removed |= flag, added &= ~flag;
2094 *premoved = removed;
2099 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2101 unsigned long before, after, added, removed;
2102 struct userNode *uNode;
2104 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2105 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2107 hi->flags = (hi->flags | added) & ~removed;
2108 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2110 /* Strip helping flag if they're only a support helper and not
2111 * currently in #support. */
2112 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2113 struct channelList *schannels;
2115 schannels = chanserv_support_channels();
2116 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2117 for (ii = 0; ii < schannels->used; ++ii)
2118 if (GetUserMode(schannels->list[ii], uNode))
2120 if (ii < schannels->used)
2124 HANDLE_CLEAR_FLAG(hi, HELPING);
2127 if (after && !before) {
2128 /* Add user to current helper list. */
2129 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2130 userList_append(&curr_helpers, uNode);
2131 } else if (!after && before) {
2132 /* Remove user from current helper list. */
2133 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2134 userList_remove(&curr_helpers, uNode);
2141 set_list(struct userNode *user, struct handle_info *hi, int override)
2145 char *set_display[] = {
2146 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2147 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2150 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2152 /* Do this so options are presented in a consistent order. */
2153 for (i = 0; i < ArrayLength(set_display); ++i)
2154 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2155 opt(user, hi, override, 0, NULL);
2158 static NICKSERV_FUNC(cmd_set)
2160 struct handle_info *hi;
2163 hi = user->handle_info;
2165 set_list(user, hi, 0);
2168 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2169 reply("NSMSG_INVALID_OPTION", argv[1]);
2172 return opt(user, hi, 0, argc-1, argv+1);
2175 static NICKSERV_FUNC(cmd_oset)
2177 struct handle_info *hi;
2180 NICKSERV_MIN_PARMS(2);
2182 if (!(hi = get_victim_oper(user, argv[1])))
2186 set_list(user, hi, 0);
2190 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2191 reply("NSMSG_INVALID_OPTION", argv[2]);
2195 return opt(user, hi, 1, argc-2, argv+2);
2198 static OPTION_FUNC(opt_info)
2202 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2204 hi->infoline = NULL;
2206 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2210 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2211 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2215 static OPTION_FUNC(opt_width)
2218 hi->screen_width = strtoul(argv[1], NULL, 0);
2220 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2221 hi->screen_width = MIN_LINE_SIZE;
2222 else if (hi->screen_width > MAX_LINE_SIZE)
2223 hi->screen_width = MAX_LINE_SIZE;
2225 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2229 static OPTION_FUNC(opt_tablewidth)
2232 hi->table_width = strtoul(argv[1], NULL, 0);
2234 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2235 hi->table_width = MIN_LINE_SIZE;
2236 else if (hi->screen_width > MAX_LINE_SIZE)
2237 hi->table_width = MAX_LINE_SIZE;
2239 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2243 static OPTION_FUNC(opt_color)
2246 if (enabled_string(argv[1]))
2247 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2248 else if (disabled_string(argv[1]))
2249 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2251 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2256 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2260 static OPTION_FUNC(opt_privmsg)
2263 if (enabled_string(argv[1]))
2264 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2265 else if (disabled_string(argv[1]))
2266 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2268 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2273 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2277 static OPTION_FUNC(opt_style)
2282 if (!irccasecmp(argv[1], "Zoot"))
2283 hi->userlist_style = HI_STYLE_ZOOT;
2284 else if (!irccasecmp(argv[1], "def"))
2285 hi->userlist_style = HI_STYLE_DEF;
2288 switch (hi->userlist_style) {
2297 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2301 static OPTION_FUNC(opt_announcements)
2306 if (enabled_string(argv[1]))
2307 hi->announcements = 'y';
2308 else if (disabled_string(argv[1]))
2309 hi->announcements = 'n';
2310 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2311 hi->announcements = '?';
2313 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2318 switch (hi->announcements) {
2319 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2320 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2321 case '?': choice = "default"; break;
2322 default: choice = "unknown"; break;
2324 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2328 static OPTION_FUNC(opt_password)
2331 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2336 cryptpass(argv[1], hi->passwd);
2338 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2342 static OPTION_FUNC(opt_flags)
2345 unsigned int ii, flen;
2348 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2353 nickserv_apply_flags(user, hi, argv[1]);
2355 for (ii = flen = 0; handle_flags[ii]; ii++)
2356 if (hi->flags & (1 << ii))
2357 flags[flen++] = handle_flags[ii];
2360 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2362 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2366 static OPTION_FUNC(opt_email)
2370 if (!is_valid_email_addr(argv[1])) {
2371 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2374 if ((str = mail_prohibited_address(argv[1]))) {
2375 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2378 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2379 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2381 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2383 nickserv_set_email_addr(hi, argv[1]);
2385 nickserv_eat_cookie(hi->cookie);
2386 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2389 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2393 static OPTION_FUNC(opt_maxlogins)
2395 unsigned char maxlogins;
2397 maxlogins = strtoul(argv[1], NULL, 0);
2398 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2399 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2402 hi->maxlogins = maxlogins;
2404 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2405 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2409 static OPTION_FUNC(opt_language)
2411 struct language *lang;
2413 lang = language_find(argv[1]);
2414 if (irccasecmp(lang->name, argv[1]))
2415 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2416 hi->language = lang;
2418 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2423 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2424 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2426 if ((user->handle_info->opserv_level < target->opserv_level)
2427 || ((user->handle_info->opserv_level == target->opserv_level)
2428 && (user->handle_info->opserv_level < 1000))) {
2429 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2432 if ((user->handle_info->opserv_level < new_level)
2433 || ((user->handle_info->opserv_level == new_level)
2434 && (user->handle_info->opserv_level < 1000))) {
2435 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2438 if (user->handle_info == target) {
2439 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2442 if (target->opserv_level == new_level)
2444 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2445 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2446 target->opserv_level = new_level;
2450 static OPTION_FUNC(opt_level)
2455 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2459 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2460 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2464 static OPTION_FUNC(opt_epithet)
2467 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2471 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2472 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2475 if ((epithet[0] == '*') && !epithet[1])
2478 hi->epithet = strdup(epithet);
2482 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2484 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2488 static OPTION_FUNC(opt_title)
2493 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2497 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2499 if (strchr(title, '.')) {
2500 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2503 if ((strlen(user->handle_info->handle) + strlen(title) +
2504 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2505 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2510 if (!strcmp(title, "*")) {
2511 hi->fakehost = NULL;
2513 hi->fakehost = malloc(strlen(title)+2);
2514 hi->fakehost[0] = '.';
2515 strcpy(hi->fakehost+1, title);
2518 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2519 title = hi->fakehost + 1;
2523 title = user_find_message(user, "MSG_NONE");
2524 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2528 static OPTION_FUNC(opt_fakehost)
2533 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2537 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2539 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2540 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2544 if (!strcmp(fake, "*"))
2545 hi->fakehost = NULL;
2547 hi->fakehost = strdup(fake);
2548 fake = hi->fakehost;
2551 fake = generate_fakehost(hi);
2553 fake = user_find_message(user, "MSG_NONE");
2554 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2558 static NICKSERV_FUNC(cmd_reclaim)
2560 struct handle_info *hi;
2561 struct nick_info *ni;
2562 struct userNode *victim;
2564 NICKSERV_MIN_PARMS(2);
2565 hi = user->handle_info;
2566 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2568 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2571 if (ni->owner != user->handle_info) {
2572 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2575 victim = GetUserH(ni->nick);
2577 reply("MSG_NICK_UNKNOWN", ni->nick);
2580 if (victim == user) {
2581 reply("NSMSG_NICK_USER_YOU");
2584 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2585 switch (nickserv_conf.reclaim_action) {
2586 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2587 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2588 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2589 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2594 static NICKSERV_FUNC(cmd_unregnick)
2597 struct handle_info *hi;
2598 struct nick_info *ni;
2600 hi = user->handle_info;
2601 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2602 ni = dict_find(nickserv_nick_dict, nick, NULL);
2604 reply("NSMSG_UNKNOWN_NICK", nick);
2607 if (hi != ni->owner) {
2608 reply("NSMSG_NOT_YOUR_NICK", nick);
2611 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2616 static NICKSERV_FUNC(cmd_ounregnick)
2618 struct nick_info *ni;
2620 NICKSERV_MIN_PARMS(2);
2621 if (!(ni = get_nick_info(argv[1]))) {
2622 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2625 if (!oper_outranks(user, ni->owner))
2627 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2632 static NICKSERV_FUNC(cmd_unregister)
2634 struct handle_info *hi;
2637 NICKSERV_MIN_PARMS(2);
2638 hi = user->handle_info;
2641 if (checkpass(passwd, hi->passwd)) {
2642 nickserv_unregister_handle(hi, user);
2645 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2646 reply("NSMSG_PASSWORD_INVALID");
2651 static NICKSERV_FUNC(cmd_ounregister)
2653 struct handle_info *hi;
2654 char reason[MAXLEN];
2656 NICKSERV_MIN_PARMS(2);
2657 if (!(hi = get_victim_oper(user, argv[1])))
2659 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2660 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2661 nickserv_unregister_handle(hi, user);
2665 static NICKSERV_FUNC(cmd_status)
2667 if (nickserv_conf.disable_nicks) {
2668 reply("NSMSG_GLOBAL_STATS_NONICK",
2669 dict_size(nickserv_handle_dict));
2671 if (user->handle_info) {
2673 struct nick_info *ni;
2674 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2675 reply("NSMSG_HANDLE_STATS", cnt);
2677 reply("NSMSG_HANDLE_NONE");
2679 reply("NSMSG_GLOBAL_STATS",
2680 dict_size(nickserv_handle_dict),
2681 dict_size(nickserv_nick_dict));
2686 static NICKSERV_FUNC(cmd_ghost)
2688 struct userNode *target;
2689 char reason[MAXLEN];
2691 NICKSERV_MIN_PARMS(2);
2692 if (!(target = GetUserH(argv[1]))) {
2693 reply("MSG_NICK_UNKNOWN", argv[1]);
2696 if (target == user) {
2697 reply("NSMSG_CANNOT_GHOST_SELF");
2700 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2701 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2704 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2705 DelUser(target, nickserv, 1, reason);
2706 reply("NSMSG_GHOST_KILLED", argv[1]);
2710 static NICKSERV_FUNC(cmd_vacation)
2712 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2713 reply("NSMSG_ON_VACATION");
2717 static NICKSERV_FUNC(cmd_addnote)
2719 struct handle_info *hi;
2720 unsigned long duration;
2723 struct handle_note *prev;
2724 struct handle_note *note;
2726 /* Parse parameters and figure out values for note's fields. */
2727 NICKSERV_MIN_PARMS(4);
2728 hi = get_victim_oper(user, argv[1]);
2731 duration = ParseInterval(argv[2]);
2732 if (duration > 2*365*86400) {
2733 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2736 unsplit_string(argv + 3, argc - 3, text);
2737 WALK_NOTES(hi, prev, note) {}
2738 id = prev ? (prev->id + 1) : 1;
2740 /* Create the new note structure. */
2741 note = calloc(1, sizeof(*note) + strlen(text));
2743 note->expires = duration ? (now + duration) : 0;
2746 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2747 strcpy(note->note, text);
2752 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2756 static NICKSERV_FUNC(cmd_delnote)
2758 struct handle_info *hi;
2759 struct handle_note *prev;
2760 struct handle_note *note;
2763 NICKSERV_MIN_PARMS(3);
2764 hi = get_victim_oper(user, argv[1]);
2767 id = strtoul(argv[2], NULL, 10);
2768 WALK_NOTES(hi, prev, note) {
2769 if (id == note->id) {
2771 prev->next = note->next;
2773 hi->notes = note->next;
2775 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2779 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2784 nickserv_saxdb_write(struct saxdb_context *ctx) {
2786 struct handle_info *hi;
2789 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2791 #ifdef WITH_PROTOCOL_BAHAMUT
2794 saxdb_start_record(ctx, iter_key(it), 0);
2795 if (hi->announcements != '?') {
2796 flags[0] = hi->announcements;
2798 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2801 struct handle_cookie *cookie = hi->cookie;
2804 switch (cookie->type) {
2805 case ACTIVATION: type = KEY_ACTIVATION; break;
2806 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2807 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2808 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2809 default: type = NULL; break;
2812 saxdb_start_record(ctx, KEY_COOKIE, 0);
2813 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2814 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2816 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2817 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2818 saxdb_end_record(ctx);
2822 struct handle_note *prev, *note;
2823 saxdb_start_record(ctx, KEY_NOTES, 0);
2824 WALK_NOTES(hi, prev, note) {
2825 snprintf(flags, sizeof(flags), "%d", note->id);
2826 saxdb_start_record(ctx, flags, 0);
2828 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
2829 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
2830 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
2831 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
2832 saxdb_end_record(ctx);
2834 saxdb_end_record(ctx);
2837 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2839 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2841 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2845 for (ii=flen=0; handle_flags[ii]; ++ii)
2846 if (hi->flags & (1 << ii))
2847 flags[flen++] = handle_flags[ii];
2849 saxdb_write_string(ctx, KEY_FLAGS, flags);
2851 #ifdef WITH_PROTOCOL_BAHAMUT
2852 saxdb_write_int(ctx, KEY_ID, hi->id);
2855 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2856 if (hi->last_quit_host[0])
2857 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2858 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2859 if (hi->masks->used)
2860 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2862 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2864 struct string_list *slist;
2865 struct nick_info *ni;
2867 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2868 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2869 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2873 if (hi->opserv_level)
2874 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2875 if (hi->language != lang_C)
2876 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2877 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2878 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2879 if (hi->screen_width)
2880 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2881 if (hi->table_width)
2882 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2883 flags[0] = hi->userlist_style;
2885 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2886 saxdb_end_record(ctx);
2891 static handle_merge_func_t *handle_merge_func_list;
2892 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2895 reg_handle_merge_func(handle_merge_func_t func)
2897 if (handle_merge_func_used == handle_merge_func_size) {
2898 if (handle_merge_func_size) {
2899 handle_merge_func_size <<= 1;
2900 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2902 handle_merge_func_size = 8;
2903 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2906 handle_merge_func_list[handle_merge_func_used++] = func;
2909 static NICKSERV_FUNC(cmd_merge)
2911 struct handle_info *hi_from, *hi_to;
2912 struct userNode *last_user;
2913 struct userData *cList, *cListNext;
2914 unsigned int ii, jj, n;
2915 char buffer[MAXLEN];
2917 NICKSERV_MIN_PARMS(3);
2919 if (!(hi_from = get_victim_oper(user, argv[1])))
2921 if (!(hi_to = get_victim_oper(user, argv[2])))
2923 if (hi_to == hi_from) {
2924 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2928 for (n=0; n<handle_merge_func_used; n++)
2929 handle_merge_func_list[n](user, hi_to, hi_from);
2931 /* Append "from" handle's nicks to "to" handle's nick list. */
2933 struct nick_info *last_ni;
2934 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2935 last_ni->next = hi_from->nicks;
2937 while (hi_from->nicks) {
2938 hi_from->nicks->owner = hi_to;
2939 hi_from->nicks = hi_from->nicks->next;
2942 /* Merge the hostmasks. */
2943 for (ii=0; ii<hi_from->masks->used; ii++) {
2944 char *mask = hi_from->masks->list[ii];
2945 for (jj=0; jj<hi_to->masks->used; jj++)
2946 if (match_ircglobs(hi_to->masks->list[jj], mask))
2948 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2949 string_list_append(hi_to->masks, strdup(mask));
2952 /* Merge the lists of authed users. */
2954 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2955 last_user->next_authed = hi_from->users;
2957 hi_to->users = hi_from->users;
2959 /* Repoint the old "from" handle's users. */
2960 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2961 last_user->handle_info = hi_to;
2963 hi_from->users = NULL;
2965 /* Merge channel userlists. */
2966 for (cList=hi_from->channels; cList; cList=cListNext) {
2967 struct userData *cList2;
2968 cListNext = cList->u_next;
2969 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2970 if (cList->channel == cList2->channel)
2972 if (cList2 && (cList2->access >= cList->access)) {
2973 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);
2974 /* keep cList2 in hi_to; remove cList from hi_from */
2975 del_channel_user(cList, 1);
2978 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);
2979 /* remove the lower-ranking cList2 from hi_to */
2980 del_channel_user(cList2, 1);
2982 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
2984 /* cList needs to be moved from hi_from to hi_to */
2985 cList->handle = hi_to;
2986 /* Remove from linked list for hi_from */
2987 assert(!cList->u_prev);
2988 hi_from->channels = cList->u_next;
2990 cList->u_next->u_prev = cList->u_prev;
2991 /* Add to linked list for hi_to */
2992 cList->u_prev = NULL;
2993 cList->u_next = hi_to->channels;
2994 if (hi_to->channels)
2995 hi_to->channels->u_prev = cList;
2996 hi_to->channels = cList;
3000 /* Do they get an OpServ level promotion? */
3001 if (hi_from->opserv_level > hi_to->opserv_level)
3002 hi_to->opserv_level = hi_from->opserv_level;
3004 /* What about last seen time? */
3005 if (hi_from->lastseen > hi_to->lastseen)
3006 hi_to->lastseen = hi_from->lastseen;
3008 /* Does a fakehost carry over? (This intentionally doesn't set it
3009 * for users previously attached to hi_to. They'll just have to
3012 if (hi_from->fakehost && !hi_to->fakehost)
3013 hi_to->fakehost = strdup(hi_from->fakehost);
3015 /* Notify of success. */
3016 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3017 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3018 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3020 /* Unregister the "from" handle. */
3021 nickserv_unregister_handle(hi_from, NULL);
3026 struct nickserv_discrim {
3027 unsigned int limit, min_level, max_level;
3028 unsigned long flags_on, flags_off;
3029 time_t min_registered, max_registered;
3031 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3032 const char *nickmask;
3033 const char *hostmask;
3034 const char *handlemask;
3035 const char *emailmask;
3038 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3040 struct discrim_apply_info {
3041 struct nickserv_discrim *discrim;
3042 discrim_search_func func;
3043 struct userNode *source;
3044 unsigned int matched;
3047 static struct nickserv_discrim *
3048 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3051 struct nickserv_discrim *discrim;
3053 discrim = malloc(sizeof(*discrim));
3054 memset(discrim, 0, sizeof(*discrim));
3055 discrim->min_level = 0;
3056 discrim->max_level = ~0;
3057 discrim->limit = 50;
3058 discrim->min_registered = 0;
3059 discrim->max_registered = INT_MAX;
3060 discrim->lastseen = now;
3062 for (i=0; i<argc; i++) {
3063 if (i == argc - 1) {
3064 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3067 if (!irccasecmp(argv[i], "limit")) {
3068 discrim->limit = strtoul(argv[++i], NULL, 0);
3069 } else if (!irccasecmp(argv[i], "flags")) {
3070 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3071 } else if (!irccasecmp(argv[i], "registered")) {
3072 const char *cmp = argv[++i];
3073 if (cmp[0] == '<') {
3074 if (cmp[1] == '=') {
3075 discrim->min_registered = now - ParseInterval(cmp+2);
3077 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3079 } else if (cmp[0] == '=') {
3080 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3081 } else if (cmp[0] == '>') {
3082 if (cmp[1] == '=') {
3083 discrim->max_registered = now - ParseInterval(cmp+2);
3085 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3088 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3090 } else if (!irccasecmp(argv[i], "seen")) {
3091 discrim->lastseen = now - ParseInterval(argv[++i]);
3092 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3093 discrim->nickmask = argv[++i];
3094 } else if (!irccasecmp(argv[i], "hostmask")) {
3096 if (!irccasecmp(argv[i], "exact")) {
3097 if (i == argc - 1) {
3098 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3101 discrim->hostmask_type = EXACT;
3102 } else if (!irccasecmp(argv[i], "subset")) {
3103 if (i == argc - 1) {
3104 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3107 discrim->hostmask_type = SUBSET;
3108 } else if (!irccasecmp(argv[i], "superset")) {
3109 if (i == argc - 1) {
3110 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3113 discrim->hostmask_type = SUPERSET;
3114 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3115 if (i == argc - 1) {
3116 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3119 discrim->hostmask_type = LASTQUIT;
3122 discrim->hostmask_type = SUPERSET;
3124 discrim->hostmask = argv[++i];
3125 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3126 if (!irccasecmp(argv[++i], "*")) {
3127 discrim->handlemask = 0;
3129 discrim->handlemask = argv[i];
3131 } else if (!irccasecmp(argv[i], "email")) {
3132 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3133 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3135 } else if (!irccasecmp(argv[++i], "*")) {
3136 discrim->emailmask = 0;
3138 discrim->emailmask = argv[i];
3140 } else if (!irccasecmp(argv[i], "access")) {
3141 const char *cmp = argv[++i];
3142 if (cmp[0] == '<') {
3143 if (discrim->min_level == 0) discrim->min_level = 1;
3144 if (cmp[1] == '=') {
3145 discrim->max_level = strtoul(cmp+2, NULL, 0);
3147 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3149 } else if (cmp[0] == '=') {
3150 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3151 } else if (cmp[0] == '>') {
3152 if (cmp[1] == '=') {
3153 discrim->min_level = strtoul(cmp+2, NULL, 0);
3155 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3158 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3161 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3172 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3174 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3175 || (discrim->flags_off & hi->flags)
3176 || (discrim->min_registered > hi->registered)
3177 || (discrim->max_registered < hi->registered)
3178 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3179 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3180 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3181 || (discrim->min_level > hi->opserv_level)
3182 || (discrim->max_level < hi->opserv_level)) {
3185 if (discrim->hostmask) {
3187 for (i=0; i<hi->masks->used; i++) {
3188 const char *mask = hi->masks->list[i];
3189 if ((discrim->hostmask_type == SUBSET)
3190 && (match_ircglobs(discrim->hostmask, mask))) break;
3191 else if ((discrim->hostmask_type == EXACT)
3192 && !irccasecmp(discrim->hostmask, mask)) break;
3193 else if ((discrim->hostmask_type == SUPERSET)
3194 && (match_ircglobs(mask, discrim->hostmask))) break;
3195 else if ((discrim->hostmask_type == LASTQUIT)
3196 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3198 if (i==hi->masks->used) return 0;
3200 if (discrim->nickmask) {
3201 struct nick_info *nick = hi->nicks;
3203 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3206 if (!nick) return 0;
3212 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3214 dict_iterator_t it, next;
3215 unsigned int matched;
3217 for (it = dict_first(nickserv_handle_dict), matched = 0;
3218 it && (matched < discrim->limit);
3220 next = iter_next(it);
3221 if (nickserv_discrim_match(discrim, iter_data(it))) {
3222 dsf(source, iter_data(it));
3230 search_print_func(struct userNode *source, struct handle_info *match)
3232 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3236 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3241 search_unregister_func (struct userNode *source, struct handle_info *match)
3243 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3244 nickserv_unregister_handle(match, source);
3248 nickserv_sort_accounts_by_access(const void *a, const void *b)
3250 const struct handle_info *hi_a = *(const struct handle_info**)a;
3251 const struct handle_info *hi_b = *(const struct handle_info**)b;
3252 if (hi_a->opserv_level != hi_b->opserv_level)
3253 return hi_b->opserv_level - hi_a->opserv_level;
3254 return irccasecmp(hi_a->handle, hi_b->handle);
3258 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3260 struct handle_info_list hil;
3261 struct helpfile_table tbl;
3266 memset(&hil, 0, sizeof(hil));
3267 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3268 struct handle_info *hi = iter_data(it);
3269 if (hi->opserv_level)
3270 handle_info_list_append(&hil, hi);
3272 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3273 tbl.length = hil.used + 1;
3275 tbl.flags = TABLE_NO_FREE;
3276 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3277 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3280 for (ii = 0; ii < hil.used; ) {
3281 ary = malloc(tbl.width * sizeof(ary[0]));
3282 ary[0] = hil.list[ii]->handle;
3283 ary[1] = strtab(hil.list[ii]->opserv_level);
3284 tbl.contents[++ii] = ary;
3286 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3287 reply("MSG_MATCH_COUNT", hil.used);
3288 for (ii = 0; ii < hil.used; ii++)
3289 free(tbl.contents[ii]);
3294 static NICKSERV_FUNC(cmd_search)
3296 struct nickserv_discrim *discrim;
3297 discrim_search_func action;
3298 struct svccmd *subcmd;
3299 unsigned int matches;
3302 NICKSERV_MIN_PARMS(3);
3303 sprintf(buf, "search %s", argv[1]);
3304 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3305 if (!irccasecmp(argv[1], "print"))
3306 action = search_print_func;
3307 else if (!irccasecmp(argv[1], "count"))
3308 action = search_count_func;
3309 else if (!irccasecmp(argv[1], "unregister"))
3310 action = search_unregister_func;
3312 reply("NSMSG_INVALID_ACTION", argv[1]);
3316 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3319 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3323 if (action == search_print_func)
3324 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3325 else if (action == search_count_func)
3326 discrim->limit = INT_MAX;
3328 matches = nickserv_discrim_search(discrim, action, user);
3331 reply("MSG_MATCH_COUNT", matches);
3333 reply("MSG_NO_MATCHES");
3339 static MODCMD_FUNC(cmd_checkpass)
3341 struct handle_info *hi;
3343 NICKSERV_MIN_PARMS(3);
3344 if (!(hi = get_handle_info(argv[1]))) {
3345 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3348 if (checkpass(argv[2], hi->passwd))
3349 reply("CHECKPASS_YES");
3351 reply("CHECKPASS_NO");
3357 nickserv_db_read_handle(const char *handle, dict_t obj)
3360 struct string_list *masks, *slist;
3361 struct handle_info *hi;
3362 struct userNode *authed_users;
3363 struct userData *channels;
3364 unsigned long int id;
3368 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3369 id = str ? strtoul(str, NULL, 0) : 0;
3370 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3372 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3375 if ((hi = get_handle_info(handle))) {
3376 authed_users = hi->users;
3377 channels = hi->channels;
3379 hi->channels = NULL;
3380 dict_remove(nickserv_handle_dict, hi->handle);
3382 authed_users = NULL;
3385 hi = register_handle(handle, str, id);
3387 hi->users = authed_users;
3388 while (authed_users) {
3389 authed_users->handle_info = hi;
3390 authed_users = authed_users->next_authed;
3393 hi->channels = channels;
3394 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3395 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3396 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3397 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3398 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3399 hi->language = language_find(str ? str : "C");
3400 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3401 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3402 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3404 hi->infoline = strdup(str);
3405 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3406 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3407 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3408 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3409 /* We want to read the nicks even if disable_nicks is set. This is so
3410 * that we don't lose the nick data entirely. */
3411 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3413 for (ii=0; ii<slist->used; ii++)
3414 register_nick(slist->list[ii], hi);
3416 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3418 for (ii=0; str[ii]; ii++)
3419 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3421 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3422 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3423 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3424 hi->announcements = str ? str[0] : '?';
3425 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3426 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3427 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3428 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3429 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3431 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3433 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3434 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3436 nickserv_set_email_addr(hi, str);
3437 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3439 hi->epithet = strdup(str);
3440 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3442 hi->fakehost = strdup(str);
3443 /* Read the "cookie" sub-database (if it exists). */
3444 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3446 const char *data, *type, *expires, *cookie_str;
3447 struct handle_cookie *cookie;
3449 cookie = calloc(1, sizeof(*cookie));
3450 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3451 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3452 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3453 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3454 if (!type || !expires || !cookie_str) {
3455 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3458 if (!irccasecmp(type, KEY_ACTIVATION))
3459 cookie->type = ACTIVATION;
3460 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3461 cookie->type = PASSWORD_CHANGE;
3462 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3463 cookie->type = EMAIL_CHANGE;
3464 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3465 cookie->type = ALLOWAUTH;
3467 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3470 cookie->expires = strtoul(expires, NULL, 0);
3471 if (cookie->expires < now)
3474 cookie->data = strdup(data);
3475 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3479 nickserv_bake_cookie(cookie);
3481 nickserv_free_cookie(cookie);
3483 /* Read the "notes" sub-database (if it exists). */
3484 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3487 struct handle_note *last_note;
3488 struct handle_note *note;
3491 for (it = dict_first(subdb); it; it = iter_next(it)) {
3492 const char *expires;
3500 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3502 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", id, hi->handle);
3505 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3506 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3507 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3508 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3509 if (!setter || !text || !set) {
3510 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", id, hi->handle);
3513 note = calloc(1, sizeof(*note) + strlen(text));
3515 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3516 note->set = strtoul(set, NULL, 10);
3517 note->id = strtoul(id, NULL, 10);
3518 safestrncpy(note->setter, setter, sizeof(note->setter));
3519 strcpy(note->note, text);
3521 last_note->next = note;
3530 nickserv_saxdb_read(dict_t db) {
3532 struct record_data *rd;
3534 for (it=dict_first(db); it; it=iter_next(it)) {
3536 nickserv_db_read_handle(iter_key(it), rd->d.object);
3541 static NICKSERV_FUNC(cmd_mergedb)
3543 struct timeval start, stop;
3546 NICKSERV_MIN_PARMS(2);
3547 gettimeofday(&start, NULL);
3548 if (!(db = parse_database(argv[1]))) {
3549 reply("NSMSG_DB_UNREADABLE", argv[1]);
3552 nickserv_saxdb_read(db);
3554 gettimeofday(&stop, NULL);
3555 stop.tv_sec -= start.tv_sec;
3556 stop.tv_usec -= start.tv_usec;
3557 if (stop.tv_usec < 0) {
3559 stop.tv_usec += 1000000;
3561 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3566 expire_handles(UNUSED_ARG(void *data))
3568 dict_iterator_t it, next;
3570 struct handle_info *hi;
3572 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3573 next = iter_next(it);
3575 if ((hi->opserv_level > 0)
3577 || HANDLE_FLAGGED(hi, FROZEN)
3578 || HANDLE_FLAGGED(hi, NODELETE)) {
3581 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3582 if ((now - hi->lastseen) > expiry) {
3583 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3584 nickserv_unregister_handle(hi, NULL);
3588 if (nickserv_conf.handle_expire_frequency)
3589 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3593 nickserv_load_dict(const char *fname)
3597 if (!(file = fopen(fname, "r"))) {
3598 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3601 while (!feof(file)) {
3602 fgets(line, sizeof(line), file);
3605 if (line[strlen(line)-1] == '\n')
3606 line[strlen(line)-1] = 0;
3607 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3610 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3613 static enum reclaim_action
3614 reclaim_action_from_string(const char *str) {
3616 return RECLAIM_NONE;
3617 else if (!irccasecmp(str, "warn"))
3618 return RECLAIM_WARN;
3619 else if (!irccasecmp(str, "svsnick"))
3620 return RECLAIM_SVSNICK;
3621 else if (!irccasecmp(str, "kill"))
3622 return RECLAIM_KILL;
3624 return RECLAIM_NONE;
3628 nickserv_conf_read(void)
3630 dict_t conf_node, child;
3634 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3635 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3638 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3640 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3641 if (nickserv_conf.valid_handle_regex_set)
3642 regfree(&nickserv_conf.valid_handle_regex);
3644 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3645 nickserv_conf.valid_handle_regex_set = !err;
3646 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3648 nickserv_conf.valid_handle_regex_set = 0;
3650 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3651 if (nickserv_conf.valid_nick_regex_set)
3652 regfree(&nickserv_conf.valid_nick_regex);
3654 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3655 nickserv_conf.valid_nick_regex_set = !err;
3656 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3658 nickserv_conf.valid_nick_regex_set = 0;
3660 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3662 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3663 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3664 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3665 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3666 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3667 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3668 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3669 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3670 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3671 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3672 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3673 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3674 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3675 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3676 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3677 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3678 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3679 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3680 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3681 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3682 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3683 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3684 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3685 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3686 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3688 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3689 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3690 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3692 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3693 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3694 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3696 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3697 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3698 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3699 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3700 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3701 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3702 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3703 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3704 if (!nickserv_conf.disable_nicks) {
3705 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3706 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3707 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3708 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3709 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3710 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3711 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3712 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3714 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3715 for (it=dict_first(child); it; it=iter_next(it)) {
3716 const char *key = iter_key(it), *value;
3720 if (!strncasecmp(key, "uc_", 3))
3721 flag = toupper(key[3]);
3722 else if (!strncasecmp(key, "lc_", 3))
3723 flag = tolower(key[3]);
3727 if ((pos = handle_inverse_flags[flag])) {
3728 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3729 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3732 if (nickserv_conf.weak_password_dict)
3733 dict_delete(nickserv_conf.weak_password_dict);
3734 nickserv_conf.weak_password_dict = dict_new();
3735 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3736 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3737 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3738 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3740 nickserv_load_dict(str);
3741 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3742 if (nickserv && str)
3743 NickChange(nickserv, str, 0);
3744 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3745 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3746 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3747 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3748 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3749 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3750 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3751 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3752 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3753 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3754 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3755 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3756 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3757 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3758 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3759 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3760 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3761 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3762 str = conf_get_data("server/network", RECDB_QSTRING);
3763 nickserv_conf.network_name = str ? str : "some IRC network";
3764 if (!nickserv_conf.auth_policer_params) {
3765 nickserv_conf.auth_policer_params = policer_params_new();
3766 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3767 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3769 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3770 for (it=dict_first(child); it; it=iter_next(it))
3771 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3775 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3777 char newnick[NICKLEN+1];
3786 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3788 case RECLAIM_SVSNICK:
3790 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3791 } while (GetUserH(newnick));
3792 irc_svsnick(nickserv, user, newnick);
3795 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3796 DelUser(user, nickserv, 1, msg);
3802 nickserv_reclaim_p(void *data) {
3803 struct userNode *user = data;
3804 struct nick_info *ni = get_nick_info(user->nick);
3806 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3810 check_user_nick(struct userNode *user) {
3811 struct nick_info *ni;
3812 user->modes &= ~FLAGS_REGNICK;
3813 if (!(ni = get_nick_info(user->nick)))
3815 if (user->handle_info == ni->owner) {
3816 user->modes |= FLAGS_REGNICK;
3820 if (nickserv_conf.warn_nick_owned)
3821 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3822 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3824 if (nickserv_conf.auto_reclaim_delay)
3825 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3827 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3832 handle_new_user(struct userNode *user)
3834 return check_user_nick(user);
3838 handle_account(struct userNode *user, const char *stamp)
3840 struct handle_info *hi;
3842 #ifdef WITH_PROTOCOL_P10
3843 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3845 hi = dict_find(nickserv_id_dict, stamp, NULL);
3849 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3852 set_user_handle_info(user, hi, 0);
3854 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3859 handle_nick_change(struct userNode *user, const char *old_nick)
3861 struct handle_info *hi;
3863 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3864 dict_remove(nickserv_allow_auth_dict, old_nick);
3865 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3867 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3868 check_user_nick(user);
3872 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3874 dict_remove(nickserv_allow_auth_dict, user->nick);
3875 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3876 set_user_handle_info(user, NULL, 0);
3879 static struct modcmd *
3880 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3882 if (min_level > 0) {
3884 sprintf(buf, "%u", min_level);
3885 if (must_be_qualified) {
3886 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3888 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3890 } else if (min_level == 0) {
3891 if (must_be_qualified) {
3892 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3894 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3897 if (must_be_qualified) {
3898 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3900 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3906 nickserv_db_cleanup(void)
3908 unreg_del_user_func(nickserv_remove_user);
3909 userList_clean(&curr_helpers);
3910 policer_params_delete(nickserv_conf.auth_policer_params);
3911 dict_delete(nickserv_handle_dict);
3912 dict_delete(nickserv_nick_dict);
3913 dict_delete(nickserv_opt_dict);
3914 dict_delete(nickserv_allow_auth_dict);
3915 dict_delete(nickserv_email_dict);
3916 dict_delete(nickserv_id_dict);
3917 dict_delete(nickserv_conf.weak_password_dict);
3918 free(auth_func_list);
3919 free(unreg_func_list);
3921 free(allowauth_func_list);
3922 free(handle_merge_func_list);
3923 free(failpw_func_list);
3924 if (nickserv_conf.valid_handle_regex_set)
3925 regfree(&nickserv_conf.valid_handle_regex);
3926 if (nickserv_conf.valid_nick_regex_set)
3927 regfree(&nickserv_conf.valid_nick_regex);
3931 init_nickserv(const char *nick)
3934 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3935 reg_new_user_func(handle_new_user);
3936 reg_nick_change_func(handle_nick_change);
3937 reg_del_user_func(nickserv_remove_user);
3938 reg_account_func(handle_account);
3940 /* set up handle_inverse_flags */
3941 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3942 for (i=0; handle_flags[i]; i++) {
3943 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3944 flag_access_levels[i] = 0;
3947 conf_register_reload(nickserv_conf_read);
3948 nickserv_opt_dict = dict_new();
3949 nickserv_email_dict = dict_new();
3950 dict_set_free_keys(nickserv_email_dict, free);
3951 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3953 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3954 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3955 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3956 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3957 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3958 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3959 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3960 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3961 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3962 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3963 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3964 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3965 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3966 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3967 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3968 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3969 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3970 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3971 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
3972 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
3973 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
3974 if (!nickserv_conf.disable_nicks) {
3975 /* nick management commands */
3976 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3977 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3978 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3979 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3980 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3981 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3983 if (nickserv_conf.email_enabled) {
3984 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3985 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3986 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3987 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3988 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3990 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3991 /* miscellaneous commands */
3992 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3993 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3994 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3995 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3996 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3998 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3999 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4000 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4001 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4002 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4003 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4004 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4005 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4006 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4007 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4008 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4009 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4010 if (nickserv_conf.titlehost_suffix) {
4011 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4012 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4014 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
4015 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4016 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4018 nickserv_handle_dict = dict_new();
4019 dict_set_free_keys(nickserv_handle_dict, free);
4020 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4022 nickserv_id_dict = dict_new();
4023 dict_set_free_keys(nickserv_id_dict, free);
4025 nickserv_nick_dict = dict_new();
4026 dict_set_free_data(nickserv_nick_dict, free);
4028 nickserv_allow_auth_dict = dict_new();
4030 userList_init(&curr_helpers);
4033 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4034 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4035 nickserv_service = service_register(nickserv);
4037 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4038 reg_exit_func(nickserv_db_cleanup);
4039 if(nickserv_conf.handle_expire_frequency)
4040 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4041 message_register_table(msgtab);