1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2008 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_SET_FAKEIDENT_LEVEL "set_fakeident_level"
55 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
56 #define KEY_FLAG_LEVELS "flag_levels"
57 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
58 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
59 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
60 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
61 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
62 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
63 #define KEY_DICT_FILE "dict_file"
64 #define KEY_NICK "nick"
65 #define KEY_LANGUAGE "language"
66 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
67 #define KEY_AUTOGAG_DURATION "autogag_duration"
68 #define KEY_AUTH_POLICER "auth_policer"
69 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
70 #define KEY_EMAIL_ENABLED "email_enabled"
71 #define KEY_EMAIL_REQUIRED "email_required"
72 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
73 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
74 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
75 #define KEY_OUNREGISTER_INACTIVE "ounregister_inactive"
76 #define KEY_OUNREGISTER_FLAGS "ounregister_flags"
77 #define KEY_HANDLE_TS_MODE "account_timestamp_mode"
80 #define KEY_PASSWD "passwd"
81 #define KEY_NICKS "nicks"
82 #define KEY_MASKS "masks"
83 #define KEY_OPSERV_LEVEL "opserv_level"
84 #define KEY_FLAGS "flags"
85 #define KEY_REGISTER_ON "register"
86 #define KEY_LAST_SEEN "lastseen"
87 #define KEY_INFO "info"
88 #define KEY_USERLIST_STYLE "user_style"
89 #define KEY_SCREEN_WIDTH "screen_width"
90 #define KEY_LAST_AUTHED_HOST "last_authed_host"
91 #define KEY_LAST_QUIT_HOST "last_quit_host"
92 #define KEY_EMAIL_ADDR "email_addr"
93 #define KEY_COOKIE "cookie"
94 #define KEY_COOKIE_DATA "data"
95 #define KEY_COOKIE_TYPE "type"
96 #define KEY_COOKIE_EXPIRES "expires"
97 #define KEY_ACTIVATION "activation"
98 #define KEY_PASSWORD_CHANGE "password change"
99 #define KEY_EMAIL_CHANGE "email change"
100 #define KEY_ALLOWAUTH "allowauth"
101 #define KEY_EPITHET "epithet"
102 #define KEY_TABLE_WIDTH "table_width"
103 #define KEY_MAXLOGINS "maxlogins"
104 #define KEY_FAKEHOST "fakehost"
105 #define KEY_FAKEIDENT "fakeident"
106 #define KEY_NOTES "notes"
107 #define KEY_NOTE_EXPIRES "expires"
108 #define KEY_NOTE_SET "set"
109 #define KEY_NOTE_SETTER "setter"
110 #define KEY_NOTE_NOTE "note"
111 #define KEY_KARMA "karma"
113 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
115 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
116 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
117 typedef OPTION_FUNC(option_func_t);
119 DEFINE_LIST(handle_info_list, struct handle_info*)
121 #define NICKSERV_MIN_PARMS(N) do { \
123 reply("MSG_MISSING_PARAMS", argv[0]); \
124 svccmd_send_help(user, nickserv, cmd); \
128 struct userNode *nickserv;
129 struct userList curr_helpers;
130 const char *handle_flags = HANDLE_FLAGS;
132 static struct module *nickserv_module;
133 static struct service *nickserv_service;
134 static struct log_type *NS_LOG;
135 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
136 static dict_t nickserv_id_dict; /* contains struct handle_info* */
137 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
138 static dict_t nickserv_opt_dict; /* contains option_func_t* */
139 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
140 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
141 static char handle_inverse_flags[256];
142 static unsigned int flag_access_levels[32];
143 static const struct message_entry msgtab[] = {
144 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
145 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
146 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
147 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
148 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
149 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
150 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
151 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
152 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
153 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
154 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
155 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
156 { "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." },
157 { "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." },
158 { "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." },
159 { "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." },
160 { "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." },
161 { "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." },
162 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
163 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
164 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
165 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
166 { "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." },
167 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
168 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
169 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
170 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
171 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
172 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
173 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
174 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
175 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
176 { "NSMSG_ATE_COOKIE_FOREIGN", "I ate the cookie for account $b%s$b. It may now have another." },
177 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
178 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
179 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
180 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
181 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
182 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
183 { "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)" },
184 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
185 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
186 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
187 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
188 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
189 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
190 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
191 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
192 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
193 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
194 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
195 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
196 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
197 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
198 { "NSMSG_FAKEIDENT_INVALID", "Fake idents must be shorter than %d characters." },
199 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
200 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
201 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
202 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
203 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
204 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
205 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
206 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
207 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
208 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
209 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
210 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
211 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
212 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
213 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
214 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
215 { "NSMSG_HANDLEINFO_FAKEIDENT", " Fake ident: %s" },
216 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
217 { "NSMSG_HANDLEINFO_FAKEIDENTHOST", " Fake host: %s@%s" },
218 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
219 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
220 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
221 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
222 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
223 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
224 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
225 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
226 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
227 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
228 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
229 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
230 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
231 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
232 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
233 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
234 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
235 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
236 { "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)." },
237 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
238 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
239 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
240 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
241 { "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." },
242 { "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." },
243 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
244 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
245 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
246 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
247 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
248 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
249 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
250 { "NSMSG_PASS_SUCCESS", "Password changed." },
251 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
252 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
253 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
254 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
255 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
256 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
257 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
258 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
259 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
260 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
261 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
262 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
263 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
264 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
265 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
266 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
267 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
268 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
269 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
270 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
271 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
272 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
273 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
274 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
275 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
276 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
277 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
278 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
279 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
280 { "NSMSG_NO_ACCESS", "Access denied." },
281 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
282 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
283 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
284 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
285 { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
286 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
287 { "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." },
288 { "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." },
289 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
290 { "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." },
291 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
292 { "NSMSG_SEARCH_MATCH", "Match: %s" },
293 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
294 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
295 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
296 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
297 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
298 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
299 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
300 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
301 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
302 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
303 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
304 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
305 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
306 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
307 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
308 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
309 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
310 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
311 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
312 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
313 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
314 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
315 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
316 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
317 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
318 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
319 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
320 { "NSMSG_SET_FAKEIDENT", "$bFAKEIDENT: $b%s" },
321 { "NSMSG_SET_FAKEIDENTHOST", "$bFAKEHOST: $b%s@%s" },
322 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
323 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
324 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
325 { "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." },
326 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
327 { "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." },
328 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
329 { "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." },
330 { "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." },
331 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
332 { "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." },
333 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
334 { "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." },
335 { "CHECKPASS_YES", "Yes." },
336 { "CHECKPASS_NO", "No." },
337 { "CHECKEMAIL_NOT_SET", "No email set." },
338 { "CHECKEMAIL_YES", "Yes." },
339 { "CHECKEMAIL_NO", "No." },
343 enum reclaim_action {
349 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
350 static void nickserv_reclaim_p(void *data);
351 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
353 enum handle_ts_mode {
359 unsigned int disable_nicks : 1;
360 unsigned int valid_handle_regex_set : 1;
361 unsigned int valid_nick_regex_set : 1;
362 unsigned int autogag_enabled : 1;
363 unsigned int email_enabled : 1;
364 unsigned int email_required : 1;
365 unsigned int default_hostmask : 1;
366 unsigned int warn_nick_owned : 1;
367 unsigned int warn_clone_auth : 1;
368 unsigned long nicks_per_handle;
369 unsigned long password_min_length;
370 unsigned long password_min_digits;
371 unsigned long password_min_upper;
372 unsigned long password_min_lower;
373 unsigned long db_backup_frequency;
374 unsigned long handle_expire_frequency;
375 unsigned long autogag_duration;
376 unsigned long email_visible_level;
377 unsigned long cookie_timeout;
378 unsigned long handle_expire_delay;
379 unsigned long nochan_handle_expire_delay;
380 unsigned long modoper_level;
381 unsigned long set_epithet_level;
382 unsigned long set_title_level;
383 unsigned long set_fakehost_level;
384 unsigned long set_fakeident_level;
385 unsigned long handles_per_email;
386 unsigned long email_search_level;
387 const char *network_name;
388 const char *titlehost_suffix;
389 regex_t valid_handle_regex;
390 regex_t valid_nick_regex;
391 dict_t weak_password_dict;
392 struct policer_params *auth_policer_params;
393 enum reclaim_action reclaim_action;
394 enum reclaim_action auto_reclaim_action;
395 enum handle_ts_mode handle_ts_mode;
396 unsigned long auto_reclaim_delay;
397 unsigned char default_maxlogins;
398 unsigned char hard_maxlogins;
399 unsigned long ounregister_inactive;
400 unsigned long ounregister_flags;
403 /* We have 2^32 unique account IDs to use. */
404 unsigned long int highest_id = 0;
406 #define WALK_NOTES(HANDLE, PREV, NOTE) \
407 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
408 if (NOTE->expires && NOTE->expires < now) { \
409 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
411 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
415 canonicalize_hostmask(char *mask)
417 char *out = mask, *temp;
418 if ((temp = strchr(mask, '!'))) {
420 while (*temp) *out++ = *temp++;
426 static struct handle_info *
427 register_handle(const char *handle, const char *passwd, unsigned long id)
429 struct handle_info *hi;
431 char id_base64[IDLEN + 1];
434 /* Assign a unique account ID to the account; note that 0 is
435 an invalid account ID. 1 is therefore the first account ID. */
437 id = 1 + highest_id++;
439 /* Note: highest_id is and must always be the highest ID. */
440 if (id > highest_id) {
444 inttobase64(id_base64, id, IDLEN);
446 /* Make sure an account with the same ID doesn't exist. If a
447 duplicate is found, log some details and assign a new one.
448 This should be impossible, but it never hurts to expect it. */
449 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
450 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
455 hi = calloc(1, sizeof(*hi));
456 hi->userlist_style = HI_DEFAULT_STYLE;
457 hi->handle = strdup(handle);
458 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
460 dict_insert(nickserv_handle_dict, hi->handle, hi);
463 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
469 register_nick(const char *nick, struct handle_info *owner)
471 struct nick_info *ni;
472 ni = malloc(sizeof(struct nick_info));
473 safestrncpy(ni->nick, nick, sizeof(ni->nick));
475 ni->next = owner->nicks;
477 dict_insert(nickserv_nick_dict, ni->nick, ni);
481 delete_nick(struct nick_info *ni)
483 struct nick_info *last, *next;
484 struct userNode *user;
485 /* Check to see if we should mark a user as unregistered. */
486 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
487 user->modes &= ~FLAGS_REGNICK;
490 /* Remove ni from the nick_info linked list. */
491 if (ni == ni->owner->nicks) {
492 ni->owner->nicks = ni->next;
494 last = ni->owner->nicks;
500 last->next = next->next;
502 dict_remove(nickserv_nick_dict, ni->nick);
505 static unreg_func_t *unreg_func_list;
506 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
509 reg_unreg_func(unreg_func_t func)
511 if (unreg_func_used == unreg_func_size) {
512 if (unreg_func_size) {
513 unreg_func_size <<= 1;
514 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
517 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
520 unreg_func_list[unreg_func_used++] = func;
524 nickserv_free_cookie(void *data)
526 struct handle_cookie *cookie = data;
527 if (cookie->hi) cookie->hi->cookie = NULL;
528 if (cookie->data) free(cookie->data);
533 free_handle_info(void *vhi)
535 struct handle_info *hi = vhi;
538 inttobase64(id, hi->id, IDLEN);
539 dict_remove(nickserv_id_dict, id);
541 free_string_list(hi->masks);
545 delete_nick(hi->nicks);
551 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
552 nickserv_free_cookie(hi->cookie);
555 struct handle_note *note = hi->notes;
556 hi->notes = note->next;
559 if (hi->email_addr) {
560 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
561 handle_info_list_remove(hil, hi);
563 dict_remove(nickserv_email_dict, hi->email_addr);
568 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
571 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
575 for (n=0; n<unreg_func_used; n++)
576 unreg_func_list[n](notify, hi);
578 set_user_handle_info(hi->users, NULL, 0);
580 if (nickserv_conf.disable_nicks)
581 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
583 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
585 dict_remove(nickserv_handle_dict, hi->handle);
589 get_handle_info(const char *handle)
591 return dict_find(nickserv_handle_dict, handle, 0);
595 get_nick_info(const char *nick)
597 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
601 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
606 for (nn=0; nn<channel->members.used; ++nn) {
607 mn = channel->members.list[nn];
608 if ((mn->user != except) && (mn->user->handle_info == handle))
615 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
616 if (!user->handle_info) {
618 send_message(user, bot, "MSG_AUTHENTICATE");
622 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
624 send_message(user, bot, "NSMSG_NO_ACCESS");
628 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
630 send_message(user, bot, "MSG_OPER_SUSPENDED");
634 if (user->handle_info->opserv_level < min_level) {
636 send_message(user, bot, "NSMSG_NO_ACCESS");
644 is_valid_handle(const char *handle)
646 struct userNode *user;
647 /* cant register a juped nick/service nick as handle, to prevent confusion */
648 user = GetUserH(handle);
649 if (user && IsLocal(user))
651 /* check against maximum length */
652 if (strlen(handle) > NICKSERV_HANDLE_LEN)
654 /* for consistency, only allow account names that could be nicks */
655 if (!is_valid_nick(handle))
657 /* disallow account names that look like bad words */
658 if (opserv_bad_channel(handle))
660 /* test either regex or containing all valid chars */
661 if (nickserv_conf.valid_handle_regex_set) {
662 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
665 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
666 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
670 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
675 is_registerable_nick(const char *nick)
677 /* make sure it could be used as an account name */
678 if (!is_valid_handle(nick))
681 if (strlen(nick) > NICKLEN)
683 /* test either regex or as valid handle */
684 if (nickserv_conf.valid_nick_regex_set) {
685 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
688 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
689 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
697 is_valid_email_addr(const char *email)
699 return strchr(email, '@') != NULL;
703 visible_email_addr(struct userNode *user, struct handle_info *hi)
705 if (hi->email_addr) {
706 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
707 return hi->email_addr;
717 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
719 struct handle_info *hi;
720 struct userNode *target;
724 if (!(hi = get_handle_info(++name))) {
725 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
730 if (!(target = GetUserH(name))) {
731 send_message(user, service, "MSG_NICK_UNKNOWN", name);
734 if (IsLocal(target)) {
735 if (IsService(target))
736 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
738 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
741 if (!(hi = target->handle_info)) {
742 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
750 oper_outranks(struct userNode *user, struct handle_info *hi) {
751 if (user->handle_info->opserv_level > hi->opserv_level)
753 if (user->handle_info->opserv_level == hi->opserv_level) {
754 if ((user->handle_info->opserv_level == 1000)
755 || (user->handle_info == hi)
756 || ((user->handle_info->opserv_level == 0)
757 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
758 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
762 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
766 static struct handle_info *
767 get_victim_oper(struct userNode *user, const char *target)
769 struct handle_info *hi;
770 if (!(hi = smart_get_handle_info(nickserv, user, target)))
772 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
773 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
776 return oper_outranks(user, hi) ? hi : NULL;
780 valid_user_for(struct userNode *user, struct handle_info *hi)
784 /* If no hostmasks on the account, allow it. */
785 if (!hi->masks->used)
787 /* If any hostmask matches, allow it. */
788 for (ii=0; ii<hi->masks->used; ii++)
789 if (user_matches_glob(user, hi->masks->list[ii], 0))
791 /* If they are allowauthed to this account, allow it (removing the aa). */
792 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
793 dict_remove(nickserv_allow_auth_dict, user->nick);
796 /* The user is not allowed to use this account. */
801 is_secure_password(const char *handle, const char *pass, struct userNode *user)
804 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
808 if (len < nickserv_conf.password_min_length) {
810 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
813 if (!irccasecmp(pass, handle)) {
815 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
818 dict_find(nickserv_conf.weak_password_dict, pass, &p);
821 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
824 for (i=0; i<len; i++) {
825 if (isdigit(pass[i]))
827 if (isupper(pass[i]))
829 if (islower(pass[i]))
832 if ((cnt_lower < nickserv_conf.password_min_lower)
833 || (cnt_upper < nickserv_conf.password_min_upper)
834 || (cnt_digits < nickserv_conf.password_min_digits)) {
836 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
842 static auth_func_t *auth_func_list;
843 static unsigned int auth_func_size = 0, auth_func_used = 0;
846 reg_auth_func(auth_func_t func)
848 if (auth_func_used == auth_func_size) {
849 if (auth_func_size) {
850 auth_func_size <<= 1;
851 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
854 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
857 auth_func_list[auth_func_used++] = func;
860 static handle_rename_func_t *rf_list;
861 static unsigned int rf_list_size, rf_list_used;
864 reg_handle_rename_func(handle_rename_func_t func)
866 if (rf_list_used == rf_list_size) {
869 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
872 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
875 rf_list[rf_list_used++] = func;
879 generate_fakehost(struct handle_info *handle)
881 extern const char *hidden_host_suffix;
882 static char buffer[HOSTLEN+1];
884 if (!handle->fakehost) {
885 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
887 } else if (handle->fakehost[0] == '.') {
888 /* A leading dot indicates the stored value is actually a title. */
889 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
892 return handle->fakehost;
896 generate_fakeident(struct handle_info *handle, struct userNode *user)
898 static char buffer[USERLEN+1];
900 if (!handle->fakeident) {
903 safestrncpy(buffer, user->ident, sizeof(buffer));
906 return handle->fakeident;
910 apply_fakehost(struct handle_info *handle, struct userNode *user)
912 struct userNode *target;
913 char *fakehost, *fakeident;
918 fakehost = generate_fakehost(handle);
921 fakeident = generate_fakeident(handle, user);
922 assign_fakehost(user, fakehost, fakeident, 0, 1);
926 for (target = handle->users; target; target = target->next_authed) {
927 fakeident = generate_fakeident(handle, target);
928 assign_fakehost(target, fakehost, fakeident, 0, 1);
933 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
936 struct handle_info *old_info;
938 /* This can happen if somebody uses COOKIE while authed, or if
939 * they re-auth to their current handle (which is silly, but users
941 if (user->handle_info == hi)
944 if (user->handle_info) {
945 struct userNode *other;
948 userList_remove(&curr_helpers, user);
950 /* remove from next_authed linked list */
951 if (user->handle_info->users == user) {
952 user->handle_info->users = user->next_authed;
953 } else if (user->handle_info->users != NULL) {
954 for (other = user->handle_info->users;
955 other->next_authed != user;
956 other = other->next_authed) ;
957 other->next_authed = user->next_authed;
959 /* No users authed to the account - can happen if they get
960 * killed for authing. */
962 /* if nobody left on old handle, and they're not an oper, remove !god */
963 if (!user->handle_info->users && !user->handle_info->opserv_level)
964 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
965 /* record them as being last seen at this time */
966 user->handle_info->lastseen = now;
967 /* and record their hostmask */
968 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
970 old_info = user->handle_info;
971 user->handle_info = hi;
972 if (hi && !hi->users && !hi->opserv_level)
973 HANDLE_CLEAR_FLAG(hi, HELPING);
974 for (n=0; n<auth_func_used; n++) {
975 auth_func_list[n](user, old_info);
980 struct nick_info *ni;
982 HANDLE_CLEAR_FLAG(hi, FROZEN);
983 if (nickserv_conf.warn_clone_auth) {
984 struct userNode *other;
985 for (other = hi->users; other; other = other->next_authed)
986 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
988 user->next_authed = hi->users;
991 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
992 userList_append(&curr_helpers, user);
994 if (hi->fakehost || hi->fakeident || old_info)
995 apply_fakehost(hi, user);
998 if (!nickserv_conf.disable_nicks) {
999 struct nick_info *ni2;
1000 for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
1001 if (!irccasecmp(user->nick, ni2->nick)) {
1002 user->modes |= FLAGS_REGNICK;
1007 StampUser(user, hi->handle, hi->registered, hi->id);
1010 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
1011 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
1013 /* We cannot clear the user's account ID, unfortunately. */
1014 user->next_authed = NULL;
1018 static struct handle_info*
1019 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
1021 struct handle_info *hi;
1022 struct nick_info *ni;
1023 char crypted[MD5_CRYPT_LENGTH];
1025 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
1026 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
1030 if (!is_secure_password(handle, passwd, user))
1033 cryptpass(passwd, crypted);
1034 hi = register_handle(handle, crypted, 0);
1035 hi->masks = alloc_string_list(1);
1037 hi->language = lang_C;
1038 hi->registered = now;
1040 hi->flags = HI_DEFAULT_FLAGS;
1041 if (settee && !no_auth)
1042 set_user_handle_info(settee, hi, 1);
1045 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1046 else if (nickserv_conf.disable_nicks)
1047 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1048 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1049 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1051 register_nick(user->nick, hi);
1052 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1054 if (settee && (user != settee))
1055 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1060 nickserv_bake_cookie(struct handle_cookie *cookie)
1062 cookie->hi->cookie = cookie;
1063 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1067 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1069 struct handle_cookie *cookie;
1070 char subject[128], body[4096], *misc;
1071 const char *netname, *fmt;
1075 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1079 cookie = calloc(1, sizeof(*cookie));
1081 cookie->type = type;
1082 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1083 cookie->expires = now + nickserv_conf.cookie_timeout;
1084 inttobase64(cookie->cookie, rand(), 5);
1085 inttobase64(cookie->cookie+5, rand(), 5);
1087 netname = nickserv_conf.network_name;
1090 switch (cookie->type) {
1092 hi->passwd[0] = 0; /* invalidate password */
1093 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1094 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1095 snprintf(subject, sizeof(subject), fmt, netname);
1096 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1097 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1100 case PASSWORD_CHANGE:
1101 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1102 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1103 snprintf(subject, sizeof(subject), fmt, netname);
1104 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1105 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1108 misc = hi->email_addr;
1109 hi->email_addr = cookie->data;
1111 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1112 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1113 snprintf(subject, sizeof(subject), fmt, netname);
1114 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1115 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1116 mail_send(nickserv, hi, subject, body, 1);
1117 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1118 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1120 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1121 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1122 snprintf(subject, sizeof(subject), fmt, netname);
1123 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1124 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1125 mail_send(nickserv, hi, subject, body, 1);
1128 hi->email_addr = misc;
1131 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1132 snprintf(subject, sizeof(subject), fmt, netname);
1133 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1134 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1135 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1138 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1142 mail_send(nickserv, hi, subject, body, first_time);
1143 nickserv_bake_cookie(cookie);
1147 nickserv_eat_cookie(struct handle_cookie *cookie)
1149 cookie->hi->cookie = NULL;
1150 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1151 nickserv_free_cookie(cookie);
1155 nickserv_free_email_addr(void *data)
1157 handle_info_list_clean(data);
1162 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1164 struct handle_info_list *hil;
1165 /* Remove from old handle_info_list ... */
1166 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1167 handle_info_list_remove(hil, hi);
1168 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1169 hi->email_addr = NULL;
1171 /* Add to the new list.. */
1172 if (new_email_addr) {
1173 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1174 hil = calloc(1, sizeof(*hil));
1175 hil->tag = strdup(new_email_addr);
1176 handle_info_list_init(hil);
1177 dict_insert(nickserv_email_dict, hil->tag, hil);
1179 handle_info_list_append(hil, hi);
1180 hi->email_addr = hil->tag;
1184 static NICKSERV_FUNC(cmd_register)
1187 struct handle_info *hi;
1188 const char *email_addr, *password;
1191 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1192 /* Require the first handle registered to belong to someone +o. */
1193 reply("NSMSG_REQUIRE_OPER");
1197 if (user->handle_info) {
1198 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1202 if (IsRegistering(user)) {
1203 reply("NSMSG_ALREADY_REGISTERING");
1207 if (IsStamped(user)) {
1208 /* Unauthenticated users might still have been stamped
1209 previously and could therefore have a hidden host;
1210 do not allow them to register a new account. */
1211 reply("NSMSG_STAMPED_REGISTER");
1215 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1217 if (!is_valid_handle(argv[1])) {
1218 reply("NSMSG_BAD_HANDLE", argv[1]);
1222 if ((argc >= 4) && nickserv_conf.email_enabled) {
1223 struct handle_info_list *hil;
1226 /* Remember email address. */
1227 email_addr = argv[3];
1229 /* Check that the email address looks valid.. */
1230 if (!is_valid_email_addr(email_addr)) {
1231 reply("NSMSG_BAD_EMAIL_ADDR");
1235 /* .. and that we are allowed to send to it. */
1236 if ((str = mail_prohibited_address(email_addr))) {
1237 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1241 /* If we do email verify, make sure we don't spam the address. */
1242 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1244 for (nn=0; nn<hil->used; nn++) {
1245 if (hil->list[nn]->cookie) {
1246 reply("NSMSG_EMAIL_UNACTIVATED");
1250 if (hil->used >= nickserv_conf.handles_per_email) {
1251 reply("NSMSG_EMAIL_OVERUSED");
1264 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1266 /* Add any masks they should get. */
1267 if (nickserv_conf.default_hostmask) {
1268 string_list_append(hi->masks, strdup("*@*"));
1270 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1271 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1272 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1275 /* If they're the first to register, give them level 1000. */
1276 if (dict_size(nickserv_handle_dict) == 1) {
1277 hi->opserv_level = 1000;
1278 reply("NSMSG_ROOT_HANDLE", argv[1]);
1281 /* Set their email address. */
1283 nickserv_set_email_addr(hi, email_addr);
1285 /* If they need to do email verification, tell them. */
1287 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1289 /* Set registering flag.. */
1290 user->modes |= FLAGS_REGISTERING;
1295 static NICKSERV_FUNC(cmd_oregister)
1298 struct userNode *settee;
1299 struct handle_info *hi;
1301 NICKSERV_MIN_PARMS(3);
1303 if (!is_valid_handle(argv[1])) {
1304 reply("NSMSG_BAD_HANDLE", argv[1]);
1311 } else if (strchr(argv[3], '@')) {
1312 mask = canonicalize_hostmask(strdup(argv[3]));
1314 settee = GetUserH(argv[4]);
1316 reply("MSG_NICK_UNKNOWN", argv[4]);
1323 } else if ((settee = GetUserH(argv[3]))) {
1324 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1326 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1329 if (settee && settee->handle_info) {
1330 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1334 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1339 string_list_append(hi->masks, mask);
1343 static NICKSERV_FUNC(cmd_handleinfo)
1346 unsigned int i, pos=0, herelen;
1347 struct userNode *target, *next_un;
1348 struct handle_info *hi;
1349 const char *nsmsg_none;
1353 if (!(hi = user->handle_info)) {
1354 reply("NSMSG_MUST_AUTH");
1357 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1361 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1362 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1363 feh = hi->registered;
1364 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1367 intervalString(buff, now - hi->lastseen, user->handle_info);
1368 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1370 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1373 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1374 if (HANDLE_FLAGGED(hi, FROZEN))
1375 reply("NSMSG_HANDLEINFO_VACATION");
1377 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1378 struct do_not_register *dnr;
1379 if ((dnr = chanserv_is_dnr(NULL, hi)))
1380 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1381 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1383 } else if (hi != user->handle_info)
1387 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1389 if (nickserv_conf.email_enabled)
1390 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1394 switch (hi->cookie->type) {
1395 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1396 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1397 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1398 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1399 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1404 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1405 reply("NSMSG_HANDLEINFO_ID", hi->id);
1407 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1409 reply("NSMSG_HANDLEINFO_NO_NOTES");
1411 struct handle_note *prev, *note;
1413 WALK_NOTES(hi, prev, note) {
1414 char set_time[INTERVALLEN];
1415 intervalString(set_time, now - note->set, user->handle_info);
1416 if (note->expires) {
1417 char exp_time[INTERVALLEN];
1418 intervalString(exp_time, note->expires - now, user->handle_info);
1419 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1421 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1428 unsigned long flen = 1;
1429 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1431 for (i=0, flen=1; handle_flags[i]; i++)
1432 if (hi->flags & 1 << i)
1433 flags[flen++] = handle_flags[i];
1435 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1437 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1440 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1441 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1442 || (hi->opserv_level > 0)) {
1443 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1446 if (hi->fakeident && hi->fakehost)
1447 reply("NSMSG_HANDLEINFO_FAKEIDENTHOST", hi->fakeident, hi->fakehost);
1448 else if (hi->fakeident)
1449 reply("NSMSG_HANDLEINFO_FAKEIDENT", hi->fakeident);
1450 else if (hi->fakehost)
1451 reply("NSMSG_HANDLEINFO_FAKEHOST", hi->fakehost);
1453 if (hi->last_quit_host[0])
1454 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1456 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1458 if (nickserv_conf.disable_nicks) {
1459 /* nicks disabled; don't show anything about registered nicks */
1460 } else if (hi->nicks) {
1461 struct nick_info *ni, *next_ni;
1462 for (ni = hi->nicks; ni; ni = next_ni) {
1463 herelen = strlen(ni->nick);
1464 if (pos + herelen + 1 > ArrayLength(buff)) {
1466 goto print_nicks_buff;
1470 memcpy(buff+pos, ni->nick, herelen);
1471 pos += herelen; buff[pos++] = ' ';
1475 reply("NSMSG_HANDLEINFO_NICKS", buff);
1480 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1483 if (hi->masks->used) {
1484 for (i=0; i < hi->masks->used; i++) {
1485 herelen = strlen(hi->masks->list[i]);
1486 if (pos + herelen + 1 > ArrayLength(buff)) {
1488 goto print_mask_buff;
1490 memcpy(buff+pos, hi->masks->list[i], herelen);
1491 pos += herelen; buff[pos++] = ' ';
1492 if (i+1 == hi->masks->used) {
1495 reply("NSMSG_HANDLEINFO_MASKS", buff);
1500 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1504 struct userData *chan, *next;
1507 for (chan = hi->channels; chan; chan = next) {
1508 next = chan->u_next;
1509 name = chan->channel->channel->name;
1510 herelen = strlen(name);
1511 if (pos + herelen + 7 > ArrayLength(buff)) {
1513 goto print_chans_buff;
1515 if (IsUserSuspended(chan))
1517 pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
1521 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1526 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1529 for (target = hi->users; target; target = next_un) {
1530 herelen = strlen(target->nick);
1531 if (pos + herelen + 1 > ArrayLength(buff)) {
1533 goto print_cnick_buff;
1535 next_un = target->next_authed;
1537 memcpy(buff+pos, target->nick, herelen);
1538 pos += herelen; buff[pos++] = ' ';
1542 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1547 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1550 static NICKSERV_FUNC(cmd_userinfo)
1552 struct userNode *target;
1554 NICKSERV_MIN_PARMS(2);
1555 if (!(target = GetUserH(argv[1]))) {
1556 reply("MSG_NICK_UNKNOWN", argv[1]);
1559 if (target->handle_info)
1560 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1562 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1566 static NICKSERV_FUNC(cmd_nickinfo)
1568 struct nick_info *ni;
1570 NICKSERV_MIN_PARMS(2);
1571 if (!(ni = get_nick_info(argv[1]))) {
1572 reply("MSG_NICK_UNKNOWN", argv[1]);
1575 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1579 static NICKSERV_FUNC(cmd_notes)
1581 struct handle_info *hi;
1582 struct handle_note *prev, *note;
1585 NICKSERV_MIN_PARMS(2);
1586 if (!(hi = get_victim_oper(user, argv[1])))
1589 WALK_NOTES(hi, prev, note) {
1590 char set_time[INTERVALLEN];
1591 intervalString(set_time, now - note->set, user->handle_info);
1592 if (note->expires) {
1593 char exp_time[INTERVALLEN];
1594 intervalString(exp_time, note->expires - now, user->handle_info);
1595 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1597 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1601 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1605 static NICKSERV_FUNC(cmd_rename_handle)
1607 struct handle_info *hi;
1608 char msgbuf[MAXLEN], *old_handle;
1611 NICKSERV_MIN_PARMS(3);
1612 if (!(hi = get_victim_oper(user, argv[1])))
1614 if (!is_valid_handle(argv[2])) {
1615 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1618 if (get_handle_info(argv[2])) {
1619 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1623 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1624 hi->handle = strdup(argv[2]);
1625 dict_insert(nickserv_handle_dict, hi->handle, hi);
1626 for (nn=0; nn<rf_list_used; nn++)
1627 rf_list[nn](hi, old_handle);
1628 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1629 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1630 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1635 static failpw_func_t *failpw_func_list;
1636 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1639 reg_failpw_func(failpw_func_t func)
1641 if (failpw_func_used == failpw_func_size) {
1642 if (failpw_func_size) {
1643 failpw_func_size <<= 1;
1644 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1646 failpw_func_size = 8;
1647 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1650 failpw_func_list[failpw_func_used++] = func;
1653 static NICKSERV_FUNC(cmd_auth)
1655 int pw_arg, used, maxlogins;
1656 struct handle_info *hi;
1658 struct userNode *other;
1660 if (user->handle_info) {
1661 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1664 if (IsStamped(user)) {
1665 /* Unauthenticated users might still have been stamped
1666 previously and could therefore have a hidden host;
1667 do not allow them to authenticate. */
1668 reply("NSMSG_STAMPED_AUTH");
1672 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1674 } else if (argc == 2) {
1675 if (nickserv_conf.disable_nicks) {
1676 if (!(hi = get_handle_info(user->nick))) {
1677 reply("NSMSG_HANDLE_NOT_FOUND");
1681 /* try to look up their handle from their nick */
1682 struct nick_info *ni;
1683 ni = get_nick_info(user->nick);
1685 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1692 reply("MSG_MISSING_PARAMS", argv[0]);
1693 svccmd_send_help(user, nickserv, cmd);
1697 reply("NSMSG_HANDLE_NOT_FOUND");
1700 /* Responses from here on look up the language used by the handle they asked about. */
1701 passwd = argv[pw_arg];
1702 if (!valid_user_for(user, hi)) {
1703 if (hi->email_addr && nickserv_conf.email_enabled)
1704 send_message_type(4, user, cmd->parent->bot,
1705 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1708 send_message_type(4, user, cmd->parent->bot,
1709 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1711 argv[pw_arg] = "BADMASK";
1714 if (!checkpass(passwd, hi->passwd)) {
1716 send_message_type(4, user, cmd->parent->bot,
1717 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1718 argv[pw_arg] = "BADPASS";
1719 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1720 if (nickserv_conf.autogag_enabled) {
1721 if (!user->auth_policer.params) {
1722 user->auth_policer.last_req = now;
1723 user->auth_policer.params = nickserv_conf.auth_policer_params;
1725 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1727 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1728 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1729 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1731 argv[pw_arg] = "GAGGED";
1736 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1737 send_message_type(4, user, cmd->parent->bot,
1738 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1739 argv[pw_arg] = "SUSPENDED";
1742 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1743 for (used = 0, other = hi->users; other; other = other->next_authed) {
1744 if (++used >= maxlogins) {
1745 send_message_type(4, user, cmd->parent->bot,
1746 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1748 argv[pw_arg] = "MAXLOGINS";
1753 set_user_handle_info(user, hi, 1);
1754 if (nickserv_conf.email_required && !hi->email_addr)
1755 reply("NSMSG_PLEASE_SET_EMAIL");
1756 if (!is_secure_password(hi->handle, passwd, NULL))
1757 reply("NSMSG_WEAK_PASSWORD");
1758 if (hi->passwd[0] != '$')
1759 cryptpass(passwd, hi->passwd);
1760 if (!hi->masks->used) {
1762 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1763 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1764 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1766 argv[pw_arg] = "****";
1767 reply("NSMSG_AUTH_SUCCESS");
1771 static allowauth_func_t *allowauth_func_list;
1772 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1775 reg_allowauth_func(allowauth_func_t func)
1777 if (allowauth_func_used == allowauth_func_size) {
1778 if (allowauth_func_size) {
1779 allowauth_func_size <<= 1;
1780 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1782 allowauth_func_size = 8;
1783 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1786 allowauth_func_list[allowauth_func_used++] = func;
1789 static NICKSERV_FUNC(cmd_allowauth)
1791 struct userNode *target;
1792 struct handle_info *hi;
1795 NICKSERV_MIN_PARMS(2);
1796 if (!(target = GetUserH(argv[1]))) {
1797 reply("MSG_NICK_UNKNOWN", argv[1]);
1800 if (target->handle_info) {
1801 reply("NSMSG_USER_PREV_AUTH", target->nick);
1804 if (IsStamped(target)) {
1805 /* Unauthenticated users might still have been stamped
1806 previously and could therefore have a hidden host;
1807 do not allow them to authenticate to an account. */
1808 reply("NSMSG_USER_PREV_STAMP", target->nick);
1813 else if (!(hi = get_handle_info(argv[2]))) {
1814 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1818 if (hi->opserv_level > user->handle_info->opserv_level) {
1819 reply("MSG_USER_OUTRANKED", hi->handle);
1822 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1823 || (hi->opserv_level > 0))
1824 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1825 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1828 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1829 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1830 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1831 if (nickserv_conf.email_enabled)
1832 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1834 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1835 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1837 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1839 for (n=0; n<allowauth_func_used; n++)
1840 allowauth_func_list[n](user, target, hi);
1844 static NICKSERV_FUNC(cmd_authcookie)
1846 struct handle_info *hi;
1848 NICKSERV_MIN_PARMS(2);
1849 if (user->handle_info) {
1850 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1853 if (IsStamped(user)) {
1854 /* Unauthenticated users might still have been stamped
1855 previously and could therefore have a hidden host;
1856 do not allow them to authenticate to an account. */
1857 reply("NSMSG_STAMPED_AUTHCOOKIE");
1860 if (!(hi = get_handle_info(argv[1]))) {
1861 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1864 if (!hi->email_addr) {
1865 reply("MSG_SET_EMAIL_ADDR");
1868 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1872 static NICKSERV_FUNC(cmd_delcookie)
1874 struct handle_info *hi;
1876 hi = user->handle_info;
1878 reply("NSMSG_NO_COOKIE");
1881 switch (hi->cookie->type) {
1884 reply("NSMSG_MUST_TIME_OUT");
1887 nickserv_eat_cookie(hi->cookie);
1888 reply("NSMSG_ATE_COOKIE");
1894 static NICKSERV_FUNC(cmd_odelcookie)
1896 struct handle_info *hi;
1898 NICKSERV_MIN_PARMS(2);
1900 if (!(hi = get_victim_oper(user, argv[1])))
1904 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
1908 nickserv_eat_cookie(hi->cookie);
1909 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
1914 static NICKSERV_FUNC(cmd_resetpass)
1916 struct handle_info *hi;
1917 char crypted[MD5_CRYPT_LENGTH];
1919 NICKSERV_MIN_PARMS(3);
1920 if (user->handle_info) {
1921 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1924 if (IsStamped(user)) {
1925 /* Unauthenticated users might still have been stamped
1926 previously and could therefore have a hidden host;
1927 do not allow them to activate an account. */
1928 reply("NSMSG_STAMPED_RESETPASS");
1931 if (!(hi = get_handle_info(argv[1]))) {
1932 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1935 if (!hi->email_addr) {
1936 reply("MSG_SET_EMAIL_ADDR");
1939 cryptpass(argv[2], crypted);
1941 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1945 static NICKSERV_FUNC(cmd_cookie)
1947 struct handle_info *hi;
1950 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1953 NICKSERV_MIN_PARMS(3);
1954 if (!(hi = get_handle_info(argv[1]))) {
1955 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1961 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1962 reply("NSMSG_HANDLE_SUSPENDED");
1967 reply("NSMSG_NO_COOKIE");
1971 /* Check validity of operation before comparing cookie to
1972 * prohibit guessing by authed users. */
1973 if (user->handle_info
1974 && (hi->cookie->type != EMAIL_CHANGE)
1975 && (hi->cookie->type != PASSWORD_CHANGE)) {
1976 reply("NSMSG_CANNOT_COOKIE");
1980 if (strcmp(cookie, hi->cookie->cookie)) {
1981 reply("NSMSG_BAD_COOKIE");
1985 switch (hi->cookie->type) {
1987 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1988 set_user_handle_info(user, hi, 1);
1989 reply("NSMSG_HANDLE_ACTIVATED");
1991 case PASSWORD_CHANGE:
1992 set_user_handle_info(user, hi, 1);
1993 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1994 reply("NSMSG_PASSWORD_CHANGED");
1997 nickserv_set_email_addr(hi, hi->cookie->data);
1998 reply("NSMSG_EMAIL_CHANGED");
2001 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2002 set_user_handle_info(user, hi, 1);
2003 nickserv_addmask(user, hi, mask);
2004 reply("NSMSG_AUTH_SUCCESS");
2009 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2010 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2014 nickserv_eat_cookie(hi->cookie);
2019 static NICKSERV_FUNC(cmd_oregnick) {
2021 struct handle_info *target;
2022 struct nick_info *ni;
2024 NICKSERV_MIN_PARMS(3);
2025 if (!(target = modcmd_get_handle_info(user, argv[1])))
2028 if (!is_registerable_nick(nick)) {
2029 reply("NSMSG_BAD_NICK", nick);
2032 ni = dict_find(nickserv_nick_dict, nick, NULL);
2034 reply("NSMSG_NICK_EXISTS", nick);
2037 register_nick(nick, target);
2038 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2042 static NICKSERV_FUNC(cmd_regnick) {
2044 struct nick_info *ni;
2046 if (!is_registerable_nick(user->nick)) {
2047 reply("NSMSG_BAD_NICK", user->nick);
2050 /* count their nicks, see if it's too many */
2051 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2052 if (n >= nickserv_conf.nicks_per_handle) {
2053 reply("NSMSG_TOO_MANY_NICKS");
2056 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2058 reply("NSMSG_NICK_EXISTS", user->nick);
2061 register_nick(user->nick, user->handle_info);
2062 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2066 static NICKSERV_FUNC(cmd_pass)
2068 struct handle_info *hi;
2069 const char *old_pass, *new_pass;
2071 NICKSERV_MIN_PARMS(3);
2072 hi = user->handle_info;
2076 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2077 if (!checkpass(old_pass, hi->passwd)) {
2078 argv[1] = "BADPASS";
2079 reply("NSMSG_PASSWORD_INVALID");
2082 cryptpass(new_pass, hi->passwd);
2084 reply("NSMSG_PASS_SUCCESS");
2089 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2092 char *new_mask = canonicalize_hostmask(strdup(mask));
2093 for (i=0; i<hi->masks->used; i++) {
2094 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2095 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2100 string_list_append(hi->masks, new_mask);
2101 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2105 static NICKSERV_FUNC(cmd_addmask)
2108 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2109 int res = nickserv_addmask(user, user->handle_info, mask);
2113 if (!is_gline(argv[1])) {
2114 reply("NSMSG_MASK_INVALID", argv[1]);
2117 return nickserv_addmask(user, user->handle_info, argv[1]);
2121 static NICKSERV_FUNC(cmd_oaddmask)
2123 struct handle_info *hi;
2125 NICKSERV_MIN_PARMS(3);
2126 if (!(hi = get_victim_oper(user, argv[1])))
2128 return nickserv_addmask(user, hi, argv[2]);
2132 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2135 for (i=0; i<hi->masks->used; i++) {
2136 if (!strcmp(del_mask, hi->masks->list[i])) {
2137 char *old_mask = hi->masks->list[i];
2138 if (hi->masks->used == 1 && !force) {
2139 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2142 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2143 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2148 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2152 static NICKSERV_FUNC(cmd_delmask)
2154 NICKSERV_MIN_PARMS(2);
2155 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2158 static NICKSERV_FUNC(cmd_odelmask)
2160 struct handle_info *hi;
2161 NICKSERV_MIN_PARMS(3);
2162 if (!(hi = get_victim_oper(user, argv[1])))
2164 return nickserv_delmask(user, hi, argv[2], 1);
2168 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2169 unsigned int nn, add = 1, pos;
2170 unsigned long added, removed, flag;
2172 for (added=removed=nn=0; str[nn]; nn++) {
2174 case '+': add = 1; break;
2175 case '-': add = 0; break;
2177 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2178 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2181 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2182 /* cheesy avoidance of looking up the flag name.. */
2183 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2186 flag = 1 << (pos - 1);
2188 added |= flag, removed &= ~flag;
2190 removed |= flag, added &= ~flag;
2195 *premoved = removed;
2200 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2202 unsigned long before, after, added, removed;
2203 struct userNode *uNode;
2205 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2206 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2208 hi->flags = (hi->flags | added) & ~removed;
2209 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2211 /* Strip helping flag if they're only a support helper and not
2212 * currently in #support. */
2213 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2214 struct channelList *schannels;
2216 schannels = chanserv_support_channels();
2217 for (ii = 0; ii < schannels->used; ++ii)
2218 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2220 if (ii == schannels->used)
2221 HANDLE_CLEAR_FLAG(hi, HELPING);
2224 if (after && !before) {
2225 /* Add user to current helper list. */
2226 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2227 userList_append(&curr_helpers, uNode);
2228 } else if (!after && before) {
2229 /* Remove user from current helper list. */
2230 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2231 userList_remove(&curr_helpers, uNode);
2238 set_list(struct userNode *user, struct handle_info *hi, int override)
2242 char *set_display[] = {
2243 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2244 "EMAIL", "MAXLOGINS", "LANGUAGE"
2247 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2249 /* Do this so options are presented in a consistent order. */
2250 for (i = 0; i < ArrayLength(set_display); ++i)
2251 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2252 opt(user, hi, override, 0, NULL);
2255 static NICKSERV_FUNC(cmd_set)
2257 struct handle_info *hi;
2260 hi = user->handle_info;
2262 set_list(user, hi, 0);
2265 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2266 reply("NSMSG_INVALID_OPTION", argv[1]);
2269 return opt(user, hi, 0, argc-1, argv+1);
2272 static NICKSERV_FUNC(cmd_oset)
2274 struct handle_info *hi;
2275 struct svccmd *subcmd;
2277 char cmdname[MAXLEN];
2279 NICKSERV_MIN_PARMS(2);
2281 if (!(hi = get_victim_oper(user, argv[1])))
2285 set_list(user, hi, 0);
2289 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2290 reply("NSMSG_INVALID_OPTION", argv[2]);
2294 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2295 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2296 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2299 return opt(user, hi, 1, argc-2, argv+2);
2302 static OPTION_FUNC(opt_info)
2306 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2308 hi->infoline = NULL;
2310 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2314 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2315 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2319 static OPTION_FUNC(opt_width)
2322 hi->screen_width = strtoul(argv[1], NULL, 0);
2324 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2325 hi->screen_width = MIN_LINE_SIZE;
2326 else if (hi->screen_width > MAX_LINE_SIZE)
2327 hi->screen_width = MAX_LINE_SIZE;
2329 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2333 static OPTION_FUNC(opt_tablewidth)
2336 hi->table_width = strtoul(argv[1], NULL, 0);
2338 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2339 hi->table_width = MIN_LINE_SIZE;
2340 else if (hi->screen_width > MAX_LINE_SIZE)
2341 hi->table_width = MAX_LINE_SIZE;
2343 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2347 static OPTION_FUNC(opt_color)
2350 if (enabled_string(argv[1]))
2351 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2352 else if (disabled_string(argv[1]))
2353 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2355 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2360 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2364 static OPTION_FUNC(opt_privmsg)
2367 if (enabled_string(argv[1]))
2368 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2369 else if (disabled_string(argv[1]))
2370 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2372 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2377 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2381 static OPTION_FUNC(opt_style)
2386 if (!irccasecmp(argv[1], "Zoot"))
2387 hi->userlist_style = HI_STYLE_ZOOT;
2388 else if (!irccasecmp(argv[1], "def"))
2389 hi->userlist_style = HI_STYLE_DEF;
2392 switch (hi->userlist_style) {
2401 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2405 static OPTION_FUNC(opt_password)
2408 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2413 cryptpass(argv[1], hi->passwd);
2415 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2419 static OPTION_FUNC(opt_flags)
2422 unsigned int ii, flen;
2425 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2430 nickserv_apply_flags(user, hi, argv[1]);
2432 for (ii = flen = 0; handle_flags[ii]; ii++)
2433 if (hi->flags & (1 << ii))
2434 flags[flen++] = handle_flags[ii];
2437 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2439 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2443 static OPTION_FUNC(opt_email)
2447 if (!is_valid_email_addr(argv[1])) {
2448 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2451 if ((str = mail_prohibited_address(argv[1]))) {
2452 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2455 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2456 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2458 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2460 nickserv_set_email_addr(hi, argv[1]);
2462 nickserv_eat_cookie(hi->cookie);
2463 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2466 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2470 static OPTION_FUNC(opt_maxlogins)
2472 unsigned char maxlogins;
2474 maxlogins = strtoul(argv[1], NULL, 0);
2475 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2476 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2479 hi->maxlogins = maxlogins;
2481 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2482 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2486 static OPTION_FUNC(opt_language)
2488 struct language *lang;
2490 lang = language_find(argv[1]);
2491 if (irccasecmp(lang->name, argv[1]))
2492 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2493 hi->language = lang;
2495 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2499 static OPTION_FUNC(opt_karma)
2502 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2507 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2508 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2509 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2510 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2512 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2516 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2521 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2522 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2524 if ((user->handle_info->opserv_level < target->opserv_level)
2525 || ((user->handle_info->opserv_level == target->opserv_level)
2526 && (user->handle_info->opserv_level < 1000))) {
2527 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2530 if ((user->handle_info->opserv_level < new_level)
2531 || ((user->handle_info->opserv_level == new_level)
2532 && (user->handle_info->opserv_level < 1000))) {
2533 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2536 if (user->handle_info == target) {
2537 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2540 if (target->opserv_level == new_level)
2542 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2543 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2544 target->opserv_level = new_level;
2548 static OPTION_FUNC(opt_level)
2553 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2557 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2558 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2562 static OPTION_FUNC(opt_epithet)
2565 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2569 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2570 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2573 if ((epithet[0] == '*') && !epithet[1])
2576 hi->epithet = strdup(epithet);
2580 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2582 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2586 static OPTION_FUNC(opt_title)
2591 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2595 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2597 if (strchr(title, '.')) {
2598 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2601 if ((strlen(user->handle_info->handle) + strlen(title) +
2602 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2603 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2608 if (!strcmp(title, "*")) {
2609 hi->fakehost = NULL;
2611 hi->fakehost = malloc(strlen(title)+2);
2612 hi->fakehost[0] = '.';
2613 strcpy(hi->fakehost+1, title);
2615 apply_fakehost(hi, NULL);
2616 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2617 title = hi->fakehost + 1;
2621 title = user_find_message(user, "MSG_NONE");
2622 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2626 static OPTION_FUNC(opt_fakehost)
2628 char mask[USERLEN + HOSTLEN + 2];
2632 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2636 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2637 safestrncpy(mask, argv[1], sizeof(mask));
2639 if ((host = strrchr(mask, '@')) && host != mask) {
2640 if(!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0))
2649 if ((strlen(host) > HOSTLEN) || (host[0] == '.')) {
2650 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2654 if (ident && strlen(ident) > USERLEN) {
2655 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2661 if (!strcmp(host, "*"))
2662 hi->fakehost = NULL;
2664 hi->fakehost = strdup(host);
2665 host = hi->fakehost;
2668 host = generate_fakehost(hi);
2671 free(hi->fakeident);
2672 if (!strcmp(ident, "*"))
2673 hi->fakeident = NULL;
2675 hi->fakeident = strdup(ident);
2676 ident = hi->fakeident;
2679 ident = generate_fakeident(hi, NULL);
2681 apply_fakehost(hi, NULL);
2684 host = generate_fakehost(hi);
2685 ident = generate_fakeident(hi, NULL);
2688 host = (char *) user_find_message(user, "MSG_NONE");
2690 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
2692 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
2696 static OPTION_FUNC(opt_fakeident)
2701 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2705 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2707 if (strlen(ident) > USERLEN) {
2708 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2711 free(hi->fakeident);
2712 if (!strcmp(ident, "*"))
2713 hi->fakeident = NULL;
2715 hi->fakeident = strdup(ident);
2716 ident = hi->fakeident;
2717 apply_fakehost(hi, NULL);
2719 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
2721 ident = user_find_message(user, "MSG_NONE");
2722 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
2726 static NICKSERV_FUNC(cmd_reclaim)
2728 struct handle_info *hi;
2729 struct nick_info *ni;
2730 struct userNode *victim;
2732 NICKSERV_MIN_PARMS(2);
2733 hi = user->handle_info;
2734 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2736 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2739 if (ni->owner != user->handle_info) {
2740 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2743 victim = GetUserH(ni->nick);
2745 reply("MSG_NICK_UNKNOWN", ni->nick);
2748 if (victim == user) {
2749 reply("NSMSG_NICK_USER_YOU");
2752 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2753 switch (nickserv_conf.reclaim_action) {
2754 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2755 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2756 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2757 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2762 static NICKSERV_FUNC(cmd_unregnick)
2765 struct handle_info *hi;
2766 struct nick_info *ni;
2768 hi = user->handle_info;
2769 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2770 ni = dict_find(nickserv_nick_dict, nick, NULL);
2772 reply("NSMSG_UNKNOWN_NICK", nick);
2775 if (hi != ni->owner) {
2776 reply("NSMSG_NOT_YOUR_NICK", nick);
2779 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2784 static NICKSERV_FUNC(cmd_ounregnick)
2786 struct nick_info *ni;
2788 NICKSERV_MIN_PARMS(2);
2789 if (!(ni = get_nick_info(argv[1]))) {
2790 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2793 if (!oper_outranks(user, ni->owner))
2795 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2800 static NICKSERV_FUNC(cmd_unregister)
2802 struct handle_info *hi;
2805 NICKSERV_MIN_PARMS(2);
2806 hi = user->handle_info;
2809 if (checkpass(passwd, hi->passwd)) {
2810 nickserv_unregister_handle(hi, user);
2813 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2814 reply("NSMSG_PASSWORD_INVALID");
2819 static NICKSERV_FUNC(cmd_ounregister)
2821 struct handle_info *hi;
2822 char reason[MAXLEN];
2825 NICKSERV_MIN_PARMS(2);
2826 if (!(hi = get_victim_oper(user, argv[1])))
2829 if (HANDLE_FLAGGED(hi, NODELETE)) {
2830 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
2834 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
2836 ((hi->flags & nickserv_conf.ounregister_flags)
2838 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
2839 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
2843 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2844 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2845 nickserv_unregister_handle(hi, user);
2849 static NICKSERV_FUNC(cmd_status)
2851 if (nickserv_conf.disable_nicks) {
2852 reply("NSMSG_GLOBAL_STATS_NONICK",
2853 dict_size(nickserv_handle_dict));
2855 if (user->handle_info) {
2857 struct nick_info *ni;
2858 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2859 reply("NSMSG_HANDLE_STATS", cnt);
2861 reply("NSMSG_HANDLE_NONE");
2863 reply("NSMSG_GLOBAL_STATS",
2864 dict_size(nickserv_handle_dict),
2865 dict_size(nickserv_nick_dict));
2870 static NICKSERV_FUNC(cmd_ghost)
2872 struct userNode *target;
2873 char reason[MAXLEN];
2875 NICKSERV_MIN_PARMS(2);
2876 if (!(target = GetUserH(argv[1]))) {
2877 reply("MSG_NICK_UNKNOWN", argv[1]);
2880 if (target == user) {
2881 reply("NSMSG_CANNOT_GHOST_SELF");
2884 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2885 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2888 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2889 DelUser(target, nickserv, 1, reason);
2890 reply("NSMSG_GHOST_KILLED", argv[1]);
2894 static NICKSERV_FUNC(cmd_vacation)
2896 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2897 reply("NSMSG_ON_VACATION");
2901 static NICKSERV_FUNC(cmd_addnote)
2903 struct handle_info *hi;
2904 unsigned long duration;
2907 struct handle_note *prev;
2908 struct handle_note *note;
2910 /* Parse parameters and figure out values for note's fields. */
2911 NICKSERV_MIN_PARMS(4);
2912 hi = get_victim_oper(user, argv[1]);
2915 if(!strcmp(argv[2], "0"))
2917 else if(!(duration = ParseInterval(argv[2])))
2919 reply("MSG_INVALID_DURATION", argv[2]);
2922 if (duration > 2*365*86400) {
2923 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2926 unsplit_string(argv + 3, argc - 3, text);
2927 WALK_NOTES(hi, prev, note) {}
2928 id = prev ? (prev->id + 1) : 1;
2930 /* Create the new note structure. */
2931 note = calloc(1, sizeof(*note) + strlen(text));
2933 note->expires = duration ? (now + duration) : 0;
2936 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2937 strcpy(note->note, text);
2942 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2946 static NICKSERV_FUNC(cmd_delnote)
2948 struct handle_info *hi;
2949 struct handle_note *prev;
2950 struct handle_note *note;
2953 NICKSERV_MIN_PARMS(3);
2954 hi = get_victim_oper(user, argv[1]);
2957 id = strtoul(argv[2], NULL, 10);
2958 WALK_NOTES(hi, prev, note) {
2959 if (id == note->id) {
2961 prev->next = note->next;
2963 hi->notes = note->next;
2965 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2969 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2974 nickserv_saxdb_write(struct saxdb_context *ctx) {
2976 struct handle_info *hi;
2979 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2981 assert(hi->id != 0);
2982 saxdb_start_record(ctx, iter_key(it), 0);
2984 struct handle_cookie *cookie = hi->cookie;
2987 switch (cookie->type) {
2988 case ACTIVATION: type = KEY_ACTIVATION; break;
2989 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2990 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2991 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2992 default: type = NULL; break;
2995 saxdb_start_record(ctx, KEY_COOKIE, 0);
2996 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2997 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2999 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3000 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3001 saxdb_end_record(ctx);
3005 struct handle_note *prev, *note;
3006 saxdb_start_record(ctx, KEY_NOTES, 0);
3007 WALK_NOTES(hi, prev, note) {
3008 snprintf(flags, sizeof(flags), "%d", note->id);
3009 saxdb_start_record(ctx, flags, 0);
3011 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3012 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3013 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3014 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3015 saxdb_end_record(ctx);
3017 saxdb_end_record(ctx);
3020 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3022 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3024 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3026 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3030 for (ii=flen=0; handle_flags[ii]; ++ii)
3031 if (hi->flags & (1 << ii))
3032 flags[flen++] = handle_flags[ii];
3034 saxdb_write_string(ctx, KEY_FLAGS, flags);
3036 saxdb_write_int(ctx, KEY_ID, hi->id);
3038 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3039 if (hi->last_quit_host[0])
3040 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3041 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3043 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3044 if (hi->masks->used)
3045 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3047 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3049 struct string_list *slist;
3050 struct nick_info *ni;
3052 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3053 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3054 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3058 if (hi->opserv_level)
3059 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3060 if (hi->language != lang_C)
3061 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3062 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3063 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3064 if (hi->screen_width)
3065 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3066 if (hi->table_width)
3067 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3068 flags[0] = hi->userlist_style;
3070 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3071 saxdb_end_record(ctx);
3076 static handle_merge_func_t *handle_merge_func_list;
3077 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3080 reg_handle_merge_func(handle_merge_func_t func)
3082 if (handle_merge_func_used == handle_merge_func_size) {
3083 if (handle_merge_func_size) {
3084 handle_merge_func_size <<= 1;
3085 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3087 handle_merge_func_size = 8;
3088 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3091 handle_merge_func_list[handle_merge_func_used++] = func;
3094 static NICKSERV_FUNC(cmd_merge)
3096 struct handle_info *hi_from, *hi_to;
3097 struct userNode *last_user;
3098 struct userData *cList, *cListNext;
3099 unsigned int ii, jj, n;
3100 char buffer[MAXLEN];
3102 NICKSERV_MIN_PARMS(3);
3104 if (!(hi_from = get_victim_oper(user, argv[1])))
3106 if (!(hi_to = get_victim_oper(user, argv[2])))
3108 if (hi_to == hi_from) {
3109 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3113 for (n=0; n<handle_merge_func_used; n++)
3114 handle_merge_func_list[n](user, hi_to, hi_from);
3116 /* Append "from" handle's nicks to "to" handle's nick list. */
3118 struct nick_info *last_ni;
3119 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3120 last_ni->next = hi_from->nicks;
3122 while (hi_from->nicks) {
3123 hi_from->nicks->owner = hi_to;
3124 hi_from->nicks = hi_from->nicks->next;
3127 /* Merge the hostmasks. */
3128 for (ii=0; ii<hi_from->masks->used; ii++) {
3129 char *mask = hi_from->masks->list[ii];
3130 for (jj=0; jj<hi_to->masks->used; jj++)
3131 if (match_ircglobs(hi_to->masks->list[jj], mask))
3133 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3134 string_list_append(hi_to->masks, strdup(mask));
3137 /* Merge the lists of authed users. */
3139 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3140 last_user->next_authed = hi_from->users;
3142 hi_to->users = hi_from->users;
3144 /* Repoint the old "from" handle's users. */
3145 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3146 last_user->handle_info = hi_to;
3148 hi_from->users = NULL;
3150 /* Merge channel userlists. */
3151 for (cList=hi_from->channels; cList; cList=cListNext) {
3152 struct userData *cList2;
3153 cListNext = cList->u_next;
3154 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3155 if (cList->channel == cList2->channel)
3157 if (cList2 && (cList2->access >= cList->access)) {
3158 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);
3159 /* keep cList2 in hi_to; remove cList from hi_from */
3160 del_channel_user(cList, 1);
3163 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);
3164 /* remove the lower-ranking cList2 from hi_to */
3165 del_channel_user(cList2, 1);
3167 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3169 /* cList needs to be moved from hi_from to hi_to */
3170 cList->handle = hi_to;
3171 /* Remove from linked list for hi_from */
3172 assert(!cList->u_prev);
3173 hi_from->channels = cList->u_next;
3175 cList->u_next->u_prev = cList->u_prev;
3176 /* Add to linked list for hi_to */
3177 cList->u_prev = NULL;
3178 cList->u_next = hi_to->channels;
3179 if (hi_to->channels)
3180 hi_to->channels->u_prev = cList;
3181 hi_to->channels = cList;
3185 /* Do they get an OpServ level promotion? */
3186 if (hi_from->opserv_level > hi_to->opserv_level)
3187 hi_to->opserv_level = hi_from->opserv_level;
3189 /* What about last seen time? */
3190 if (hi_from->lastseen > hi_to->lastseen)
3191 hi_to->lastseen = hi_from->lastseen;
3193 /* New karma is the sum of the two original karmas. */
3194 hi_to->karma += hi_from->karma;
3196 /* Does a fakehost carry over? (This intentionally doesn't set it
3197 * for users previously attached to hi_to. They'll just have to
3200 if (hi_from->fakehost && !hi_to->fakehost)
3201 hi_to->fakehost = strdup(hi_from->fakehost);
3202 if (hi_from->fakeident && !hi_to->fakeident)
3203 hi_to->fakeident = strdup(hi_from->fakeident);
3205 /* Notify of success. */
3206 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3207 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3208 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3210 /* Unregister the "from" handle. */
3211 nickserv_unregister_handle(hi_from, NULL);
3216 struct nickserv_discrim {
3217 unsigned long flags_on, flags_off;
3218 unsigned long min_registered, max_registered;
3219 unsigned long lastseen;
3221 int min_level, max_level;
3222 int min_karma, max_karma;
3223 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3224 const char *nickmask;
3225 const char *hostmask;
3226 const char *fakehostmask;
3227 const char *fakeidentmask;
3228 const char *handlemask;
3229 const char *emailmask;
3232 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3234 struct discrim_apply_info {
3235 struct nickserv_discrim *discrim;
3236 discrim_search_func func;
3237 struct userNode *source;
3238 unsigned int matched;
3241 static struct nickserv_discrim *
3242 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3245 struct nickserv_discrim *discrim;
3247 discrim = malloc(sizeof(*discrim));
3248 memset(discrim, 0, sizeof(*discrim));
3249 discrim->min_level = 0;
3250 discrim->max_level = INT_MAX;
3251 discrim->limit = 50;
3252 discrim->min_registered = 0;
3253 discrim->max_registered = ULONG_MAX;
3254 discrim->lastseen = ULONG_MAX;
3255 discrim->min_karma = INT_MIN;
3256 discrim->max_karma = INT_MAX;
3258 for (i=0; i<argc; i++) {
3259 if (i == argc - 1) {
3260 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3263 if (!irccasecmp(argv[i], "limit")) {
3264 discrim->limit = strtoul(argv[++i], NULL, 0);
3265 } else if (!irccasecmp(argv[i], "flags")) {
3266 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3267 } else if (!irccasecmp(argv[i], "registered")) {
3268 const char *cmp = argv[++i];
3269 if (cmp[0] == '<') {
3270 if (cmp[1] == '=') {
3271 discrim->min_registered = now - ParseInterval(cmp+2);
3273 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3275 } else if (cmp[0] == '=') {
3276 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3277 } else if (cmp[0] == '>') {
3278 if (cmp[1] == '=') {
3279 discrim->max_registered = now - ParseInterval(cmp+2);
3281 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3284 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3286 } else if (!irccasecmp(argv[i], "seen")) {
3287 discrim->lastseen = now - ParseInterval(argv[++i]);
3288 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3289 discrim->nickmask = argv[++i];
3290 } else if (!irccasecmp(argv[i], "hostmask")) {
3292 if (!irccasecmp(argv[i], "exact")) {
3293 if (i == argc - 1) {
3294 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3297 discrim->hostmask_type = EXACT;
3298 } else if (!irccasecmp(argv[i], "subset")) {
3299 if (i == argc - 1) {
3300 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3303 discrim->hostmask_type = SUBSET;
3304 } else if (!irccasecmp(argv[i], "superset")) {
3305 if (i == argc - 1) {
3306 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3309 discrim->hostmask_type = SUPERSET;
3310 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3311 if (i == argc - 1) {
3312 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3315 discrim->hostmask_type = LASTQUIT;
3318 discrim->hostmask_type = SUPERSET;
3320 discrim->hostmask = argv[++i];
3321 } else if (!irccasecmp(argv[i], "fakehost")) {
3322 if (!irccasecmp(argv[++i], "*")) {
3323 discrim->fakehostmask = 0;
3325 discrim->fakehostmask = argv[i];
3327 } else if (!irccasecmp(argv[i], "fakeident")) {
3328 if (!irccasecmp(argv[++i], "*")) {
3329 discrim->fakeidentmask = 0;
3331 discrim->fakeidentmask = argv[i];
3333 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3334 if (!irccasecmp(argv[++i], "*")) {
3335 discrim->handlemask = 0;
3337 discrim->handlemask = argv[i];
3339 } else if (!irccasecmp(argv[i], "email")) {
3340 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3341 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3343 } else if (!irccasecmp(argv[++i], "*")) {
3344 discrim->emailmask = 0;
3346 discrim->emailmask = argv[i];
3348 } else if (!irccasecmp(argv[i], "access")) {
3349 const char *cmp = argv[++i];
3350 if (cmp[0] == '<') {
3351 if (discrim->min_level == 0) discrim->min_level = 1;
3352 if (cmp[1] == '=') {
3353 discrim->max_level = strtoul(cmp+2, NULL, 0);
3355 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3357 } else if (cmp[0] == '=') {
3358 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3359 } else if (cmp[0] == '>') {
3360 if (cmp[1] == '=') {
3361 discrim->min_level = strtoul(cmp+2, NULL, 0);
3363 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3366 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3368 } else if (!irccasecmp(argv[i], "karma")) {
3369 const char *cmp = argv[++i];
3370 if (cmp[0] == '<') {
3371 if (cmp[1] == '=') {
3372 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3374 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3376 } else if (cmp[0] == '=') {
3377 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3378 } else if (cmp[0] == '>') {
3379 if (cmp[1] == '=') {
3380 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3382 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3385 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3388 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3399 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3401 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3402 || (discrim->flags_off & hi->flags)
3403 || (discrim->min_registered > hi->registered)
3404 || (discrim->max_registered < hi->registered)
3405 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3406 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3407 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3408 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3409 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3410 || (discrim->min_level > hi->opserv_level)
3411 || (discrim->max_level < hi->opserv_level)
3412 || (discrim->min_karma > hi->karma)
3413 || (discrim->max_karma < hi->karma)
3417 if (discrim->hostmask) {
3419 for (i=0; i<hi->masks->used; i++) {
3420 const char *mask = hi->masks->list[i];
3421 if ((discrim->hostmask_type == SUBSET)
3422 && (match_ircglobs(discrim->hostmask, mask))) break;
3423 else if ((discrim->hostmask_type == EXACT)
3424 && !irccasecmp(discrim->hostmask, mask)) break;
3425 else if ((discrim->hostmask_type == SUPERSET)
3426 && (match_ircglobs(mask, discrim->hostmask))) break;
3427 else if ((discrim->hostmask_type == LASTQUIT)
3428 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3430 if (i==hi->masks->used) return 0;
3432 if (discrim->nickmask) {
3433 struct nick_info *nick = hi->nicks;
3435 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3438 if (!nick) return 0;
3444 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3446 dict_iterator_t it, next;
3447 unsigned int matched;
3449 for (it = dict_first(nickserv_handle_dict), matched = 0;
3450 it && (matched < discrim->limit);
3452 next = iter_next(it);
3453 if (nickserv_discrim_match(discrim, iter_data(it))) {
3454 dsf(source, iter_data(it));
3462 search_print_func(struct userNode *source, struct handle_info *match)
3464 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3468 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3473 search_unregister_func (struct userNode *source, struct handle_info *match)
3475 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3476 nickserv_unregister_handle(match, source);
3480 nickserv_sort_accounts_by_access(const void *a, const void *b)
3482 const struct handle_info *hi_a = *(const struct handle_info**)a;
3483 const struct handle_info *hi_b = *(const struct handle_info**)b;
3484 if (hi_a->opserv_level != hi_b->opserv_level)
3485 return hi_b->opserv_level - hi_a->opserv_level;
3486 return irccasecmp(hi_a->handle, hi_b->handle);
3490 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3492 struct handle_info_list hil;
3493 struct helpfile_table tbl;
3498 memset(&hil, 0, sizeof(hil));
3499 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3500 struct handle_info *hi = iter_data(it);
3501 if (hi->opserv_level)
3502 handle_info_list_append(&hil, hi);
3504 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3505 tbl.length = hil.used + 1;
3507 tbl.flags = TABLE_NO_FREE;
3508 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3509 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3512 for (ii = 0; ii < hil.used; ) {
3513 ary = malloc(tbl.width * sizeof(ary[0]));
3514 ary[0] = hil.list[ii]->handle;
3515 ary[1] = strtab(hil.list[ii]->opserv_level);
3516 tbl.contents[++ii] = ary;
3518 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3519 reply("MSG_MATCH_COUNT", hil.used);
3520 for (ii = 0; ii < hil.used; ii++)
3521 free(tbl.contents[ii]);
3526 static NICKSERV_FUNC(cmd_search)
3528 struct nickserv_discrim *discrim;
3529 discrim_search_func action;
3530 struct svccmd *subcmd;
3531 unsigned int matches;
3534 NICKSERV_MIN_PARMS(3);
3535 sprintf(buf, "search %s", argv[1]);
3536 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3537 if (!irccasecmp(argv[1], "print"))
3538 action = search_print_func;
3539 else if (!irccasecmp(argv[1], "count"))
3540 action = search_count_func;
3541 else if (!irccasecmp(argv[1], "unregister"))
3542 action = search_unregister_func;
3544 reply("NSMSG_INVALID_ACTION", argv[1]);
3548 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3551 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3555 if (action == search_print_func)
3556 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3557 else if (action == search_count_func)
3558 discrim->limit = INT_MAX;
3560 matches = nickserv_discrim_search(discrim, action, user);
3563 reply("MSG_MATCH_COUNT", matches);
3565 reply("MSG_NO_MATCHES");
3571 static MODCMD_FUNC(cmd_checkpass)
3573 struct handle_info *hi;
3575 NICKSERV_MIN_PARMS(3);
3576 if (!(hi = get_handle_info(argv[1]))) {
3577 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3580 if (checkpass(argv[2], hi->passwd))
3581 reply("CHECKPASS_YES");
3583 reply("CHECKPASS_NO");
3588 static MODCMD_FUNC(cmd_checkemail)
3590 struct handle_info *hi;
3592 NICKSERV_MIN_PARMS(3);
3593 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3596 if (!hi->email_addr)
3597 reply("CHECKEMAIL_NOT_SET");
3598 else if (!irccasecmp(argv[2], hi->email_addr))
3599 reply("CHECKEMAIL_YES");
3601 reply("CHECKEMAIL_NO");
3607 nickserv_db_read_handle(const char *handle, dict_t obj)
3610 struct string_list *masks, *slist;
3611 struct handle_info *hi;
3612 struct userNode *authed_users;
3613 struct userData *channel_list;
3618 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3619 id = str ? strtoul(str, NULL, 0) : 0;
3620 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3622 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3625 if ((hi = get_handle_info(handle))) {
3626 authed_users = hi->users;
3627 channel_list = hi->channels;
3629 hi->channels = NULL;
3630 dict_remove(nickserv_handle_dict, hi->handle);
3632 authed_users = NULL;
3633 channel_list = NULL;
3635 hi = register_handle(handle, str, id);
3637 hi->users = authed_users;
3638 while (authed_users) {
3639 authed_users->handle_info = hi;
3640 authed_users = authed_users->next_authed;
3643 hi->channels = channel_list;
3644 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3645 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3646 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3647 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3648 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3649 hi->language = language_find(str ? str : "C");
3650 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3651 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3652 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3654 hi->infoline = strdup(str);
3655 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3656 hi->registered = str ? strtoul(str, NULL, 0) : now;
3657 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3658 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3659 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3660 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3661 /* We want to read the nicks even if disable_nicks is set. This is so
3662 * that we don't lose the nick data entirely. */
3663 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3665 for (ii=0; ii<slist->used; ii++)
3666 register_nick(slist->list[ii], hi);
3668 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3670 for (ii=0; str[ii]; ii++)
3671 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3673 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3674 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3675 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3676 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3677 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3678 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3679 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3681 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3683 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3684 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3686 nickserv_set_email_addr(hi, str);
3687 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3689 hi->epithet = strdup(str);
3690 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3692 hi->fakehost = strdup(str);
3693 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
3695 hi->fakeident = strdup(str);
3696 /* Read the "cookie" sub-database (if it exists). */
3697 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3699 const char *data, *type, *expires, *cookie_str;
3700 struct handle_cookie *cookie;
3702 cookie = calloc(1, sizeof(*cookie));
3703 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3704 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3705 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3706 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3707 if (!type || !expires || !cookie_str) {
3708 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3711 if (!irccasecmp(type, KEY_ACTIVATION))
3712 cookie->type = ACTIVATION;
3713 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3714 cookie->type = PASSWORD_CHANGE;
3715 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3716 cookie->type = EMAIL_CHANGE;
3717 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3718 cookie->type = ALLOWAUTH;
3720 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3723 cookie->expires = strtoul(expires, NULL, 0);
3724 if (cookie->expires < now)
3727 cookie->data = strdup(data);
3728 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3732 nickserv_bake_cookie(cookie);
3734 nickserv_free_cookie(cookie);
3736 /* Read the "notes" sub-database (if it exists). */
3737 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3740 struct handle_note *last_note;
3741 struct handle_note *note;
3744 for (it = dict_first(subdb); it; it = iter_next(it)) {
3745 const char *expires;
3749 const char *note_id;
3752 note_id = iter_key(it);
3753 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3755 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
3758 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3759 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3760 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3761 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3762 if (!setter || !text || !set) {
3763 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
3766 note = calloc(1, sizeof(*note) + strlen(text));
3768 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3769 note->set = strtoul(set, NULL, 10);
3770 note->id = strtoul(note_id, NULL, 10);
3771 safestrncpy(note->setter, setter, sizeof(note->setter));
3772 strcpy(note->note, text);
3774 last_note->next = note;
3783 nickserv_saxdb_read(dict_t db) {
3785 struct record_data *rd;
3787 for (it=dict_first(db); it; it=iter_next(it)) {
3789 nickserv_db_read_handle(iter_key(it), rd->d.object);
3794 static NICKSERV_FUNC(cmd_mergedb)
3796 struct timeval start, stop;
3799 NICKSERV_MIN_PARMS(2);
3800 gettimeofday(&start, NULL);
3801 if (!(db = parse_database(argv[1]))) {
3802 reply("NSMSG_DB_UNREADABLE", argv[1]);
3805 nickserv_saxdb_read(db);
3807 gettimeofday(&stop, NULL);
3808 stop.tv_sec -= start.tv_sec;
3809 stop.tv_usec -= start.tv_usec;
3810 if (stop.tv_usec < 0) {
3812 stop.tv_usec += 1000000;
3814 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
3819 expire_handles(UNUSED_ARG(void *data))
3821 dict_iterator_t it, next;
3822 unsigned long expiry;
3823 struct handle_info *hi;
3825 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3826 next = iter_next(it);
3828 if ((hi->opserv_level > 0)
3830 || HANDLE_FLAGGED(hi, FROZEN)
3831 || HANDLE_FLAGGED(hi, NODELETE)) {
3834 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3835 if ((now - hi->lastseen) > expiry) {
3836 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3837 nickserv_unregister_handle(hi, NULL);
3841 if (nickserv_conf.handle_expire_frequency)
3842 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3846 nickserv_load_dict(const char *fname)
3850 if (!(file = fopen(fname, "r"))) {
3851 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3854 while (fgets(line, sizeof(line), file)) {
3857 if (line[strlen(line)-1] == '\n')
3858 line[strlen(line)-1] = 0;
3859 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3862 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3865 static enum reclaim_action
3866 reclaim_action_from_string(const char *str) {
3868 return RECLAIM_NONE;
3869 else if (!irccasecmp(str, "warn"))
3870 return RECLAIM_WARN;
3871 else if (!irccasecmp(str, "svsnick"))
3872 return RECLAIM_SVSNICK;
3873 else if (!irccasecmp(str, "kill"))
3874 return RECLAIM_KILL;
3876 return RECLAIM_NONE;
3880 nickserv_conf_read(void)
3882 dict_t conf_node, child;
3886 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3887 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3890 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3892 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3893 if (nickserv_conf.valid_handle_regex_set)
3894 regfree(&nickserv_conf.valid_handle_regex);
3896 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3897 nickserv_conf.valid_handle_regex_set = !err;
3898 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3900 nickserv_conf.valid_handle_regex_set = 0;
3902 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3903 if (nickserv_conf.valid_nick_regex_set)
3904 regfree(&nickserv_conf.valid_nick_regex);
3906 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3907 nickserv_conf.valid_nick_regex_set = !err;
3908 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3910 nickserv_conf.valid_nick_regex_set = 0;
3912 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3914 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3915 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3916 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3917 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3918 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3919 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3920 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3921 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3922 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3923 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3924 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3925 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3926 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3927 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3928 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3929 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3930 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3931 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3932 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3933 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3934 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3935 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3936 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3937 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3938 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
3939 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
3940 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3942 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3943 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3944 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3946 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3947 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3948 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3950 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3951 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3952 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3953 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3954 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3955 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3956 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3957 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3958 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
3959 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
3960 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
3963 nickserv_conf.ounregister_flags = 0;
3965 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
3968 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
3970 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
3972 nickserv_conf.handle_ts_mode = TS_IGNORE;
3973 else if (!irccasecmp(str, "ircu"))
3974 nickserv_conf.handle_ts_mode = TS_IRCU;
3976 nickserv_conf.handle_ts_mode = TS_IGNORE;
3977 if (!nickserv_conf.disable_nicks) {
3978 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3979 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3980 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3981 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3982 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3983 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3984 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3985 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3987 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3988 for (it=dict_first(child); it; it=iter_next(it)) {
3989 const char *key = iter_key(it), *value;
3993 if (!strncasecmp(key, "uc_", 3))
3994 flag = toupper(key[3]);
3995 else if (!strncasecmp(key, "lc_", 3))
3996 flag = tolower(key[3]);
4000 if ((pos = handle_inverse_flags[flag])) {
4001 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4002 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4005 if (nickserv_conf.weak_password_dict)
4006 dict_delete(nickserv_conf.weak_password_dict);
4007 nickserv_conf.weak_password_dict = dict_new();
4008 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4009 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4010 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4011 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4013 nickserv_load_dict(str);
4014 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4015 if (nickserv && str)
4016 NickChange(nickserv, str, 0);
4017 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4018 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4019 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4020 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4021 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4022 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4023 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4024 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4025 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4026 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4027 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4028 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4029 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4030 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4031 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4032 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4033 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4034 nickserv_conf.titlehost_suffix = str ? str : "example.net";
4035 str = conf_get_data("server/network", RECDB_QSTRING);
4036 nickserv_conf.network_name = str ? str : "some IRC network";
4037 if (!nickserv_conf.auth_policer_params) {
4038 nickserv_conf.auth_policer_params = policer_params_new();
4039 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4040 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4042 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4043 for (it=dict_first(child); it; it=iter_next(it))
4044 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4048 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4050 char newnick[NICKLEN+1];
4059 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4061 case RECLAIM_SVSNICK:
4063 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4064 } while (GetUserH(newnick));
4065 irc_svsnick(nickserv, user, newnick);
4068 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4069 DelUser(user, nickserv, 1, msg);
4075 nickserv_reclaim_p(void *data) {
4076 struct userNode *user = data;
4077 struct nick_info *ni = get_nick_info(user->nick);
4079 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4083 check_user_nick(struct userNode *user) {
4084 struct nick_info *ni;
4085 user->modes &= ~FLAGS_REGNICK;
4086 if (!(ni = get_nick_info(user->nick)))
4088 if (user->handle_info == ni->owner) {
4089 user->modes |= FLAGS_REGNICK;
4093 if (nickserv_conf.warn_nick_owned)
4094 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4095 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4097 if (nickserv_conf.auto_reclaim_delay)
4098 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4100 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4104 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4106 struct handle_info *hi = NULL;
4109 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4110 if ((hi == NULL) && (serial != 0)) {
4112 inttobase64(id, serial, IDLEN);
4113 hi = dict_find(nickserv_id_dict, id, NULL);
4117 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4118 && (timestamp != hi->registered)) {
4121 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4124 set_user_handle_info(user, hi, 0);
4126 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4131 handle_nick_change(struct userNode *user, const char *old_nick)
4133 struct handle_info *hi;
4135 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4136 dict_remove(nickserv_allow_auth_dict, old_nick);
4137 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4139 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4140 check_user_nick(user);
4144 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4146 dict_remove(nickserv_allow_auth_dict, user->nick);
4147 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4148 set_user_handle_info(user, NULL, 0);
4151 static struct modcmd *
4152 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4154 if (min_level > 0) {
4156 sprintf(buf, "%u", min_level);
4157 if (must_be_qualified) {
4158 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4160 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4162 } else if (min_level == 0) {
4163 if (must_be_qualified) {
4164 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4166 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4169 if (must_be_qualified) {
4170 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4172 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4178 nickserv_db_cleanup(void)
4180 unreg_del_user_func(nickserv_remove_user);
4181 userList_clean(&curr_helpers);
4182 policer_params_delete(nickserv_conf.auth_policer_params);
4183 dict_delete(nickserv_handle_dict);
4184 dict_delete(nickserv_nick_dict);
4185 dict_delete(nickserv_opt_dict);
4186 dict_delete(nickserv_allow_auth_dict);
4187 dict_delete(nickserv_email_dict);
4188 dict_delete(nickserv_id_dict);
4189 dict_delete(nickserv_conf.weak_password_dict);
4190 free(auth_func_list);
4191 free(unreg_func_list);
4193 free(allowauth_func_list);
4194 free(handle_merge_func_list);
4195 free(failpw_func_list);
4196 if (nickserv_conf.valid_handle_regex_set)
4197 regfree(&nickserv_conf.valid_handle_regex);
4198 if (nickserv_conf.valid_nick_regex_set)
4199 regfree(&nickserv_conf.valid_nick_regex);
4203 init_nickserv(const char *nick)
4206 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4207 reg_new_user_func(check_user_nick);
4208 reg_nick_change_func(handle_nick_change);
4209 reg_del_user_func(nickserv_remove_user);
4210 reg_account_func(handle_account);
4212 /* set up handle_inverse_flags */
4213 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4214 for (i=0; handle_flags[i]; i++) {
4215 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4216 flag_access_levels[i] = 0;
4219 conf_register_reload(nickserv_conf_read);
4220 nickserv_opt_dict = dict_new();
4221 nickserv_email_dict = dict_new();
4222 dict_set_free_keys(nickserv_email_dict, free);
4223 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4225 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4226 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4227 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4228 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4229 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4230 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4231 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4232 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4233 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4234 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4235 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4236 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4237 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4238 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4239 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4240 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4241 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4242 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4243 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4244 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4245 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4246 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4247 if (!nickserv_conf.disable_nicks) {
4248 /* nick management commands */
4249 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4250 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4251 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4252 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4253 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4254 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4256 if (nickserv_conf.email_enabled) {
4257 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4258 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4259 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4260 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4261 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4262 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4264 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4265 /* miscellaneous commands */
4266 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4267 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4268 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4269 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4270 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4271 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4273 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4274 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4275 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4276 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4277 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4278 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4279 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4280 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4281 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4282 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4283 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4284 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4285 if (nickserv_conf.titlehost_suffix) {
4286 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4287 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4288 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4290 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4291 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4292 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4293 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4295 nickserv_handle_dict = dict_new();
4296 dict_set_free_keys(nickserv_handle_dict, free);
4297 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4299 nickserv_id_dict = dict_new();
4300 dict_set_free_keys(nickserv_id_dict, free);
4302 nickserv_nick_dict = dict_new();
4303 dict_set_free_data(nickserv_nick_dict, free);
4305 nickserv_allow_auth_dict = dict_new();
4307 userList_init(&curr_helpers);
4310 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4311 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4312 nickserv_service = service_register(nickserv);
4314 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4315 reg_exit_func(nickserv_db_cleanup);
4316 if(nickserv_conf.handle_expire_frequency)
4317 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4318 message_register_table(msgtab);