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"
74 #define KEY_OUNREGISTER_INACTIVE "ounregister_inactive"
75 #define KEY_OUNREGISTER_FLAGS "ounregister_flags"
78 #define KEY_PASSWD "passwd"
79 #define KEY_NICKS "nicks"
80 #define KEY_MASKS "masks"
81 #define KEY_OPSERV_LEVEL "opserv_level"
82 #define KEY_FLAGS "flags"
83 #define KEY_REGISTER_ON "register"
84 #define KEY_LAST_SEEN "lastseen"
85 #define KEY_INFO "info"
86 #define KEY_USERLIST_STYLE "user_style"
87 #define KEY_SCREEN_WIDTH "screen_width"
88 #define KEY_LAST_AUTHED_HOST "last_authed_host"
89 #define KEY_LAST_QUIT_HOST "last_quit_host"
90 #define KEY_EMAIL_ADDR "email_addr"
91 #define KEY_COOKIE "cookie"
92 #define KEY_COOKIE_DATA "data"
93 #define KEY_COOKIE_TYPE "type"
94 #define KEY_COOKIE_EXPIRES "expires"
95 #define KEY_ACTIVATION "activation"
96 #define KEY_PASSWORD_CHANGE "password change"
97 #define KEY_EMAIL_CHANGE "email change"
98 #define KEY_ALLOWAUTH "allowauth"
99 #define KEY_EPITHET "epithet"
100 #define KEY_TABLE_WIDTH "table_width"
101 #define KEY_MAXLOGINS "maxlogins"
102 #define KEY_FAKEHOST "fakehost"
103 #define KEY_NOTES "notes"
104 #define KEY_NOTE_EXPIRES "expires"
105 #define KEY_NOTE_SET "set"
106 #define KEY_NOTE_SETTER "setter"
107 #define KEY_NOTE_NOTE "note"
108 #define KEY_KARMA "karma"
110 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
112 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
113 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
114 typedef OPTION_FUNC(option_func_t);
116 DEFINE_LIST(handle_info_list, struct handle_info*)
118 #define NICKSERV_MIN_PARMS(N) do { \
120 reply("MSG_MISSING_PARAMS", argv[0]); \
121 svccmd_send_help(user, nickserv, cmd); \
125 struct userNode *nickserv;
126 struct userList curr_helpers;
127 const char *handle_flags = HANDLE_FLAGS;
129 static struct module *nickserv_module;
130 static struct service *nickserv_service;
131 static struct log_type *NS_LOG;
132 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
133 static dict_t nickserv_id_dict; /* contains struct handle_info* */
134 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
135 static dict_t nickserv_opt_dict; /* contains option_func_t* */
136 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
137 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
138 static char handle_inverse_flags[256];
139 static unsigned int flag_access_levels[32];
140 static const struct message_entry msgtab[] = {
141 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
142 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
143 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
144 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
145 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
146 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
147 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
148 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
149 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
150 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
151 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
152 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
153 { "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." },
154 { "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." },
155 { "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." },
156 { "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." },
157 { "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." },
158 { "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." },
159 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
160 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
161 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
162 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
163 { "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." },
164 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
165 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
166 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
167 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
168 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
169 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
170 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
171 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
172 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
173 { "NSMSG_ATE_COOKIE_FOREIGN", "I ate the cookie for account $b%s$b. It may now have another." },
174 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
175 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
176 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
177 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
178 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
179 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
180 { "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)" },
181 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
182 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
183 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
184 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
185 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
186 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
187 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
188 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
189 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
190 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
191 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
192 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
193 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
194 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
195 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
196 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
197 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
198 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
199 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
200 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
201 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
202 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
203 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
204 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
205 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
206 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
207 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
208 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
209 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
210 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
211 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
212 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
213 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
214 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
215 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
216 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
217 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
218 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
219 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
220 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
221 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
222 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
223 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
224 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
225 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
226 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
227 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
228 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
229 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
230 { "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)." },
231 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
232 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
233 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
234 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
235 { "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." },
236 { "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." },
237 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
238 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
239 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
240 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
241 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
242 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
243 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
244 { "NSMSG_PASS_SUCCESS", "Password changed." },
245 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
246 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
247 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
248 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
249 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
250 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
251 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
252 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
253 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
254 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
255 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
256 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
257 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
258 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
259 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
260 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
261 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
262 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
263 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
264 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
265 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
266 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
267 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
268 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
269 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
270 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
271 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
272 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
273 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
274 { "NSMSG_NO_ACCESS", "Access denied." },
275 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
276 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
277 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
278 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
279 { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
280 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
281 { "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." },
282 { "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." },
283 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
284 { "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." },
285 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
286 { "NSMSG_SEARCH_MATCH", "Match: %s" },
287 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
288 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
289 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
290 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
291 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
292 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
293 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
294 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
295 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
296 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
297 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
298 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
299 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
300 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
301 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
302 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
303 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
304 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
305 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
306 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
307 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
308 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
309 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
310 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
311 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
312 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
313 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
314 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
315 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
316 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
317 { "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." },
318 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
319 { "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." },
320 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
321 { "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." },
322 { "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." },
323 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
324 { "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." },
325 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
326 { "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." },
327 { "CHECKPASS_YES", "Yes." },
328 { "CHECKPASS_NO", "No." },
329 { "CHECKEMAIL_NOT_SET", "No email set." },
330 { "CHECKEMAIL_YES", "Yes." },
331 { "CHECKEMAIL_NO", "No." },
335 enum reclaim_action {
341 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
342 static void nickserv_reclaim_p(void *data);
343 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
346 unsigned int disable_nicks : 1;
347 unsigned int valid_handle_regex_set : 1;
348 unsigned int valid_nick_regex_set : 1;
349 unsigned int autogag_enabled : 1;
350 unsigned int email_enabled : 1;
351 unsigned int email_required : 1;
352 unsigned int default_hostmask : 1;
353 unsigned int warn_nick_owned : 1;
354 unsigned int warn_clone_auth : 1;
355 unsigned long nicks_per_handle;
356 unsigned long password_min_length;
357 unsigned long password_min_digits;
358 unsigned long password_min_upper;
359 unsigned long password_min_lower;
360 unsigned long db_backup_frequency;
361 unsigned long handle_expire_frequency;
362 unsigned long autogag_duration;
363 unsigned long email_visible_level;
364 unsigned long cookie_timeout;
365 unsigned long handle_expire_delay;
366 unsigned long nochan_handle_expire_delay;
367 unsigned long modoper_level;
368 unsigned long set_epithet_level;
369 unsigned long set_title_level;
370 unsigned long set_fakehost_level;
371 unsigned long handles_per_email;
372 unsigned long email_search_level;
373 const char *network_name;
374 const char *titlehost_suffix;
375 regex_t valid_handle_regex;
376 regex_t valid_nick_regex;
377 dict_t weak_password_dict;
378 struct policer_params *auth_policer_params;
379 enum reclaim_action reclaim_action;
380 enum reclaim_action auto_reclaim_action;
381 unsigned long auto_reclaim_delay;
382 unsigned char default_maxlogins;
383 unsigned char hard_maxlogins;
384 unsigned long ounregister_inactive;
385 unsigned long ounregister_flags;
388 /* We have 2^32 unique account IDs to use. */
389 unsigned long int highest_id = 0;
391 #define WALK_NOTES(HANDLE, PREV, NOTE) \
392 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
393 if (NOTE->expires && NOTE->expires < now) { \
394 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
396 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
400 canonicalize_hostmask(char *mask)
402 char *out = mask, *temp;
403 if ((temp = strchr(mask, '!'))) {
405 while (*temp) *out++ = *temp++;
411 static struct handle_info *
412 register_handle(const char *handle, const char *passwd, unsigned long id)
414 struct handle_info *hi;
416 char id_base64[IDLEN + 1];
419 /* Assign a unique account ID to the account; note that 0 is
420 an invalid account ID. 1 is therefore the first account ID. */
422 id = 1 + highest_id++;
424 /* Note: highest_id is and must always be the highest ID. */
425 if (id > highest_id) {
429 inttobase64(id_base64, id, IDLEN);
431 /* Make sure an account with the same ID doesn't exist. If a
432 duplicate is found, log some details and assign a new one.
433 This should be impossible, but it never hurts to expect it. */
434 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
435 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
440 hi = calloc(1, sizeof(*hi));
441 hi->userlist_style = HI_DEFAULT_STYLE;
442 hi->handle = strdup(handle);
443 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
445 dict_insert(nickserv_handle_dict, hi->handle, hi);
448 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
454 register_nick(const char *nick, struct handle_info *owner)
456 struct nick_info *ni;
457 ni = malloc(sizeof(struct nick_info));
458 safestrncpy(ni->nick, nick, sizeof(ni->nick));
460 ni->next = owner->nicks;
462 dict_insert(nickserv_nick_dict, ni->nick, ni);
466 delete_nick(struct nick_info *ni)
468 struct nick_info *last, *next;
469 struct userNode *user;
470 /* Check to see if we should mark a user as unregistered. */
471 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
472 user->modes &= ~FLAGS_REGNICK;
475 /* Remove ni from the nick_info linked list. */
476 if (ni == ni->owner->nicks) {
477 ni->owner->nicks = ni->next;
479 last = ni->owner->nicks;
485 last->next = next->next;
487 dict_remove(nickserv_nick_dict, ni->nick);
490 static unreg_func_t *unreg_func_list;
491 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
494 reg_unreg_func(unreg_func_t func)
496 if (unreg_func_used == unreg_func_size) {
497 if (unreg_func_size) {
498 unreg_func_size <<= 1;
499 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
502 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
505 unreg_func_list[unreg_func_used++] = func;
509 nickserv_free_cookie(void *data)
511 struct handle_cookie *cookie = data;
512 if (cookie->hi) cookie->hi->cookie = NULL;
513 if (cookie->data) free(cookie->data);
518 free_handle_info(void *vhi)
520 struct handle_info *hi = vhi;
523 inttobase64(id, hi->id, IDLEN);
524 dict_remove(nickserv_id_dict, id);
526 free_string_list(hi->masks);
530 delete_nick(hi->nicks);
535 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
536 nickserv_free_cookie(hi->cookie);
539 struct handle_note *note = hi->notes;
540 hi->notes = note->next;
543 if (hi->email_addr) {
544 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
545 handle_info_list_remove(hil, hi);
547 dict_remove(nickserv_email_dict, hi->email_addr);
552 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
555 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
559 for (n=0; n<unreg_func_used; n++)
560 unreg_func_list[n](notify, hi);
562 set_user_handle_info(hi->users, NULL, 0);
564 if (nickserv_conf.disable_nicks)
565 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
567 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
569 dict_remove(nickserv_handle_dict, hi->handle);
573 get_handle_info(const char *handle)
575 return dict_find(nickserv_handle_dict, handle, 0);
579 get_nick_info(const char *nick)
581 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
585 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
590 for (nn=0; nn<channel->members.used; ++nn) {
591 mn = channel->members.list[nn];
592 if ((mn->user != except) && (mn->user->handle_info == handle))
599 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
600 if (!user->handle_info) {
602 send_message(user, bot, "MSG_AUTHENTICATE");
606 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
608 send_message(user, bot, "NSMSG_NO_ACCESS");
612 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
614 send_message(user, bot, "MSG_OPER_SUSPENDED");
618 if (user->handle_info->opserv_level < min_level) {
620 send_message(user, bot, "NSMSG_NO_ACCESS");
628 is_valid_handle(const char *handle)
630 struct userNode *user;
631 /* cant register a juped nick/service nick as handle, to prevent confusion */
632 user = GetUserH(handle);
633 if (user && IsLocal(user))
635 /* check against maximum length */
636 if (strlen(handle) > NICKSERV_HANDLE_LEN)
638 /* for consistency, only allow account names that could be nicks */
639 if (!is_valid_nick(handle))
641 /* disallow account names that look like bad words */
642 if (opserv_bad_channel(handle))
644 /* test either regex or containing all valid chars */
645 if (nickserv_conf.valid_handle_regex_set) {
646 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
649 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
650 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
654 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
659 is_registerable_nick(const char *nick)
661 /* make sure it could be used as an account name */
662 if (!is_valid_handle(nick))
665 if (strlen(nick) > NICKLEN)
667 /* test either regex or as valid handle */
668 if (nickserv_conf.valid_nick_regex_set) {
669 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
672 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
673 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
681 is_valid_email_addr(const char *email)
683 return strchr(email, '@') != NULL;
687 visible_email_addr(struct userNode *user, struct handle_info *hi)
689 if (hi->email_addr) {
690 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
691 return hi->email_addr;
701 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
703 struct handle_info *hi;
704 struct userNode *target;
708 if (!(hi = get_handle_info(++name))) {
709 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
714 if (!(target = GetUserH(name))) {
715 send_message(user, service, "MSG_NICK_UNKNOWN", name);
718 if (IsLocal(target)) {
719 if (IsService(target))
720 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
722 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
725 if (!(hi = target->handle_info)) {
726 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
734 oper_outranks(struct userNode *user, struct handle_info *hi) {
735 if (user->handle_info->opserv_level > hi->opserv_level)
737 if (user->handle_info->opserv_level == hi->opserv_level) {
738 if ((user->handle_info->opserv_level == 1000)
739 || (user->handle_info == hi)
740 || ((user->handle_info->opserv_level == 0)
741 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
742 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
746 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
750 static struct handle_info *
751 get_victim_oper(struct userNode *user, const char *target)
753 struct handle_info *hi;
754 if (!(hi = smart_get_handle_info(nickserv, user, target)))
756 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
757 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
760 return oper_outranks(user, hi) ? hi : NULL;
764 valid_user_for(struct userNode *user, struct handle_info *hi)
768 /* If no hostmasks on the account, allow it. */
769 if (!hi->masks->used)
771 /* If any hostmask matches, allow it. */
772 for (ii=0; ii<hi->masks->used; ii++)
773 if (user_matches_glob(user, hi->masks->list[ii], 0))
775 /* If they are allowauthed to this account, allow it (removing the aa). */
776 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
777 dict_remove(nickserv_allow_auth_dict, user->nick);
780 /* The user is not allowed to use this account. */
785 is_secure_password(const char *handle, const char *pass, struct userNode *user)
788 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
792 if (len < nickserv_conf.password_min_length) {
794 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
797 if (!irccasecmp(pass, handle)) {
799 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
802 dict_find(nickserv_conf.weak_password_dict, pass, &p);
805 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
808 for (i=0; i<len; i++) {
809 if (isdigit(pass[i]))
811 if (isupper(pass[i]))
813 if (islower(pass[i]))
816 if ((cnt_lower < nickserv_conf.password_min_lower)
817 || (cnt_upper < nickserv_conf.password_min_upper)
818 || (cnt_digits < nickserv_conf.password_min_digits)) {
820 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
826 static auth_func_t *auth_func_list;
827 static unsigned int auth_func_size = 0, auth_func_used = 0;
830 reg_auth_func(auth_func_t func)
832 if (auth_func_used == auth_func_size) {
833 if (auth_func_size) {
834 auth_func_size <<= 1;
835 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
838 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
841 auth_func_list[auth_func_used++] = func;
844 static handle_rename_func_t *rf_list;
845 static unsigned int rf_list_size, rf_list_used;
848 reg_handle_rename_func(handle_rename_func_t func)
850 if (rf_list_used == rf_list_size) {
853 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
856 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
859 rf_list[rf_list_used++] = func;
863 generate_fakehost(struct handle_info *handle)
865 extern const char *hidden_host_suffix;
866 static char buffer[HOSTLEN+1];
868 if (!handle->fakehost) {
869 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
871 } else if (handle->fakehost[0] == '.') {
872 /* A leading dot indicates the stored value is actually a title. */
873 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
876 return handle->fakehost;
880 apply_fakehost(struct handle_info *handle)
882 struct userNode *target;
887 fake = generate_fakehost(handle);
888 for (target = handle->users; target; target = target->next_authed)
889 assign_fakehost(target, fake, 1);
893 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
896 struct handle_info *old_info;
898 /* This can happen if somebody uses COOKIE while authed, or if
899 * they re-auth to their current handle (which is silly, but users
901 if (user->handle_info == hi)
904 if (user->handle_info) {
905 struct userNode *other;
908 userList_remove(&curr_helpers, user);
910 /* remove from next_authed linked list */
911 if (user->handle_info->users == user) {
912 user->handle_info->users = user->next_authed;
914 for (other = user->handle_info->users;
915 other->next_authed != user;
916 other = other->next_authed) ;
917 other->next_authed = user->next_authed;
919 /* if nobody left on old handle, and they're not an oper, remove !god */
920 if (!user->handle_info->users && !user->handle_info->opserv_level)
921 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
922 /* record them as being last seen at this time */
923 user->handle_info->lastseen = now;
924 /* and record their hostmask */
925 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
927 old_info = user->handle_info;
928 user->handle_info = hi;
929 if (hi && !hi->users && !hi->opserv_level)
930 HANDLE_CLEAR_FLAG(hi, HELPING);
931 for (n=0; n<auth_func_used; n++)
932 auth_func_list[n](user, old_info);
934 struct nick_info *ni;
936 HANDLE_CLEAR_FLAG(hi, FROZEN);
937 if (nickserv_conf.warn_clone_auth) {
938 struct userNode *other;
939 for (other = hi->users; other; other = other->next_authed)
940 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
942 user->next_authed = hi->users;
945 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
946 userList_append(&curr_helpers, user);
948 if (hi->fakehost || old_info)
952 if (!nickserv_conf.disable_nicks) {
953 struct nick_info *ni;
954 for (ni = hi->nicks; ni; ni = ni->next) {
955 if (!irccasecmp(user->nick, ni->nick)) {
956 user->modes |= FLAGS_REGNICK;
961 StampUser(user, hi->handle, hi->registered, hi->id);
964 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
965 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
967 /* We cannot clear the user's account ID, unfortunately. */
968 user->next_authed = NULL;
972 static struct handle_info*
973 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
975 struct handle_info *hi;
976 struct nick_info *ni;
977 char crypted[MD5_CRYPT_LENGTH];
979 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
980 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
984 if (!is_secure_password(handle, passwd, user))
987 cryptpass(passwd, crypted);
988 hi = register_handle(handle, crypted, 0);
989 hi->masks = alloc_string_list(1);
991 hi->language = lang_C;
992 hi->registered = now;
994 hi->flags = HI_DEFAULT_FLAGS;
995 if (settee && !no_auth)
996 set_user_handle_info(settee, hi, 1);
999 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1000 else if (nickserv_conf.disable_nicks)
1001 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1002 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1003 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1005 register_nick(user->nick, hi);
1006 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1008 if (settee && (user != settee))
1009 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1014 nickserv_bake_cookie(struct handle_cookie *cookie)
1016 cookie->hi->cookie = cookie;
1017 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1021 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1023 struct handle_cookie *cookie;
1024 char subject[128], body[4096], *misc;
1025 const char *netname, *fmt;
1029 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1033 cookie = calloc(1, sizeof(*cookie));
1035 cookie->type = type;
1036 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1037 cookie->expires = now + nickserv_conf.cookie_timeout;
1038 inttobase64(cookie->cookie, rand(), 5);
1039 inttobase64(cookie->cookie+5, rand(), 5);
1041 netname = nickserv_conf.network_name;
1044 switch (cookie->type) {
1046 hi->passwd[0] = 0; /* invalidate password */
1047 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1048 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1049 snprintf(subject, sizeof(subject), fmt, netname);
1050 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1051 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1054 case PASSWORD_CHANGE:
1055 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1056 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1057 snprintf(subject, sizeof(subject), fmt, netname);
1058 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1059 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1062 misc = hi->email_addr;
1063 hi->email_addr = cookie->data;
1065 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1066 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1067 snprintf(subject, sizeof(subject), fmt, netname);
1068 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1069 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1070 mail_send(nickserv, hi, subject, body, 1);
1071 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1072 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1074 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1075 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1076 snprintf(subject, sizeof(subject), fmt, netname);
1077 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1078 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1079 mail_send(nickserv, hi, subject, body, 1);
1082 hi->email_addr = misc;
1085 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1086 snprintf(subject, sizeof(subject), fmt, netname);
1087 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1088 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1089 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1092 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1096 mail_send(nickserv, hi, subject, body, first_time);
1097 nickserv_bake_cookie(cookie);
1101 nickserv_eat_cookie(struct handle_cookie *cookie)
1103 cookie->hi->cookie = NULL;
1104 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1105 nickserv_free_cookie(cookie);
1109 nickserv_free_email_addr(void *data)
1111 handle_info_list_clean(data);
1116 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1118 struct handle_info_list *hil;
1119 /* Remove from old handle_info_list ... */
1120 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1121 handle_info_list_remove(hil, hi);
1122 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1123 hi->email_addr = NULL;
1125 /* Add to the new list.. */
1126 if (new_email_addr) {
1127 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1128 hil = calloc(1, sizeof(*hil));
1129 hil->tag = strdup(new_email_addr);
1130 handle_info_list_init(hil);
1131 dict_insert(nickserv_email_dict, hil->tag, hil);
1133 handle_info_list_append(hil, hi);
1134 hi->email_addr = hil->tag;
1138 static NICKSERV_FUNC(cmd_register)
1141 struct handle_info *hi;
1142 const char *email_addr, *password;
1145 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1146 /* Require the first handle registered to belong to someone +o. */
1147 reply("NSMSG_REQUIRE_OPER");
1151 if (user->handle_info) {
1152 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1156 if (IsRegistering(user)) {
1157 reply("NSMSG_ALREADY_REGISTERING");
1161 if (IsStamped(user)) {
1162 /* Unauthenticated users might still have been stamped
1163 previously and could therefore have a hidden host;
1164 do not allow them to register a new account. */
1165 reply("NSMSG_STAMPED_REGISTER");
1169 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1171 if (!is_valid_handle(argv[1])) {
1172 reply("NSMSG_BAD_HANDLE", argv[1]);
1176 if ((argc >= 4) && nickserv_conf.email_enabled) {
1177 struct handle_info_list *hil;
1180 /* Remember email address. */
1181 email_addr = argv[3];
1183 /* Check that the email address looks valid.. */
1184 if (!is_valid_email_addr(email_addr)) {
1185 reply("NSMSG_BAD_EMAIL_ADDR");
1189 /* .. and that we are allowed to send to it. */
1190 if ((str = mail_prohibited_address(email_addr))) {
1191 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1195 /* If we do email verify, make sure we don't spam the address. */
1196 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1198 for (nn=0; nn<hil->used; nn++) {
1199 if (hil->list[nn]->cookie) {
1200 reply("NSMSG_EMAIL_UNACTIVATED");
1204 if (hil->used >= nickserv_conf.handles_per_email) {
1205 reply("NSMSG_EMAIL_OVERUSED");
1218 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1220 /* Add any masks they should get. */
1221 if (nickserv_conf.default_hostmask) {
1222 string_list_append(hi->masks, strdup("*@*"));
1224 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1225 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1226 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1229 /* If they're the first to register, give them level 1000. */
1230 if (dict_size(nickserv_handle_dict) == 1) {
1231 hi->opserv_level = 1000;
1232 reply("NSMSG_ROOT_HANDLE", argv[1]);
1235 /* Set their email address. */
1237 nickserv_set_email_addr(hi, email_addr);
1239 /* If they need to do email verification, tell them. */
1241 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1243 /* Set registering flag.. */
1244 user->modes |= FLAGS_REGISTERING;
1249 static NICKSERV_FUNC(cmd_oregister)
1252 struct userNode *settee;
1253 struct handle_info *hi;
1255 NICKSERV_MIN_PARMS(3);
1257 if (!is_valid_handle(argv[1])) {
1258 reply("NSMSG_BAD_HANDLE", argv[1]);
1265 } else if (strchr(argv[3], '@')) {
1266 mask = canonicalize_hostmask(strdup(argv[3]));
1268 settee = GetUserH(argv[4]);
1270 reply("MSG_NICK_UNKNOWN", argv[4]);
1277 } else if ((settee = GetUserH(argv[3]))) {
1278 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1280 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1283 if (settee && settee->handle_info) {
1284 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1288 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1293 string_list_append(hi->masks, mask);
1297 static NICKSERV_FUNC(cmd_handleinfo)
1300 unsigned int i, pos=0, herelen;
1301 struct userNode *target, *next_un;
1302 struct handle_info *hi;
1303 const char *nsmsg_none;
1307 if (!(hi = user->handle_info)) {
1308 reply("NSMSG_MUST_AUTH");
1311 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1315 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1316 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1317 feh = hi->registered;
1318 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1321 intervalString(buff, now - hi->lastseen, user->handle_info);
1322 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1324 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1327 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1328 if (HANDLE_FLAGGED(hi, FROZEN))
1329 reply("NSMSG_HANDLEINFO_VACATION");
1331 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1332 struct do_not_register *dnr;
1333 if ((dnr = chanserv_is_dnr(NULL, hi)))
1334 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1335 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1337 } else if (hi != user->handle_info)
1341 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
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;
1358 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1359 reply("NSMSG_HANDLEINFO_ID", hi->id);
1361 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1363 reply("NSMSG_HANDLEINFO_NO_NOTES");
1365 struct handle_note *prev, *note;
1367 WALK_NOTES(hi, prev, note) {
1368 char set_time[INTERVALLEN];
1369 intervalString(set_time, now - note->set, user->handle_info);
1370 if (note->expires) {
1371 char exp_time[INTERVALLEN];
1372 intervalString(exp_time, note->expires - now, user->handle_info);
1373 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1375 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1382 unsigned long flen = 1;
1383 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1385 for (i=0, flen=1; handle_flags[i]; i++)
1386 if (hi->flags & 1 << i)
1387 flags[flen++] = handle_flags[i];
1389 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1391 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1394 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1395 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1396 || (hi->opserv_level > 0)) {
1397 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1401 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1403 if (hi->last_quit_host[0])
1404 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1406 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1408 if (nickserv_conf.disable_nicks) {
1409 /* nicks disabled; don't show anything about registered nicks */
1410 } else if (hi->nicks) {
1411 struct nick_info *ni, *next_ni;
1412 for (ni = hi->nicks; ni; ni = next_ni) {
1413 herelen = strlen(ni->nick);
1414 if (pos + herelen + 1 > ArrayLength(buff)) {
1416 goto print_nicks_buff;
1420 memcpy(buff+pos, ni->nick, herelen);
1421 pos += herelen; buff[pos++] = ' ';
1425 reply("NSMSG_HANDLEINFO_NICKS", buff);
1430 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1433 if (hi->masks->used) {
1434 for (i=0; i < hi->masks->used; i++) {
1435 herelen = strlen(hi->masks->list[i]);
1436 if (pos + herelen + 1 > ArrayLength(buff)) {
1438 goto print_mask_buff;
1440 memcpy(buff+pos, hi->masks->list[i], herelen);
1441 pos += herelen; buff[pos++] = ' ';
1442 if (i+1 == hi->masks->used) {
1445 reply("NSMSG_HANDLEINFO_MASKS", buff);
1450 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1454 struct userData *channel, *next;
1457 for (channel = hi->channels; channel; channel = next) {
1458 next = channel->u_next;
1459 name = channel->channel->channel->name;
1460 herelen = strlen(name);
1461 if (pos + herelen + 7 > ArrayLength(buff)) {
1463 goto print_chans_buff;
1465 if (IsUserSuspended(channel))
1467 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1471 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1476 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1479 for (target = hi->users; target; target = next_un) {
1480 herelen = strlen(target->nick);
1481 if (pos + herelen + 1 > ArrayLength(buff)) {
1483 goto print_cnick_buff;
1485 next_un = target->next_authed;
1487 memcpy(buff+pos, target->nick, herelen);
1488 pos += herelen; buff[pos++] = ' ';
1492 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1497 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1500 static NICKSERV_FUNC(cmd_userinfo)
1502 struct userNode *target;
1504 NICKSERV_MIN_PARMS(2);
1505 if (!(target = GetUserH(argv[1]))) {
1506 reply("MSG_NICK_UNKNOWN", argv[1]);
1509 if (target->handle_info)
1510 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1512 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1516 static NICKSERV_FUNC(cmd_nickinfo)
1518 struct nick_info *ni;
1520 NICKSERV_MIN_PARMS(2);
1521 if (!(ni = get_nick_info(argv[1]))) {
1522 reply("MSG_NICK_UNKNOWN", argv[1]);
1525 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1529 static NICKSERV_FUNC(cmd_notes)
1531 struct handle_info *hi;
1532 struct handle_note *prev, *note;
1535 NICKSERV_MIN_PARMS(2);
1536 if (!(hi = get_victim_oper(user, argv[1])))
1539 WALK_NOTES(hi, prev, note) {
1540 char set_time[INTERVALLEN];
1541 intervalString(set_time, now - note->set, user->handle_info);
1542 if (note->expires) {
1543 char exp_time[INTERVALLEN];
1544 intervalString(exp_time, note->expires - now, user->handle_info);
1545 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1547 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1551 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1555 static NICKSERV_FUNC(cmd_rename_handle)
1557 struct handle_info *hi;
1558 char msgbuf[MAXLEN], *old_handle;
1561 NICKSERV_MIN_PARMS(3);
1562 if (!(hi = get_victim_oper(user, argv[1])))
1564 if (!is_valid_handle(argv[2])) {
1565 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1568 if (get_handle_info(argv[2])) {
1569 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1573 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1574 hi->handle = strdup(argv[2]);
1575 dict_insert(nickserv_handle_dict, hi->handle, hi);
1576 for (nn=0; nn<rf_list_used; nn++)
1577 rf_list[nn](hi, old_handle);
1578 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1579 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1580 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1585 static failpw_func_t *failpw_func_list;
1586 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1589 reg_failpw_func(failpw_func_t func)
1591 if (failpw_func_used == failpw_func_size) {
1592 if (failpw_func_size) {
1593 failpw_func_size <<= 1;
1594 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1596 failpw_func_size = 8;
1597 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1600 failpw_func_list[failpw_func_used++] = func;
1603 static NICKSERV_FUNC(cmd_auth)
1605 int pw_arg, used, maxlogins;
1606 struct handle_info *hi;
1608 struct userNode *other;
1610 if (user->handle_info) {
1611 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1614 if (IsStamped(user)) {
1615 /* Unauthenticated users might still have been stamped
1616 previously and could therefore have a hidden host;
1617 do not allow them to authenticate. */
1618 reply("NSMSG_STAMPED_AUTH");
1622 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1624 } else if (argc == 2) {
1625 if (nickserv_conf.disable_nicks) {
1626 if (!(hi = get_handle_info(user->nick))) {
1627 reply("NSMSG_HANDLE_NOT_FOUND");
1631 /* try to look up their handle from their nick */
1632 struct nick_info *ni;
1633 ni = get_nick_info(user->nick);
1635 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1642 reply("MSG_MISSING_PARAMS", argv[0]);
1643 svccmd_send_help(user, nickserv, cmd);
1647 reply("NSMSG_HANDLE_NOT_FOUND");
1650 /* Responses from here on look up the language used by the handle they asked about. */
1651 passwd = argv[pw_arg];
1652 if (!valid_user_for(user, hi)) {
1653 if (hi->email_addr && nickserv_conf.email_enabled)
1654 send_message_type(4, user, cmd->parent->bot,
1655 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1658 send_message_type(4, user, cmd->parent->bot,
1659 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1661 argv[pw_arg] = "BADMASK";
1664 if (!checkpass(passwd, hi->passwd)) {
1666 send_message_type(4, user, cmd->parent->bot,
1667 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1668 argv[pw_arg] = "BADPASS";
1669 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1670 if (nickserv_conf.autogag_enabled) {
1671 if (!user->auth_policer.params) {
1672 user->auth_policer.last_req = now;
1673 user->auth_policer.params = nickserv_conf.auth_policer_params;
1675 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1677 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1678 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1679 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1681 argv[pw_arg] = "GAGGED";
1686 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1687 send_message_type(4, user, cmd->parent->bot,
1688 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1689 argv[pw_arg] = "SUSPENDED";
1692 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1693 for (used = 0, other = hi->users; other; other = other->next_authed) {
1694 if (++used >= maxlogins) {
1695 send_message_type(4, user, cmd->parent->bot,
1696 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1698 argv[pw_arg] = "MAXLOGINS";
1703 set_user_handle_info(user, hi, 1);
1704 if (nickserv_conf.email_required && !hi->email_addr)
1705 reply("NSMSG_PLEASE_SET_EMAIL");
1706 if (!is_secure_password(hi->handle, passwd, NULL))
1707 reply("NSMSG_WEAK_PASSWORD");
1708 if (hi->passwd[0] != '$')
1709 cryptpass(passwd, hi->passwd);
1710 if (!hi->masks->used) {
1712 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1713 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1714 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1716 argv[pw_arg] = "****";
1717 reply("NSMSG_AUTH_SUCCESS");
1721 static allowauth_func_t *allowauth_func_list;
1722 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1725 reg_allowauth_func(allowauth_func_t func)
1727 if (allowauth_func_used == allowauth_func_size) {
1728 if (allowauth_func_size) {
1729 allowauth_func_size <<= 1;
1730 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1732 allowauth_func_size = 8;
1733 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1736 allowauth_func_list[allowauth_func_used++] = func;
1739 static NICKSERV_FUNC(cmd_allowauth)
1741 struct userNode *target;
1742 struct handle_info *hi;
1745 NICKSERV_MIN_PARMS(2);
1746 if (!(target = GetUserH(argv[1]))) {
1747 reply("MSG_NICK_UNKNOWN", argv[1]);
1750 if (target->handle_info) {
1751 reply("NSMSG_USER_PREV_AUTH", target->nick);
1754 if (IsStamped(target)) {
1755 /* Unauthenticated users might still have been stamped
1756 previously and could therefore have a hidden host;
1757 do not allow them to authenticate to an account. */
1758 reply("NSMSG_USER_PREV_STAMP", target->nick);
1763 else if (!(hi = get_handle_info(argv[2]))) {
1764 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1768 if (hi->opserv_level > user->handle_info->opserv_level) {
1769 reply("MSG_USER_OUTRANKED", hi->handle);
1772 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1773 || (hi->opserv_level > 0))
1774 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1775 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1778 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1779 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1780 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1781 if (nickserv_conf.email_enabled)
1782 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1784 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1785 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1787 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1789 for (n=0; n<allowauth_func_used; n++)
1790 allowauth_func_list[n](user, target, hi);
1794 static NICKSERV_FUNC(cmd_authcookie)
1796 struct handle_info *hi;
1798 NICKSERV_MIN_PARMS(2);
1799 if (user->handle_info) {
1800 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1803 if (IsStamped(user)) {
1804 /* Unauthenticated users might still have been stamped
1805 previously and could therefore have a hidden host;
1806 do not allow them to authenticate to an account. */
1807 reply("NSMSG_STAMPED_AUTHCOOKIE");
1810 if (!(hi = get_handle_info(argv[1]))) {
1811 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1814 if (!hi->email_addr) {
1815 reply("MSG_SET_EMAIL_ADDR");
1818 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1822 static NICKSERV_FUNC(cmd_delcookie)
1824 struct handle_info *hi;
1826 hi = user->handle_info;
1828 reply("NSMSG_NO_COOKIE");
1831 switch (hi->cookie->type) {
1834 reply("NSMSG_MUST_TIME_OUT");
1837 nickserv_eat_cookie(hi->cookie);
1838 reply("NSMSG_ATE_COOKIE");
1844 static NICKSERV_FUNC(cmd_odelcookie)
1846 struct handle_info *hi;
1848 NICKSERV_MIN_PARMS(2);
1850 if (!(hi = get_victim_oper(user, argv[1])))
1854 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
1858 nickserv_eat_cookie(hi->cookie);
1859 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
1864 static NICKSERV_FUNC(cmd_resetpass)
1866 struct handle_info *hi;
1867 char crypted[MD5_CRYPT_LENGTH];
1869 NICKSERV_MIN_PARMS(3);
1870 if (user->handle_info) {
1871 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1874 if (IsStamped(user)) {
1875 /* Unauthenticated users might still have been stamped
1876 previously and could therefore have a hidden host;
1877 do not allow them to activate an account. */
1878 reply("NSMSG_STAMPED_RESETPASS");
1881 if (!(hi = get_handle_info(argv[1]))) {
1882 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1885 if (!hi->email_addr) {
1886 reply("MSG_SET_EMAIL_ADDR");
1889 cryptpass(argv[2], crypted);
1891 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1895 static NICKSERV_FUNC(cmd_cookie)
1897 struct handle_info *hi;
1900 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1903 NICKSERV_MIN_PARMS(3);
1904 if (!(hi = get_handle_info(argv[1]))) {
1905 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1911 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1912 reply("NSMSG_HANDLE_SUSPENDED");
1917 reply("NSMSG_NO_COOKIE");
1921 /* Check validity of operation before comparing cookie to
1922 * prohibit guessing by authed users. */
1923 if (user->handle_info
1924 && (hi->cookie->type != EMAIL_CHANGE)
1925 && (hi->cookie->type != PASSWORD_CHANGE)) {
1926 reply("NSMSG_CANNOT_COOKIE");
1930 if (strcmp(cookie, hi->cookie->cookie)) {
1931 reply("NSMSG_BAD_COOKIE");
1935 switch (hi->cookie->type) {
1937 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1938 set_user_handle_info(user, hi, 1);
1939 reply("NSMSG_HANDLE_ACTIVATED");
1941 case PASSWORD_CHANGE:
1942 set_user_handle_info(user, hi, 1);
1943 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1944 reply("NSMSG_PASSWORD_CHANGED");
1947 nickserv_set_email_addr(hi, hi->cookie->data);
1948 reply("NSMSG_EMAIL_CHANGED");
1951 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1952 set_user_handle_info(user, hi, 1);
1953 nickserv_addmask(user, hi, mask);
1954 reply("NSMSG_AUTH_SUCCESS");
1959 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1960 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1964 nickserv_eat_cookie(hi->cookie);
1969 static NICKSERV_FUNC(cmd_oregnick) {
1971 struct handle_info *target;
1972 struct nick_info *ni;
1974 NICKSERV_MIN_PARMS(3);
1975 if (!(target = modcmd_get_handle_info(user, argv[1])))
1978 if (!is_registerable_nick(nick)) {
1979 reply("NSMSG_BAD_NICK", nick);
1982 ni = dict_find(nickserv_nick_dict, nick, NULL);
1984 reply("NSMSG_NICK_EXISTS", nick);
1987 register_nick(nick, target);
1988 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1992 static NICKSERV_FUNC(cmd_regnick) {
1994 struct nick_info *ni;
1996 if (!is_registerable_nick(user->nick)) {
1997 reply("NSMSG_BAD_NICK", user->nick);
2000 /* count their nicks, see if it's too many */
2001 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2002 if (n >= nickserv_conf.nicks_per_handle) {
2003 reply("NSMSG_TOO_MANY_NICKS");
2006 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2008 reply("NSMSG_NICK_EXISTS", user->nick);
2011 register_nick(user->nick, user->handle_info);
2012 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2016 static NICKSERV_FUNC(cmd_pass)
2018 struct handle_info *hi;
2019 const char *old_pass, *new_pass;
2021 NICKSERV_MIN_PARMS(3);
2022 hi = user->handle_info;
2026 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2027 if (!checkpass(old_pass, hi->passwd)) {
2028 argv[1] = "BADPASS";
2029 reply("NSMSG_PASSWORD_INVALID");
2032 cryptpass(new_pass, hi->passwd);
2034 reply("NSMSG_PASS_SUCCESS");
2039 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2042 char *new_mask = canonicalize_hostmask(strdup(mask));
2043 for (i=0; i<hi->masks->used; i++) {
2044 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2045 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2050 string_list_append(hi->masks, new_mask);
2051 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2055 static NICKSERV_FUNC(cmd_addmask)
2058 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2059 int res = nickserv_addmask(user, user->handle_info, mask);
2063 if (!is_gline(argv[1])) {
2064 reply("NSMSG_MASK_INVALID", argv[1]);
2067 return nickserv_addmask(user, user->handle_info, argv[1]);
2071 static NICKSERV_FUNC(cmd_oaddmask)
2073 struct handle_info *hi;
2075 NICKSERV_MIN_PARMS(3);
2076 if (!(hi = get_victim_oper(user, argv[1])))
2078 return nickserv_addmask(user, hi, argv[2]);
2082 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2085 for (i=0; i<hi->masks->used; i++) {
2086 if (!strcmp(del_mask, hi->masks->list[i])) {
2087 char *old_mask = hi->masks->list[i];
2088 if (hi->masks->used == 1 && !force) {
2089 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2092 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2093 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2098 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2102 static NICKSERV_FUNC(cmd_delmask)
2104 NICKSERV_MIN_PARMS(2);
2105 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2108 static NICKSERV_FUNC(cmd_odelmask)
2110 struct handle_info *hi;
2111 NICKSERV_MIN_PARMS(3);
2112 if (!(hi = get_victim_oper(user, argv[1])))
2114 return nickserv_delmask(user, hi, argv[2], 1);
2118 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2119 unsigned int nn, add = 1, pos;
2120 unsigned long added, removed, flag;
2122 for (added=removed=nn=0; str[nn]; nn++) {
2124 case '+': add = 1; break;
2125 case '-': add = 0; break;
2127 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2128 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2131 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2132 /* cheesy avoidance of looking up the flag name.. */
2133 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2136 flag = 1 << (pos - 1);
2138 added |= flag, removed &= ~flag;
2140 removed |= flag, added &= ~flag;
2145 *premoved = removed;
2150 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2152 unsigned long before, after, added, removed;
2153 struct userNode *uNode;
2155 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2156 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2158 hi->flags = (hi->flags | added) & ~removed;
2159 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2161 /* Strip helping flag if they're only a support helper and not
2162 * currently in #support. */
2163 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2164 struct channelList *schannels;
2166 schannels = chanserv_support_channels();
2167 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2168 for (ii = 0; ii < schannels->used; ++ii)
2169 if (GetUserMode(schannels->list[ii], uNode))
2171 if (ii < schannels->used)
2175 HANDLE_CLEAR_FLAG(hi, HELPING);
2178 if (after && !before) {
2179 /* Add user to current helper list. */
2180 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2181 userList_append(&curr_helpers, uNode);
2182 } else if (!after && before) {
2183 /* Remove user from current helper list. */
2184 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2185 userList_remove(&curr_helpers, uNode);
2192 set_list(struct userNode *user, struct handle_info *hi, int override)
2196 char *set_display[] = {
2197 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2198 "EMAIL", "MAXLOGINS", "LANGUAGE"
2201 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2203 /* Do this so options are presented in a consistent order. */
2204 for (i = 0; i < ArrayLength(set_display); ++i)
2205 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2206 opt(user, hi, override, 0, NULL);
2209 static NICKSERV_FUNC(cmd_set)
2211 struct handle_info *hi;
2214 hi = user->handle_info;
2216 set_list(user, hi, 0);
2219 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2220 reply("NSMSG_INVALID_OPTION", argv[1]);
2223 return opt(user, hi, 0, argc-1, argv+1);
2226 static NICKSERV_FUNC(cmd_oset)
2228 struct handle_info *hi;
2229 struct svccmd *subcmd;
2231 char cmdname[MAXLEN];
2233 NICKSERV_MIN_PARMS(2);
2235 if (!(hi = get_victim_oper(user, argv[1])))
2239 set_list(user, hi, 0);
2243 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2244 reply("NSMSG_INVALID_OPTION", argv[2]);
2248 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2249 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2250 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2253 return opt(user, hi, 1, argc-2, argv+2);
2256 static OPTION_FUNC(opt_info)
2260 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2262 hi->infoline = NULL;
2264 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2268 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2269 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2273 static OPTION_FUNC(opt_width)
2276 hi->screen_width = strtoul(argv[1], NULL, 0);
2278 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2279 hi->screen_width = MIN_LINE_SIZE;
2280 else if (hi->screen_width > MAX_LINE_SIZE)
2281 hi->screen_width = MAX_LINE_SIZE;
2283 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2287 static OPTION_FUNC(opt_tablewidth)
2290 hi->table_width = strtoul(argv[1], NULL, 0);
2292 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2293 hi->table_width = MIN_LINE_SIZE;
2294 else if (hi->screen_width > MAX_LINE_SIZE)
2295 hi->table_width = MAX_LINE_SIZE;
2297 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2301 static OPTION_FUNC(opt_color)
2304 if (enabled_string(argv[1]))
2305 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2306 else if (disabled_string(argv[1]))
2307 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2309 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2314 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2318 static OPTION_FUNC(opt_privmsg)
2321 if (enabled_string(argv[1]))
2322 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2323 else if (disabled_string(argv[1]))
2324 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2326 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2331 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2335 static OPTION_FUNC(opt_style)
2340 if (!irccasecmp(argv[1], "Zoot"))
2341 hi->userlist_style = HI_STYLE_ZOOT;
2342 else if (!irccasecmp(argv[1], "def"))
2343 hi->userlist_style = HI_STYLE_DEF;
2346 switch (hi->userlist_style) {
2355 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2359 static OPTION_FUNC(opt_password)
2362 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2367 cryptpass(argv[1], hi->passwd);
2369 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2373 static OPTION_FUNC(opt_flags)
2376 unsigned int ii, flen;
2379 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2384 nickserv_apply_flags(user, hi, argv[1]);
2386 for (ii = flen = 0; handle_flags[ii]; ii++)
2387 if (hi->flags & (1 << ii))
2388 flags[flen++] = handle_flags[ii];
2391 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2393 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2397 static OPTION_FUNC(opt_email)
2401 if (!is_valid_email_addr(argv[1])) {
2402 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2405 if ((str = mail_prohibited_address(argv[1]))) {
2406 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2409 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2410 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2412 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2414 nickserv_set_email_addr(hi, argv[1]);
2416 nickserv_eat_cookie(hi->cookie);
2417 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2420 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2424 static OPTION_FUNC(opt_maxlogins)
2426 unsigned char maxlogins;
2428 maxlogins = strtoul(argv[1], NULL, 0);
2429 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2430 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2433 hi->maxlogins = maxlogins;
2435 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2436 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2440 static OPTION_FUNC(opt_language)
2442 struct language *lang;
2444 lang = language_find(argv[1]);
2445 if (irccasecmp(lang->name, argv[1]))
2446 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2447 hi->language = lang;
2449 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2453 static OPTION_FUNC(opt_karma)
2456 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2461 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2462 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2463 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2464 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2466 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2470 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2475 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2476 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2478 if ((user->handle_info->opserv_level < target->opserv_level)
2479 || ((user->handle_info->opserv_level == target->opserv_level)
2480 && (user->handle_info->opserv_level < 1000))) {
2481 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2484 if ((user->handle_info->opserv_level < new_level)
2485 || ((user->handle_info->opserv_level == new_level)
2486 && (user->handle_info->opserv_level < 1000))) {
2487 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2490 if (user->handle_info == target) {
2491 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2494 if (target->opserv_level == new_level)
2496 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2497 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2498 target->opserv_level = new_level;
2502 static OPTION_FUNC(opt_level)
2507 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2511 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2512 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2516 static OPTION_FUNC(opt_epithet)
2519 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2523 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2524 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2527 if ((epithet[0] == '*') && !epithet[1])
2530 hi->epithet = strdup(epithet);
2534 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2536 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2540 static OPTION_FUNC(opt_title)
2545 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2549 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2551 if (strchr(title, '.')) {
2552 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2555 if ((strlen(user->handle_info->handle) + strlen(title) +
2556 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2557 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2562 if (!strcmp(title, "*")) {
2563 hi->fakehost = NULL;
2565 hi->fakehost = malloc(strlen(title)+2);
2566 hi->fakehost[0] = '.';
2567 strcpy(hi->fakehost+1, title);
2570 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2571 title = hi->fakehost + 1;
2575 title = user_find_message(user, "MSG_NONE");
2576 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2580 static OPTION_FUNC(opt_fakehost)
2585 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2589 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2591 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2592 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2596 if (!strcmp(fake, "*"))
2597 hi->fakehost = NULL;
2599 hi->fakehost = strdup(fake);
2600 fake = hi->fakehost;
2603 fake = generate_fakehost(hi);
2605 fake = user_find_message(user, "MSG_NONE");
2606 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2610 static NICKSERV_FUNC(cmd_reclaim)
2612 struct handle_info *hi;
2613 struct nick_info *ni;
2614 struct userNode *victim;
2616 NICKSERV_MIN_PARMS(2);
2617 hi = user->handle_info;
2618 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2620 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2623 if (ni->owner != user->handle_info) {
2624 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2627 victim = GetUserH(ni->nick);
2629 reply("MSG_NICK_UNKNOWN", ni->nick);
2632 if (victim == user) {
2633 reply("NSMSG_NICK_USER_YOU");
2636 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2637 switch (nickserv_conf.reclaim_action) {
2638 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2639 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2640 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2641 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2646 static NICKSERV_FUNC(cmd_unregnick)
2649 struct handle_info *hi;
2650 struct nick_info *ni;
2652 hi = user->handle_info;
2653 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2654 ni = dict_find(nickserv_nick_dict, nick, NULL);
2656 reply("NSMSG_UNKNOWN_NICK", nick);
2659 if (hi != ni->owner) {
2660 reply("NSMSG_NOT_YOUR_NICK", nick);
2663 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2668 static NICKSERV_FUNC(cmd_ounregnick)
2670 struct nick_info *ni;
2672 NICKSERV_MIN_PARMS(2);
2673 if (!(ni = get_nick_info(argv[1]))) {
2674 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2677 if (!oper_outranks(user, ni->owner))
2679 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2684 static NICKSERV_FUNC(cmd_unregister)
2686 struct handle_info *hi;
2689 NICKSERV_MIN_PARMS(2);
2690 hi = user->handle_info;
2693 if (checkpass(passwd, hi->passwd)) {
2694 nickserv_unregister_handle(hi, user);
2697 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2698 reply("NSMSG_PASSWORD_INVALID");
2703 static NICKSERV_FUNC(cmd_ounregister)
2705 struct handle_info *hi;
2706 char reason[MAXLEN];
2709 NICKSERV_MIN_PARMS(2);
2710 if (!(hi = get_victim_oper(user, argv[1])))
2713 if (HANDLE_FLAGGED(hi, NODELETE)) {
2714 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
2718 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
2720 ((hi->flags & nickserv_conf.ounregister_flags)
2722 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
2723 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
2727 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2728 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2729 nickserv_unregister_handle(hi, user);
2733 static NICKSERV_FUNC(cmd_status)
2735 if (nickserv_conf.disable_nicks) {
2736 reply("NSMSG_GLOBAL_STATS_NONICK",
2737 dict_size(nickserv_handle_dict));
2739 if (user->handle_info) {
2741 struct nick_info *ni;
2742 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2743 reply("NSMSG_HANDLE_STATS", cnt);
2745 reply("NSMSG_HANDLE_NONE");
2747 reply("NSMSG_GLOBAL_STATS",
2748 dict_size(nickserv_handle_dict),
2749 dict_size(nickserv_nick_dict));
2754 static NICKSERV_FUNC(cmd_ghost)
2756 struct userNode *target;
2757 char reason[MAXLEN];
2759 NICKSERV_MIN_PARMS(2);
2760 if (!(target = GetUserH(argv[1]))) {
2761 reply("MSG_NICK_UNKNOWN", argv[1]);
2764 if (target == user) {
2765 reply("NSMSG_CANNOT_GHOST_SELF");
2768 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2769 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2772 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2773 DelUser(target, nickserv, 1, reason);
2774 reply("NSMSG_GHOST_KILLED", argv[1]);
2778 static NICKSERV_FUNC(cmd_vacation)
2780 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2781 reply("NSMSG_ON_VACATION");
2785 static NICKSERV_FUNC(cmd_addnote)
2787 struct handle_info *hi;
2788 unsigned long duration;
2791 struct handle_note *prev;
2792 struct handle_note *note;
2794 /* Parse parameters and figure out values for note's fields. */
2795 NICKSERV_MIN_PARMS(4);
2796 hi = get_victim_oper(user, argv[1]);
2799 if(!strcmp(argv[2], "0"))
2801 else if(!(duration = ParseInterval(argv[2])))
2803 reply("MSG_INVALID_DURATION", argv[2]);
2806 if (duration > 2*365*86400) {
2807 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2810 unsplit_string(argv + 3, argc - 3, text);
2811 WALK_NOTES(hi, prev, note) {}
2812 id = prev ? (prev->id + 1) : 1;
2814 /* Create the new note structure. */
2815 note = calloc(1, sizeof(*note) + strlen(text));
2817 note->expires = duration ? (now + duration) : 0;
2820 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2821 strcpy(note->note, text);
2826 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2830 static NICKSERV_FUNC(cmd_delnote)
2832 struct handle_info *hi;
2833 struct handle_note *prev;
2834 struct handle_note *note;
2837 NICKSERV_MIN_PARMS(3);
2838 hi = get_victim_oper(user, argv[1]);
2841 id = strtoul(argv[2], NULL, 10);
2842 WALK_NOTES(hi, prev, note) {
2843 if (id == note->id) {
2845 prev->next = note->next;
2847 hi->notes = note->next;
2849 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2853 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2858 nickserv_saxdb_write(struct saxdb_context *ctx) {
2860 struct handle_info *hi;
2863 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2865 assert(hi->id != 0);
2866 saxdb_start_record(ctx, iter_key(it), 0);
2868 struct handle_cookie *cookie = hi->cookie;
2871 switch (cookie->type) {
2872 case ACTIVATION: type = KEY_ACTIVATION; break;
2873 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2874 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2875 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2876 default: type = NULL; break;
2879 saxdb_start_record(ctx, KEY_COOKIE, 0);
2880 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2881 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2883 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2884 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2885 saxdb_end_record(ctx);
2889 struct handle_note *prev, *note;
2890 saxdb_start_record(ctx, KEY_NOTES, 0);
2891 WALK_NOTES(hi, prev, note) {
2892 snprintf(flags, sizeof(flags), "%d", note->id);
2893 saxdb_start_record(ctx, flags, 0);
2895 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
2896 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
2897 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
2898 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
2899 saxdb_end_record(ctx);
2901 saxdb_end_record(ctx);
2904 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2906 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2908 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2912 for (ii=flen=0; handle_flags[ii]; ++ii)
2913 if (hi->flags & (1 << ii))
2914 flags[flen++] = handle_flags[ii];
2916 saxdb_write_string(ctx, KEY_FLAGS, flags);
2918 saxdb_write_int(ctx, KEY_ID, hi->id);
2920 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2921 if (hi->last_quit_host[0])
2922 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2923 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2925 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
2926 if (hi->masks->used)
2927 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2929 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2931 struct string_list *slist;
2932 struct nick_info *ni;
2934 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2935 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2936 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2940 if (hi->opserv_level)
2941 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2942 if (hi->language != lang_C)
2943 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2944 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2945 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2946 if (hi->screen_width)
2947 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2948 if (hi->table_width)
2949 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2950 flags[0] = hi->userlist_style;
2952 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2953 saxdb_end_record(ctx);
2958 static handle_merge_func_t *handle_merge_func_list;
2959 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2962 reg_handle_merge_func(handle_merge_func_t func)
2964 if (handle_merge_func_used == handle_merge_func_size) {
2965 if (handle_merge_func_size) {
2966 handle_merge_func_size <<= 1;
2967 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2969 handle_merge_func_size = 8;
2970 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2973 handle_merge_func_list[handle_merge_func_used++] = func;
2976 static NICKSERV_FUNC(cmd_merge)
2978 struct handle_info *hi_from, *hi_to;
2979 struct userNode *last_user;
2980 struct userData *cList, *cListNext;
2981 unsigned int ii, jj, n;
2982 char buffer[MAXLEN];
2984 NICKSERV_MIN_PARMS(3);
2986 if (!(hi_from = get_victim_oper(user, argv[1])))
2988 if (!(hi_to = get_victim_oper(user, argv[2])))
2990 if (hi_to == hi_from) {
2991 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2995 for (n=0; n<handle_merge_func_used; n++)
2996 handle_merge_func_list[n](user, hi_to, hi_from);
2998 /* Append "from" handle's nicks to "to" handle's nick list. */
3000 struct nick_info *last_ni;
3001 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3002 last_ni->next = hi_from->nicks;
3004 while (hi_from->nicks) {
3005 hi_from->nicks->owner = hi_to;
3006 hi_from->nicks = hi_from->nicks->next;
3009 /* Merge the hostmasks. */
3010 for (ii=0; ii<hi_from->masks->used; ii++) {
3011 char *mask = hi_from->masks->list[ii];
3012 for (jj=0; jj<hi_to->masks->used; jj++)
3013 if (match_ircglobs(hi_to->masks->list[jj], mask))
3015 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3016 string_list_append(hi_to->masks, strdup(mask));
3019 /* Merge the lists of authed users. */
3021 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3022 last_user->next_authed = hi_from->users;
3024 hi_to->users = hi_from->users;
3026 /* Repoint the old "from" handle's users. */
3027 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3028 last_user->handle_info = hi_to;
3030 hi_from->users = NULL;
3032 /* Merge channel userlists. */
3033 for (cList=hi_from->channels; cList; cList=cListNext) {
3034 struct userData *cList2;
3035 cListNext = cList->u_next;
3036 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3037 if (cList->channel == cList2->channel)
3039 if (cList2 && (cList2->access >= cList->access)) {
3040 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);
3041 /* keep cList2 in hi_to; remove cList from hi_from */
3042 del_channel_user(cList, 1);
3045 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);
3046 /* remove the lower-ranking cList2 from hi_to */
3047 del_channel_user(cList2, 1);
3049 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3051 /* cList needs to be moved from hi_from to hi_to */
3052 cList->handle = hi_to;
3053 /* Remove from linked list for hi_from */
3054 assert(!cList->u_prev);
3055 hi_from->channels = cList->u_next;
3057 cList->u_next->u_prev = cList->u_prev;
3058 /* Add to linked list for hi_to */
3059 cList->u_prev = NULL;
3060 cList->u_next = hi_to->channels;
3061 if (hi_to->channels)
3062 hi_to->channels->u_prev = cList;
3063 hi_to->channels = cList;
3067 /* Do they get an OpServ level promotion? */
3068 if (hi_from->opserv_level > hi_to->opserv_level)
3069 hi_to->opserv_level = hi_from->opserv_level;
3071 /* What about last seen time? */
3072 if (hi_from->lastseen > hi_to->lastseen)
3073 hi_to->lastseen = hi_from->lastseen;
3075 /* New karma is the sum of the two original karmas. */
3076 hi_to->karma += hi_from->karma;
3078 /* Does a fakehost carry over? (This intentionally doesn't set it
3079 * for users previously attached to hi_to. They'll just have to
3082 if (hi_from->fakehost && !hi_to->fakehost)
3083 hi_to->fakehost = strdup(hi_from->fakehost);
3085 /* Notify of success. */
3086 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3087 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3088 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3090 /* Unregister the "from" handle. */
3091 nickserv_unregister_handle(hi_from, NULL);
3096 struct nickserv_discrim {
3097 unsigned long flags_on, flags_off;
3098 unsigned long min_registered, max_registered;
3099 unsigned long lastseen;
3101 int min_level, max_level;
3102 int min_karma, max_karma;
3103 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3104 const char *nickmask;
3105 const char *hostmask;
3106 const char *fakehostmask;
3107 const char *handlemask;
3108 const char *emailmask;
3111 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3113 struct discrim_apply_info {
3114 struct nickserv_discrim *discrim;
3115 discrim_search_func func;
3116 struct userNode *source;
3117 unsigned int matched;
3120 static struct nickserv_discrim *
3121 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3124 struct nickserv_discrim *discrim;
3126 discrim = malloc(sizeof(*discrim));
3127 memset(discrim, 0, sizeof(*discrim));
3128 discrim->min_level = 0;
3129 discrim->max_level = INT_MAX;
3130 discrim->limit = 50;
3131 discrim->min_registered = 0;
3132 discrim->max_registered = ULONG_MAX;
3133 discrim->lastseen = ULONG_MAX;
3134 discrim->min_karma = INT_MIN;
3135 discrim->max_karma = INT_MAX;
3137 for (i=0; i<argc; i++) {
3138 if (i == argc - 1) {
3139 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3142 if (!irccasecmp(argv[i], "limit")) {
3143 discrim->limit = strtoul(argv[++i], NULL, 0);
3144 } else if (!irccasecmp(argv[i], "flags")) {
3145 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3146 } else if (!irccasecmp(argv[i], "registered")) {
3147 const char *cmp = argv[++i];
3148 if (cmp[0] == '<') {
3149 if (cmp[1] == '=') {
3150 discrim->min_registered = now - ParseInterval(cmp+2);
3152 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3154 } else if (cmp[0] == '=') {
3155 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3156 } else if (cmp[0] == '>') {
3157 if (cmp[1] == '=') {
3158 discrim->max_registered = now - ParseInterval(cmp+2);
3160 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3163 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3165 } else if (!irccasecmp(argv[i], "seen")) {
3166 discrim->lastseen = now - ParseInterval(argv[++i]);
3167 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3168 discrim->nickmask = argv[++i];
3169 } else if (!irccasecmp(argv[i], "hostmask")) {
3171 if (!irccasecmp(argv[i], "exact")) {
3172 if (i == argc - 1) {
3173 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3176 discrim->hostmask_type = EXACT;
3177 } else if (!irccasecmp(argv[i], "subset")) {
3178 if (i == argc - 1) {
3179 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3182 discrim->hostmask_type = SUBSET;
3183 } else if (!irccasecmp(argv[i], "superset")) {
3184 if (i == argc - 1) {
3185 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3188 discrim->hostmask_type = SUPERSET;
3189 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3190 if (i == argc - 1) {
3191 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3194 discrim->hostmask_type = LASTQUIT;
3197 discrim->hostmask_type = SUPERSET;
3199 discrim->hostmask = argv[++i];
3200 } else if (!irccasecmp(argv[i], "fakehost")) {
3201 if (!irccasecmp(argv[++i], "*")) {
3202 discrim->fakehostmask = 0;
3204 discrim->fakehostmask = argv[i];
3206 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3207 if (!irccasecmp(argv[++i], "*")) {
3208 discrim->handlemask = 0;
3210 discrim->handlemask = argv[i];
3212 } else if (!irccasecmp(argv[i], "email")) {
3213 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3214 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3216 } else if (!irccasecmp(argv[++i], "*")) {
3217 discrim->emailmask = 0;
3219 discrim->emailmask = argv[i];
3221 } else if (!irccasecmp(argv[i], "access")) {
3222 const char *cmp = argv[++i];
3223 if (cmp[0] == '<') {
3224 if (discrim->min_level == 0) discrim->min_level = 1;
3225 if (cmp[1] == '=') {
3226 discrim->max_level = strtoul(cmp+2, NULL, 0);
3228 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3230 } else if (cmp[0] == '=') {
3231 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3232 } else if (cmp[0] == '>') {
3233 if (cmp[1] == '=') {
3234 discrim->min_level = strtoul(cmp+2, NULL, 0);
3236 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3239 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3241 } else if (!irccasecmp(argv[i], "karma")) {
3242 const char *cmp = argv[++i];
3243 if (cmp[0] == '<') {
3244 if (cmp[1] == '=') {
3245 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3247 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3249 } else if (cmp[0] == '=') {
3250 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3251 } else if (cmp[0] == '>') {
3252 if (cmp[1] == '=') {
3253 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3255 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3258 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3261 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3272 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3274 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3275 || (discrim->flags_off & hi->flags)
3276 || (discrim->min_registered > hi->registered)
3277 || (discrim->max_registered < hi->registered)
3278 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3279 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3280 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3281 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3282 || (discrim->min_level > hi->opserv_level)
3283 || (discrim->max_level < hi->opserv_level)
3284 || (discrim->min_karma > hi->karma)
3285 || (discrim->max_karma < hi->karma)
3289 if (discrim->hostmask) {
3291 for (i=0; i<hi->masks->used; i++) {
3292 const char *mask = hi->masks->list[i];
3293 if ((discrim->hostmask_type == SUBSET)
3294 && (match_ircglobs(discrim->hostmask, mask))) break;
3295 else if ((discrim->hostmask_type == EXACT)
3296 && !irccasecmp(discrim->hostmask, mask)) break;
3297 else if ((discrim->hostmask_type == SUPERSET)
3298 && (match_ircglobs(mask, discrim->hostmask))) break;
3299 else if ((discrim->hostmask_type == LASTQUIT)
3300 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3302 if (i==hi->masks->used) return 0;
3304 if (discrim->nickmask) {
3305 struct nick_info *nick = hi->nicks;
3307 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3310 if (!nick) return 0;
3316 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3318 dict_iterator_t it, next;
3319 unsigned int matched;
3321 for (it = dict_first(nickserv_handle_dict), matched = 0;
3322 it && (matched < discrim->limit);
3324 next = iter_next(it);
3325 if (nickserv_discrim_match(discrim, iter_data(it))) {
3326 dsf(source, iter_data(it));
3334 search_print_func(struct userNode *source, struct handle_info *match)
3336 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3340 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3345 search_unregister_func (struct userNode *source, struct handle_info *match)
3347 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3348 nickserv_unregister_handle(match, source);
3352 nickserv_sort_accounts_by_access(const void *a, const void *b)
3354 const struct handle_info *hi_a = *(const struct handle_info**)a;
3355 const struct handle_info *hi_b = *(const struct handle_info**)b;
3356 if (hi_a->opserv_level != hi_b->opserv_level)
3357 return hi_b->opserv_level - hi_a->opserv_level;
3358 return irccasecmp(hi_a->handle, hi_b->handle);
3362 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3364 struct handle_info_list hil;
3365 struct helpfile_table tbl;
3370 memset(&hil, 0, sizeof(hil));
3371 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3372 struct handle_info *hi = iter_data(it);
3373 if (hi->opserv_level)
3374 handle_info_list_append(&hil, hi);
3376 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3377 tbl.length = hil.used + 1;
3379 tbl.flags = TABLE_NO_FREE;
3380 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3381 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3384 for (ii = 0; ii < hil.used; ) {
3385 ary = malloc(tbl.width * sizeof(ary[0]));
3386 ary[0] = hil.list[ii]->handle;
3387 ary[1] = strtab(hil.list[ii]->opserv_level);
3388 tbl.contents[++ii] = ary;
3390 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3391 reply("MSG_MATCH_COUNT", hil.used);
3392 for (ii = 0; ii < hil.used; ii++)
3393 free(tbl.contents[ii]);
3398 static NICKSERV_FUNC(cmd_search)
3400 struct nickserv_discrim *discrim;
3401 discrim_search_func action;
3402 struct svccmd *subcmd;
3403 unsigned int matches;
3406 NICKSERV_MIN_PARMS(3);
3407 sprintf(buf, "search %s", argv[1]);
3408 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3409 if (!irccasecmp(argv[1], "print"))
3410 action = search_print_func;
3411 else if (!irccasecmp(argv[1], "count"))
3412 action = search_count_func;
3413 else if (!irccasecmp(argv[1], "unregister"))
3414 action = search_unregister_func;
3416 reply("NSMSG_INVALID_ACTION", argv[1]);
3420 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3423 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3427 if (action == search_print_func)
3428 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3429 else if (action == search_count_func)
3430 discrim->limit = INT_MAX;
3432 matches = nickserv_discrim_search(discrim, action, user);
3435 reply("MSG_MATCH_COUNT", matches);
3437 reply("MSG_NO_MATCHES");
3443 static MODCMD_FUNC(cmd_checkpass)
3445 struct handle_info *hi;
3447 NICKSERV_MIN_PARMS(3);
3448 if (!(hi = get_handle_info(argv[1]))) {
3449 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3452 if (checkpass(argv[2], hi->passwd))
3453 reply("CHECKPASS_YES");
3455 reply("CHECKPASS_NO");
3460 static MODCMD_FUNC(cmd_checkemail)
3462 struct handle_info *hi;
3464 NICKSERV_MIN_PARMS(3);
3465 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3466 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3469 if (!hi->email_addr)
3470 reply("CHECKEMAIL_NOT_SET");
3471 else if (!irccasecmp(argv[2], hi->email_addr))
3472 reply("CHECKEMAIL_YES");
3474 reply("CHECKEMAIL_NO");
3480 nickserv_db_read_handle(const char *handle, dict_t obj)
3483 struct string_list *masks, *slist;
3484 struct handle_info *hi;
3485 struct userNode *authed_users;
3486 struct userData *channels;
3491 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3492 id = str ? strtoul(str, NULL, 0) : 0;
3493 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3495 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3498 if ((hi = get_handle_info(handle))) {
3499 authed_users = hi->users;
3500 channels = hi->channels;
3502 hi->channels = NULL;
3503 dict_remove(nickserv_handle_dict, hi->handle);
3505 authed_users = NULL;
3508 hi = register_handle(handle, str, id);
3510 hi->users = authed_users;
3511 while (authed_users) {
3512 authed_users->handle_info = hi;
3513 authed_users = authed_users->next_authed;
3516 hi->channels = channels;
3517 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3518 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3519 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3520 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3521 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3522 hi->language = language_find(str ? str : "C");
3523 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3524 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3525 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3527 hi->infoline = strdup(str);
3528 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3529 hi->registered = str ? strtoul(str, NULL, 0) : now;
3530 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3531 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3532 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3533 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3534 /* We want to read the nicks even if disable_nicks is set. This is so
3535 * that we don't lose the nick data entirely. */
3536 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3538 for (ii=0; ii<slist->used; ii++)
3539 register_nick(slist->list[ii], hi);
3541 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3543 for (ii=0; str[ii]; ii++)
3544 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3546 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3547 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3548 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3549 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3550 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3551 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3552 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3554 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3556 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3557 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3559 nickserv_set_email_addr(hi, str);
3560 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3562 hi->epithet = strdup(str);
3563 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3565 hi->fakehost = strdup(str);
3566 /* Read the "cookie" sub-database (if it exists). */
3567 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3569 const char *data, *type, *expires, *cookie_str;
3570 struct handle_cookie *cookie;
3572 cookie = calloc(1, sizeof(*cookie));
3573 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3574 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3575 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3576 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3577 if (!type || !expires || !cookie_str) {
3578 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3581 if (!irccasecmp(type, KEY_ACTIVATION))
3582 cookie->type = ACTIVATION;
3583 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3584 cookie->type = PASSWORD_CHANGE;
3585 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3586 cookie->type = EMAIL_CHANGE;
3587 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3588 cookie->type = ALLOWAUTH;
3590 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3593 cookie->expires = strtoul(expires, NULL, 0);
3594 if (cookie->expires < now)
3597 cookie->data = strdup(data);
3598 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3602 nickserv_bake_cookie(cookie);
3604 nickserv_free_cookie(cookie);
3606 /* Read the "notes" sub-database (if it exists). */
3607 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3610 struct handle_note *last_note;
3611 struct handle_note *note;
3614 for (it = dict_first(subdb); it; it = iter_next(it)) {
3615 const char *expires;
3623 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3625 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", id, hi->handle);
3628 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3629 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3630 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3631 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3632 if (!setter || !text || !set) {
3633 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", id, hi->handle);
3636 note = calloc(1, sizeof(*note) + strlen(text));
3638 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3639 note->set = strtoul(set, NULL, 10);
3640 note->id = strtoul(id, NULL, 10);
3641 safestrncpy(note->setter, setter, sizeof(note->setter));
3642 strcpy(note->note, text);
3644 last_note->next = note;
3653 nickserv_saxdb_read(dict_t db) {
3655 struct record_data *rd;
3657 for (it=dict_first(db); it; it=iter_next(it)) {
3659 nickserv_db_read_handle(iter_key(it), rd->d.object);
3664 static NICKSERV_FUNC(cmd_mergedb)
3666 struct timeval start, stop;
3669 NICKSERV_MIN_PARMS(2);
3670 gettimeofday(&start, NULL);
3671 if (!(db = parse_database(argv[1]))) {
3672 reply("NSMSG_DB_UNREADABLE", argv[1]);
3675 nickserv_saxdb_read(db);
3677 gettimeofday(&stop, NULL);
3678 stop.tv_sec -= start.tv_sec;
3679 stop.tv_usec -= start.tv_usec;
3680 if (stop.tv_usec < 0) {
3682 stop.tv_usec += 1000000;
3684 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
3689 expire_handles(UNUSED_ARG(void *data))
3691 dict_iterator_t it, next;
3692 unsigned long expiry;
3693 struct handle_info *hi;
3695 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3696 next = iter_next(it);
3698 if ((hi->opserv_level > 0)
3700 || HANDLE_FLAGGED(hi, FROZEN)
3701 || HANDLE_FLAGGED(hi, NODELETE)) {
3704 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3705 if ((now - hi->lastseen) > expiry) {
3706 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3707 nickserv_unregister_handle(hi, NULL);
3711 if (nickserv_conf.handle_expire_frequency)
3712 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3716 nickserv_load_dict(const char *fname)
3720 if (!(file = fopen(fname, "r"))) {
3721 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3724 while (!feof(file)) {
3725 fgets(line, sizeof(line), file);
3728 if (line[strlen(line)-1] == '\n')
3729 line[strlen(line)-1] = 0;
3730 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3733 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3736 static enum reclaim_action
3737 reclaim_action_from_string(const char *str) {
3739 return RECLAIM_NONE;
3740 else if (!irccasecmp(str, "warn"))
3741 return RECLAIM_WARN;
3742 else if (!irccasecmp(str, "svsnick"))
3743 return RECLAIM_SVSNICK;
3744 else if (!irccasecmp(str, "kill"))
3745 return RECLAIM_KILL;
3747 return RECLAIM_NONE;
3751 nickserv_conf_read(void)
3753 dict_t conf_node, child;
3757 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3758 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3761 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3763 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3764 if (nickserv_conf.valid_handle_regex_set)
3765 regfree(&nickserv_conf.valid_handle_regex);
3767 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3768 nickserv_conf.valid_handle_regex_set = !err;
3769 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3771 nickserv_conf.valid_handle_regex_set = 0;
3773 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3774 if (nickserv_conf.valid_nick_regex_set)
3775 regfree(&nickserv_conf.valid_nick_regex);
3777 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3778 nickserv_conf.valid_nick_regex_set = !err;
3779 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3781 nickserv_conf.valid_nick_regex_set = 0;
3783 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3785 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3786 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3787 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3788 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3789 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3790 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3791 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3792 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3793 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3794 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3795 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3796 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3797 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3798 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3799 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3800 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3801 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3802 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3803 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3804 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3805 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3806 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3807 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3808 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3809 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3811 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3812 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3813 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3815 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3816 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3817 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3819 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3820 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3821 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3822 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3823 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3824 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3825 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3826 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3827 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
3828 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
3829 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
3832 nickserv_conf.ounregister_flags = 0;
3834 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
3837 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
3839 if (!nickserv_conf.disable_nicks) {
3840 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3841 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3842 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3843 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3844 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3845 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3846 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3847 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3849 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3850 for (it=dict_first(child); it; it=iter_next(it)) {
3851 const char *key = iter_key(it), *value;
3855 if (!strncasecmp(key, "uc_", 3))
3856 flag = toupper(key[3]);
3857 else if (!strncasecmp(key, "lc_", 3))
3858 flag = tolower(key[3]);
3862 if ((pos = handle_inverse_flags[flag])) {
3863 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3864 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3867 if (nickserv_conf.weak_password_dict)
3868 dict_delete(nickserv_conf.weak_password_dict);
3869 nickserv_conf.weak_password_dict = dict_new();
3870 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3871 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3872 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3873 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3875 nickserv_load_dict(str);
3876 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3877 if (nickserv && str)
3878 NickChange(nickserv, str, 0);
3879 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3880 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3881 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3882 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3883 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3884 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3885 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3886 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3887 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3888 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3889 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3890 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3891 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3892 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3893 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3894 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3895 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3896 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3897 str = conf_get_data("server/network", RECDB_QSTRING);
3898 nickserv_conf.network_name = str ? str : "some IRC network";
3899 if (!nickserv_conf.auth_policer_params) {
3900 nickserv_conf.auth_policer_params = policer_params_new();
3901 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3902 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3904 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3905 for (it=dict_first(child); it; it=iter_next(it))
3906 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3910 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3912 char newnick[NICKLEN+1];
3921 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3923 case RECLAIM_SVSNICK:
3925 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3926 } while (GetUserH(newnick));
3927 irc_svsnick(nickserv, user, newnick);
3930 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3931 DelUser(user, nickserv, 1, msg);
3937 nickserv_reclaim_p(void *data) {
3938 struct userNode *user = data;
3939 struct nick_info *ni = get_nick_info(user->nick);
3941 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3945 check_user_nick(struct userNode *user) {
3946 struct nick_info *ni;
3947 user->modes &= ~FLAGS_REGNICK;
3948 if (!(ni = get_nick_info(user->nick)))
3950 if (user->handle_info == ni->owner) {
3951 user->modes |= FLAGS_REGNICK;
3955 if (nickserv_conf.warn_nick_owned)
3956 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3957 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3959 if (nickserv_conf.auto_reclaim_delay)
3960 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3962 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3967 handle_new_user(struct userNode *user)
3969 return check_user_nick(user);
3973 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
3975 struct handle_info *hi = NULL;
3978 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3979 if ((hi == NULL) && (serial != 0)) {
3981 inttobase64(id, serial, IDLEN);
3982 hi = dict_find(nickserv_id_dict, id, NULL);
3986 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3989 set_user_handle_info(user, hi, 0);
3991 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
3996 handle_nick_change(struct userNode *user, const char *old_nick)
3998 struct handle_info *hi;
4000 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4001 dict_remove(nickserv_allow_auth_dict, old_nick);
4002 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4004 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4005 check_user_nick(user);
4009 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4011 dict_remove(nickserv_allow_auth_dict, user->nick);
4012 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4013 set_user_handle_info(user, NULL, 0);
4016 static struct modcmd *
4017 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4019 if (min_level > 0) {
4021 sprintf(buf, "%u", min_level);
4022 if (must_be_qualified) {
4023 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4025 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4027 } else if (min_level == 0) {
4028 if (must_be_qualified) {
4029 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4031 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4034 if (must_be_qualified) {
4035 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4037 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4043 nickserv_db_cleanup(void)
4045 unreg_del_user_func(nickserv_remove_user);
4046 userList_clean(&curr_helpers);
4047 policer_params_delete(nickserv_conf.auth_policer_params);
4048 dict_delete(nickserv_handle_dict);
4049 dict_delete(nickserv_nick_dict);
4050 dict_delete(nickserv_opt_dict);
4051 dict_delete(nickserv_allow_auth_dict);
4052 dict_delete(nickserv_email_dict);
4053 dict_delete(nickserv_id_dict);
4054 dict_delete(nickserv_conf.weak_password_dict);
4055 free(auth_func_list);
4056 free(unreg_func_list);
4058 free(allowauth_func_list);
4059 free(handle_merge_func_list);
4060 free(failpw_func_list);
4061 if (nickserv_conf.valid_handle_regex_set)
4062 regfree(&nickserv_conf.valid_handle_regex);
4063 if (nickserv_conf.valid_nick_regex_set)
4064 regfree(&nickserv_conf.valid_nick_regex);
4068 init_nickserv(const char *nick)
4071 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4072 reg_new_user_func(handle_new_user);
4073 reg_nick_change_func(handle_nick_change);
4074 reg_del_user_func(nickserv_remove_user);
4075 reg_account_func(handle_account);
4077 /* set up handle_inverse_flags */
4078 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4079 for (i=0; handle_flags[i]; i++) {
4080 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4081 flag_access_levels[i] = 0;
4084 conf_register_reload(nickserv_conf_read);
4085 nickserv_opt_dict = dict_new();
4086 nickserv_email_dict = dict_new();
4087 dict_set_free_keys(nickserv_email_dict, free);
4088 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4090 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4091 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4092 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4093 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4094 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4095 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4096 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4097 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4098 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4099 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4100 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4101 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4102 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4103 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4104 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4105 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4106 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4107 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4108 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4109 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4110 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4111 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4112 if (!nickserv_conf.disable_nicks) {
4113 /* nick management commands */
4114 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4115 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4116 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4117 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4118 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4119 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4121 if (nickserv_conf.email_enabled) {
4122 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4123 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4124 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4125 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4126 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4127 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4129 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4130 /* miscellaneous commands */
4131 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4132 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4133 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4134 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4135 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4136 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4138 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4139 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4140 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4141 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4142 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4143 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4144 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4145 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4146 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4147 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4148 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4149 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4150 if (nickserv_conf.titlehost_suffix) {
4151 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4152 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4154 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4155 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4156 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4157 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4159 nickserv_handle_dict = dict_new();
4160 dict_set_free_keys(nickserv_handle_dict, free);
4161 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4163 nickserv_id_dict = dict_new();
4164 dict_set_free_keys(nickserv_id_dict, free);
4166 nickserv_nick_dict = dict_new();
4167 dict_set_free_data(nickserv_nick_dict, free);
4169 nickserv_allow_auth_dict = dict_new();
4171 userList_init(&curr_helpers);
4174 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4175 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4176 nickserv_service = service_register(nickserv);
4178 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4179 reg_exit_func(nickserv_db_cleanup);
4180 if(nickserv_conf.handle_expire_frequency)
4181 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4182 message_register_table(msgtab);