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_DEVNULL "devnull"
89 #define KEY_WEBSITE "website"
90 #define KEY_USERLIST_STYLE "user_style"
91 #define KEY_SCREEN_WIDTH "screen_width"
92 #define KEY_LAST_AUTHED_HOST "last_authed_host"
93 #define KEY_LAST_QUIT_HOST "last_quit_host"
94 #define KEY_EMAIL_ADDR "email_addr"
95 #define KEY_COOKIE "cookie"
96 #define KEY_COOKIE_DATA "data"
97 #define KEY_COOKIE_TYPE "type"
98 #define KEY_COOKIE_EXPIRES "expires"
99 #define KEY_ACTIVATION "activation"
100 #define KEY_PASSWORD_CHANGE "password change"
101 #define KEY_EMAIL_CHANGE "email change"
102 #define KEY_ALLOWAUTH "allowauth"
103 #define KEY_EPITHET "epithet"
104 #define KEY_TABLE_WIDTH "table_width"
105 #define KEY_MAXLOGINS "maxlogins"
106 #define KEY_FAKEHOST "fakehost"
107 #define KEY_FAKEIDENT "fakeident"
108 #define KEY_NOTES "notes"
109 #define KEY_NOTE_EXPIRES "expires"
110 #define KEY_NOTE_SET "set"
111 #define KEY_NOTE_SETTER "setter"
112 #define KEY_NOTE_NOTE "note"
113 #define KEY_KARMA "karma"
115 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
117 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
118 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
119 typedef OPTION_FUNC(option_func_t);
121 DEFINE_LIST(handle_info_list, struct handle_info*)
123 #define NICKSERV_MIN_PARMS(N) do { \
125 reply("MSG_MISSING_PARAMS", argv[0]); \
126 svccmd_send_help(user, nickserv, cmd); \
130 struct userNode *nickserv;
131 struct userList curr_helpers;
132 const char *handle_flags = HANDLE_FLAGS;
134 static struct module *nickserv_module;
135 static struct service *nickserv_service;
136 static struct log_type *NS_LOG;
137 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
138 static dict_t nickserv_id_dict; /* contains struct handle_info* */
139 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
140 static dict_t nickserv_opt_dict; /* contains option_func_t* */
141 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
142 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
143 static char handle_inverse_flags[256];
144 static unsigned int flag_access_levels[32];
145 static const struct message_entry msgtab[] = {
146 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
147 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
148 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
149 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
150 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
151 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
152 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
153 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
154 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
155 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
156 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
157 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
158 { "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." },
159 { "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." },
160 { "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." },
161 { "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." },
162 { "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." },
163 { "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." },
164 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
165 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
166 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
167 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
168 { "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." },
169 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
170 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
171 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
172 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
173 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
174 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
175 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
176 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
177 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
178 { "NSMSG_ATE_COOKIE_FOREIGN", "I ate the cookie for account $b%s$b. It may now have another." },
179 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
180 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
181 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
182 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
183 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
184 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
185 { "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)" },
186 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
187 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
188 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
189 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
190 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
191 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
192 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
193 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
194 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
195 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
196 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
197 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
198 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
199 { "NSMSG_TITLE_TRUNCATED_RENAME", "That account name combined with the user's title would result in a truncated host; please choose a shorter account name." },
200 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
201 { "NSMSG_FAKEIDENT_INVALID", "Fake idents must be shorter than %d characters." },
202 { "NSMSG_FAKEMASK_INVALID", "Fake ident@hosts must be shorter than %d characters." },
203 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
204 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
205 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
206 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
207 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
208 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
209 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
210 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
211 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
212 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
213 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
214 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
215 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
216 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
217 { "NSMSG_HANDLEINFO_DEVNULL", " DevNull Class: %s" },
218 { "NSMSG_HANDLEINFO_WEBSITE", " Website: %s" },
219 { "NSMSG_HANDLEINFO_ACCESS", " Access: %i" },
220 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
221 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
222 { "NSMSG_HANDLEINFO_FAKEIDENT", " Fake ident: %s" },
223 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
224 { "NSMSG_HANDLEINFO_FAKEIDENTHOST", " Fake host: %s@%s" },
225 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
226 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
227 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
228 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
229 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
230 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
231 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
232 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
233 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
234 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
235 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
236 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
237 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
238 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
239 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
240 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
241 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
242 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
243 { "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)." },
244 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
245 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
246 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
247 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
248 { "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." },
249 { "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." },
250 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
251 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
252 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
253 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
254 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
255 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
256 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
257 { "NSMSG_PASS_SUCCESS", "Password changed." },
258 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
259 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
260 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
261 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
262 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
263 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
264 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
265 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
266 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
267 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
268 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
269 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
270 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
271 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
272 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
273 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
274 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
275 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
276 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
277 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
278 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
279 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
280 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
281 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
282 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
283 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
284 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
285 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
286 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
287 { "NSMSG_NO_ACCESS", "Access denied." },
288 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
289 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
290 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
291 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
292 { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
293 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
294 { "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." },
295 { "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." },
296 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
297 { "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." },
298 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
299 { "NSMSG_SEARCH_MATCH", "Match: %s" },
300 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
301 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
302 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
303 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
304 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
305 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
306 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
307 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
308 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
309 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
310 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
311 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
312 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
313 { "NSMSG_SET_DEVNULL", "$bDEVNULL: $b%s" },
314 { "NSMSG_SET_AUTOHIDE", "$bAUTOHIDE: $b%s" },
315 { "NSMSG_SET_WEBSITE", "$bWEBSITE: $b%s" },
316 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
317 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
318 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
319 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
320 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
321 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
322 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
323 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
324 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
325 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
326 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
327 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
328 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
329 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
330 { "NSMSG_SET_FAKEIDENT", "$bFAKEIDENT: $b%s" },
331 { "NSMSG_SET_FAKEIDENTHOST", "$bFAKEHOST: $b%s@%s" },
332 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
333 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
334 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
335 { "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." },
336 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
337 { "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." },
338 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
339 { "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." },
340 { "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." },
341 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
342 { "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." },
343 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
344 { "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." },
345 { "CHECKPASS_YES", "Yes." },
346 { "CHECKPASS_NO", "No." },
347 { "CHECKEMAIL_NOT_SET", "No email set." },
348 { "CHECKEMAIL_YES", "Yes." },
349 { "CHECKEMAIL_NO", "No." },
353 enum reclaim_action {
359 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
360 static void nickserv_reclaim_p(void *data);
361 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
363 enum handle_ts_mode {
369 unsigned int disable_nicks : 1;
370 unsigned int valid_handle_regex_set : 1;
371 unsigned int valid_nick_regex_set : 1;
372 unsigned int autogag_enabled : 1;
373 unsigned int email_enabled : 1;
374 unsigned int email_required : 1;
375 unsigned int default_hostmask : 1;
376 unsigned int warn_nick_owned : 1;
377 unsigned int warn_clone_auth : 1;
378 unsigned long nicks_per_handle;
379 unsigned long password_min_length;
380 unsigned long password_min_digits;
381 unsigned long password_min_upper;
382 unsigned long password_min_lower;
383 unsigned long db_backup_frequency;
384 unsigned long handle_expire_frequency;
385 unsigned long autogag_duration;
386 unsigned long email_visible_level;
387 unsigned long cookie_timeout;
388 unsigned long handle_expire_delay;
389 unsigned long nochan_handle_expire_delay;
390 unsigned long modoper_level;
391 unsigned long set_epithet_level;
392 unsigned long set_title_level;
393 unsigned long set_fakehost_level;
394 unsigned long set_fakeident_level;
395 unsigned long handles_per_email;
396 unsigned long email_search_level;
397 const char *network_name;
398 regex_t valid_handle_regex;
399 regex_t valid_nick_regex;
400 dict_t weak_password_dict;
401 struct policer_params *auth_policer_params;
402 enum reclaim_action reclaim_action;
403 enum reclaim_action auto_reclaim_action;
404 enum handle_ts_mode handle_ts_mode;
405 unsigned long auto_reclaim_delay;
406 unsigned char default_maxlogins;
407 unsigned char hard_maxlogins;
408 unsigned long ounregister_inactive;
409 unsigned long ounregister_flags;
412 const char *titlehost_suffix = NULL;
414 /* We have 2^32 unique account IDs to use. */
415 unsigned long int highest_id = 0;
417 #define WALK_NOTES(HANDLE, PREV, NOTE) \
418 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
419 if (NOTE->expires && NOTE->expires < now) { \
420 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
422 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
426 canonicalize_hostmask(char *mask)
428 char *out = mask, *temp;
429 if ((temp = strchr(mask, '!'))) {
431 while (*temp) *out++ = *temp++;
437 static struct handle_info *
438 register_handle(const char *handle, const char *passwd, unsigned long id)
440 struct handle_info *hi;
442 char id_base64[IDLEN + 1];
445 /* Assign a unique account ID to the account; note that 0 is
446 an invalid account ID. 1 is therefore the first account ID. */
448 id = 1 + highest_id++;
450 /* Note: highest_id is and must always be the highest ID. */
451 if (id > highest_id) {
455 inttobase64(id_base64, id, IDLEN);
457 /* Make sure an account with the same ID doesn't exist. If a
458 duplicate is found, log some details and assign a new one.
459 This should be impossible, but it never hurts to expect it. */
460 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
461 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
466 hi = calloc(1, sizeof(*hi));
467 hi->userlist_style = HI_DEFAULT_STYLE;
468 hi->handle = strdup(handle);
469 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
471 dict_insert(nickserv_handle_dict, hi->handle, hi);
476 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
482 register_nick(const char *nick, struct handle_info *owner)
484 struct nick_info *ni;
485 ni = malloc(sizeof(struct nick_info));
486 safestrncpy(ni->nick, nick, sizeof(ni->nick));
488 ni->next = owner->nicks;
490 dict_insert(nickserv_nick_dict, ni->nick, ni);
494 delete_nick(struct nick_info *ni)
496 struct nick_info *last, *next;
497 struct userNode *user;
498 /* Check to see if we should mark a user as unregistered. */
499 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
500 user->modes &= ~FLAGS_REGNICK;
503 /* Remove ni from the nick_info linked list. */
504 if (ni == ni->owner->nicks) {
505 ni->owner->nicks = ni->next;
507 last = ni->owner->nicks;
513 last->next = next->next;
515 dict_remove(nickserv_nick_dict, ni->nick);
518 static unreg_func_t *unreg_func_list;
519 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
522 reg_unreg_func(unreg_func_t func)
524 if (unreg_func_used == unreg_func_size) {
525 if (unreg_func_size) {
526 unreg_func_size <<= 1;
527 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
530 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
533 unreg_func_list[unreg_func_used++] = func;
537 nickserv_free_cookie(void *data)
539 struct handle_cookie *cookie = data;
540 if (cookie->hi) cookie->hi->cookie = NULL;
541 if (cookie->data) free(cookie->data);
546 free_handle_info(void *vhi)
548 struct handle_info *hi = vhi;
551 inttobase64(id, hi->id, IDLEN);
552 dict_remove(nickserv_id_dict, id);
554 free_string_list(hi->masks);
558 delete_nick(hi->nicks);
566 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
567 nickserv_free_cookie(hi->cookie);
570 struct handle_note *note = hi->notes;
571 hi->notes = note->next;
574 if (hi->email_addr) {
575 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
576 handle_info_list_remove(hil, hi);
578 dict_remove(nickserv_email_dict, hi->email_addr);
583 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
586 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
590 for (n=0; n<unreg_func_used; n++)
591 unreg_func_list[n](notify, hi);
593 set_user_handle_info(hi->users, NULL, 0);
595 if (nickserv_conf.disable_nicks)
596 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
598 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
600 dict_remove(nickserv_handle_dict, hi->handle);
604 get_handle_info(const char *handle)
606 return dict_find(nickserv_handle_dict, handle, 0);
610 get_nick_info(const char *nick)
612 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
616 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
621 for (nn=0; nn<channel->members.used; ++nn) {
622 mn = channel->members.list[nn];
623 if ((mn->user != except) && (mn->user->handle_info == handle))
630 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
631 if (!user->handle_info) {
633 send_message(user, bot, "MSG_AUTHENTICATE");
637 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
639 send_message(user, bot, "NSMSG_NO_ACCESS");
643 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
645 send_message(user, bot, "MSG_OPER_SUSPENDED");
649 if (user->handle_info->opserv_level < min_level) {
651 send_message(user, bot, "NSMSG_NO_ACCESS");
659 is_valid_handle(const char *handle)
661 struct userNode *user;
662 /* cant register a juped nick/service nick as handle, to prevent confusion */
663 user = GetUserH(handle);
664 if (user && IsLocal(user))
666 /* check against maximum length */
667 if (strlen(handle) > NICKSERV_HANDLE_LEN)
669 /* for consistency, only allow account names that could be nicks */
670 if (!is_valid_nick(handle))
672 /* disallow account names that look like bad words */
673 if (opserv_bad_channel(handle))
675 /* test either regex or containing all valid chars */
676 if (nickserv_conf.valid_handle_regex_set) {
677 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
680 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
681 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
685 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
690 is_registerable_nick(const char *nick)
692 /* make sure it could be used as an account name */
693 if (!is_valid_handle(nick))
696 if (strlen(nick) > NICKLEN)
698 /* test either regex or as valid handle */
699 if (nickserv_conf.valid_nick_regex_set) {
700 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
703 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
704 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
712 is_valid_email_addr(const char *email)
714 return strchr(email, '@') != NULL;
718 visible_email_addr(struct userNode *user, struct handle_info *hi)
720 if (hi->email_addr) {
721 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
722 return hi->email_addr;
732 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
734 struct handle_info *hi;
735 struct userNode *target;
739 if (!(hi = get_handle_info(++name))) {
740 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
745 if (!(target = GetUserH(name))) {
746 send_message(user, service, "MSG_NICK_UNKNOWN", name);
749 if (IsLocal(target)) {
750 if (IsService(target))
751 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
753 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
756 if (!(hi = target->handle_info)) {
757 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
765 oper_outranks(struct userNode *user, struct handle_info *hi) {
766 if (user->handle_info->opserv_level > hi->opserv_level)
768 if (user->handle_info->opserv_level == hi->opserv_level) {
769 if ((user->handle_info->opserv_level == 1000)
770 || (user->handle_info == hi)
771 || ((user->handle_info->opserv_level == 0)
772 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
773 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
777 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
781 static struct handle_info *
782 get_victim_oper(struct userNode *user, const char *target)
784 struct handle_info *hi;
785 if (!(hi = smart_get_handle_info(nickserv, user, target)))
787 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
788 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
791 return oper_outranks(user, hi) ? hi : NULL;
795 valid_user_for(struct userNode *user, struct handle_info *hi)
799 /* If no hostmasks on the account, allow it. */
800 if (!hi->masks->used || IsDummy(user))
802 /* If any hostmask matches, allow it. */
803 for (ii=0; ii<hi->masks->used; ii++)
804 if (user_matches_glob(user, hi->masks->list[ii], 0))
806 /* If they are allowauthed to this account, allow it (removing the aa). */
807 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
808 dict_remove(nickserv_allow_auth_dict, user->nick);
811 /* The user is not allowed to use this account. */
816 is_secure_password(const char *handle, const char *pass, struct userNode *user)
819 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
823 if (len < nickserv_conf.password_min_length) {
825 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
828 if (!irccasecmp(pass, handle)) {
830 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
833 dict_find(nickserv_conf.weak_password_dict, pass, &p);
836 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
839 for (i=0; i<len; i++) {
840 if (isdigit(pass[i]))
842 if (isupper(pass[i]))
844 if (islower(pass[i]))
847 if ((cnt_lower < nickserv_conf.password_min_lower)
848 || (cnt_upper < nickserv_conf.password_min_upper)
849 || (cnt_digits < nickserv_conf.password_min_digits)) {
851 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
857 static auth_func_t *auth_func_list;
858 static unsigned int auth_func_size = 0, auth_func_used = 0;
861 reg_auth_func(auth_func_t func)
863 if (auth_func_used == auth_func_size) {
864 if (auth_func_size) {
865 auth_func_size <<= 1;
866 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
869 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
872 auth_func_list[auth_func_used++] = func;
875 static handle_rename_func_t *rf_list;
876 static unsigned int rf_list_size, rf_list_used;
879 reg_handle_rename_func(handle_rename_func_t func)
881 if (rf_list_used == rf_list_size) {
884 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
887 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
890 rf_list[rf_list_used++] = func;
894 generate_fakehost(struct handle_info *handle)
896 extern const char *hidden_host_suffix;
897 static char buffer[HOSTLEN+1];
899 if (!handle->fakehost) {
900 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
902 } else if (handle->fakehost[0] == '.') {
903 /* A leading dot indicates the stored value is actually a title. */
904 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, titlehost_suffix);
906 } else if (handle->fakehost[0] == '$') {
907 /* A leading $ indicates the stored value begins with the user handle. */
908 snprintf(buffer, sizeof(buffer), "%s%s", handle->handle, handle->fakehost+1);
911 return handle->fakehost;
915 generate_fakeident(struct handle_info *handle, struct userNode *user)
917 static char buffer[USERLEN+1];
919 if (!handle->fakeident) {
922 safestrncpy(buffer, user->ident, sizeof(buffer));
925 return handle->fakeident;
929 apply_fakehost(struct handle_info *handle, struct userNode *user)
931 struct userNode *target;
932 char *fakehost, *fakeident;
937 fakehost = generate_fakehost(handle);
940 fakeident = generate_fakeident(handle, user);
941 assign_fakehost(user, fakehost, fakeident, 0, 1);
945 for (target = handle->users; target; target = target->next_authed) {
946 fakeident = generate_fakeident(handle, target);
947 assign_fakehost(target, fakehost, fakeident, 0, 1);
952 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
955 struct handle_info *old_info;
957 /* This can happen if somebody uses COOKIE while authed, or if
958 * they re-auth to their current handle (which is silly, but users
960 if (user->handle_info == hi)
963 if (user->handle_info) {
964 struct userNode *other;
967 userList_remove(&curr_helpers, user);
969 /* remove from next_authed linked list */
970 if (user->handle_info->users == user) {
971 user->handle_info->users = user->next_authed;
972 } else if (user->handle_info->users != NULL) {
973 for (other = user->handle_info->users;
974 other->next_authed != user;
975 other = other->next_authed) ;
976 other->next_authed = user->next_authed;
978 /* No users authed to the account - can happen if they get
979 * killed for authing. */
981 /* if nobody left on old handle, and they're not an oper, remove !god */
982 if (!user->handle_info->users && !user->handle_info->opserv_level)
983 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
984 /* record them as being last seen at this time */
985 user->handle_info->lastseen = now;
986 /* and record their hostmask */
987 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
989 old_info = user->handle_info;
990 user->handle_info = hi;
991 if (hi && !hi->users && !hi->opserv_level)
992 HANDLE_CLEAR_FLAG(hi, HELPING);
993 for (n=0; n<auth_func_used; n++) {
994 auth_func_list[n](user, old_info);
999 struct nick_info *ni;
1001 HANDLE_CLEAR_FLAG(hi, FROZEN);
1002 if (nickserv_conf.warn_clone_auth) {
1003 struct userNode *other;
1004 for (other = hi->users; other; other = other->next_authed)
1005 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
1007 user->next_authed = hi->users;
1010 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
1011 userList_append(&curr_helpers, user);
1013 if (hi->fakehost || hi->fakeident || old_info)
1014 apply_fakehost(hi, user);
1017 if (!nickserv_conf.disable_nicks) {
1018 struct nick_info *ni2;
1019 for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
1020 if (!irccasecmp(user->nick, ni2->nick)) {
1021 user->modes |= FLAGS_REGNICK;
1026 StampUser(user, hi->handle, hi->registered, hi->id);
1029 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
1030 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
1032 /* We cannot clear the user's account ID, unfortunately. */
1033 user->next_authed = NULL;
1037 static struct handle_info*
1038 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
1040 struct handle_info *hi;
1041 struct nick_info *ni;
1042 char crypted[MD5_CRYPT_LENGTH];
1044 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
1045 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
1049 if (!is_secure_password(handle, passwd, user))
1052 cryptpass(passwd, crypted);
1053 hi = register_handle(handle, crypted, 0);
1054 hi->masks = alloc_string_list(1);
1056 hi->language = lang_C;
1057 hi->registered = now;
1059 hi->flags = HI_DEFAULT_FLAGS;
1060 if (settee && !no_auth)
1061 set_user_handle_info(settee, hi, 1);
1064 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1065 else if (nickserv_conf.disable_nicks)
1066 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1067 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1068 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1070 register_nick(user->nick, hi);
1071 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1073 if (settee && (user != settee))
1074 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1079 nickserv_bake_cookie(struct handle_cookie *cookie)
1081 cookie->hi->cookie = cookie;
1082 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1086 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1088 struct handle_cookie *cookie;
1089 char subject[128], body[4096], *misc;
1090 const char *netname, *fmt;
1094 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1098 cookie = calloc(1, sizeof(*cookie));
1100 cookie->type = type;
1101 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1102 cookie->expires = now + nickserv_conf.cookie_timeout;
1103 inttobase64(cookie->cookie, rand(), 5);
1104 inttobase64(cookie->cookie+5, rand(), 5);
1106 netname = nickserv_conf.network_name;
1109 switch (cookie->type) {
1111 hi->passwd[0] = 0; /* invalidate password */
1112 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1113 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1114 snprintf(subject, sizeof(subject), fmt, netname);
1115 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1116 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1119 case PASSWORD_CHANGE:
1120 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1121 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1122 snprintf(subject, sizeof(subject), fmt, netname);
1123 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1124 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1127 misc = hi->email_addr;
1128 hi->email_addr = cookie->data;
1130 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1131 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1132 snprintf(subject, sizeof(subject), fmt, netname);
1133 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1134 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1135 mail_send(nickserv, hi, subject, body, 1);
1136 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1137 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1139 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1140 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1141 snprintf(subject, sizeof(subject), fmt, netname);
1142 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1143 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1144 mail_send(nickserv, hi, subject, body, 1);
1147 hi->email_addr = misc;
1150 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1151 snprintf(subject, sizeof(subject), fmt, netname);
1152 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1153 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1154 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1157 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1161 mail_send(nickserv, hi, subject, body, first_time);
1162 nickserv_bake_cookie(cookie);
1166 nickserv_eat_cookie(struct handle_cookie *cookie)
1168 cookie->hi->cookie = NULL;
1169 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1170 nickserv_free_cookie(cookie);
1174 nickserv_free_email_addr(void *data)
1176 handle_info_list_clean(data);
1181 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1183 struct handle_info_list *hil;
1184 /* Remove from old handle_info_list ... */
1185 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1186 handle_info_list_remove(hil, hi);
1187 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1188 hi->email_addr = NULL;
1190 /* Add to the new list.. */
1191 if (new_email_addr) {
1192 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1193 hil = calloc(1, sizeof(*hil));
1194 hil->tag = strdup(new_email_addr);
1195 handle_info_list_init(hil);
1196 dict_insert(nickserv_email_dict, hil->tag, hil);
1198 handle_info_list_append(hil, hi);
1199 hi->email_addr = hil->tag;
1203 static NICKSERV_FUNC(cmd_register)
1206 struct handle_info *hi;
1207 const char *email_addr, *password;
1210 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1211 /* Require the first handle registered to belong to someone +o. */
1212 reply("NSMSG_REQUIRE_OPER");
1216 if (user->handle_info) {
1217 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1221 if (IsRegistering(user)) {
1222 reply("NSMSG_ALREADY_REGISTERING");
1226 if (IsStamped(user)) {
1227 /* Unauthenticated users might still have been stamped
1228 previously and could therefore have a hidden host;
1229 do not allow them to register a new account. */
1230 reply("NSMSG_STAMPED_REGISTER");
1234 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1236 if (!is_valid_handle(argv[1])) {
1237 reply("NSMSG_BAD_HANDLE", argv[1]);
1241 if ((argc >= 4) && nickserv_conf.email_enabled) {
1242 struct handle_info_list *hil;
1245 /* Remember email address. */
1246 email_addr = argv[3];
1248 /* Check that the email address looks valid.. */
1249 if (!is_valid_email_addr(email_addr)) {
1250 reply("NSMSG_BAD_EMAIL_ADDR");
1254 /* .. and that we are allowed to send to it. */
1255 if ((str = mail_prohibited_address(email_addr))) {
1256 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1260 /* If we do email verify, make sure we don't spam the address. */
1261 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1263 for (nn=0; nn<hil->used; nn++) {
1264 if (hil->list[nn]->cookie) {
1265 reply("NSMSG_EMAIL_UNACTIVATED");
1269 if (hil->used >= nickserv_conf.handles_per_email) {
1270 reply("NSMSG_EMAIL_OVERUSED");
1283 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1285 /* Add any masks they should get. */
1286 if (nickserv_conf.default_hostmask) {
1287 string_list_append(hi->masks, strdup("*@*"));
1289 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1290 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1291 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1294 /* If they're the first to register, give them level 1000. */
1295 if (dict_size(nickserv_handle_dict) == 1) {
1296 hi->opserv_level = 1000;
1297 reply("NSMSG_ROOT_HANDLE", argv[1]);
1300 /* Set their email address. */
1302 nickserv_set_email_addr(hi, email_addr);
1304 /* If they need to do email verification, tell them. */
1306 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1308 /* Set registering flag.. */
1309 user->modes |= FLAGS_REGISTERING;
1314 static NICKSERV_FUNC(cmd_oregister)
1317 struct userNode *settee;
1318 struct handle_info *hi;
1319 const char *pass, *email;
1321 NICKSERV_MIN_PARMS(3);
1326 if (!is_valid_handle(argv[1])) {
1327 reply("NSMSG_BAD_HANDLE", argv[1]);
1331 if (argc < 5 || !nickserv_conf.email_enabled) {
1336 if (!is_valid_email_addr(email)) {
1337 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
1340 if ((str = mail_prohibited_address(email))) {
1341 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", email, str);
1346 if (argc < 4 || !strcmp(argv[3], "*")) {
1349 } else if (strchr(argv[3], '@')) {
1350 mask = canonicalize_hostmask(strdup(argv[3]));
1352 settee = GetUserH(argv[4]);
1354 reply("MSG_NICK_UNKNOWN", argv[4]);
1361 } else if ((settee = GetUserH(argv[3]))) {
1362 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1364 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1367 if (settee && settee->handle_info) {
1368 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1372 if (!(hi = nickserv_register(user, settee, argv[1], pass, 0))) {
1377 string_list_append(hi->masks, mask);
1379 nickserv_set_email_addr(hi, email);
1383 static NICKSERV_FUNC(cmd_handleinfo)
1386 unsigned int i, pos=0, herelen;
1387 struct userNode *target, *next_un;
1388 struct handle_info *hi;
1389 const char *nsmsg_none;
1393 if (!(hi = user->handle_info)) {
1394 reply("NSMSG_MUST_AUTH");
1397 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1401 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1402 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1403 feh = hi->registered;
1404 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1407 intervalString(buff, now - hi->lastseen, user->handle_info);
1408 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1410 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1413 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1414 if ((oper_has_access(user, cmd->parent->bot, 200, 1)) || IsNetworkHelper(user))
1415 reply("NSMSG_HANDLEINFO_DEVNULL", (hi->devnull ? hi->devnull : nsmsg_none));
1416 if (user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1417 reply("NSMSG_HANDLEINFO_WEBSITE", (hi->website ? hi->website : nsmsg_none));
1418 if(hi->opserv_level > 0 && user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1419 reply("NSMSG_HANDLEINFO_ACCESS", hi->opserv_level);
1420 if (HANDLE_FLAGGED(hi, FROZEN))
1421 reply("NSMSG_HANDLEINFO_VACATION");
1423 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1424 struct do_not_register *dnr;
1425 if ((dnr = chanserv_is_dnr(NULL, hi)))
1426 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1427 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1429 } else if (hi != user->handle_info)
1433 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1435 if (nickserv_conf.email_enabled)
1436 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1440 switch (hi->cookie->type) {
1441 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1442 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1443 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1444 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1445 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1450 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1451 reply("NSMSG_HANDLEINFO_ID", hi->id);
1453 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1455 reply("NSMSG_HANDLEINFO_NO_NOTES");
1457 struct handle_note *prev, *note;
1459 WALK_NOTES(hi, prev, note) {
1460 char set_time[INTERVALLEN];
1461 intervalString(set_time, now - note->set, user->handle_info);
1462 if (note->expires) {
1463 char exp_time[INTERVALLEN];
1464 intervalString(exp_time, note->expires - now, user->handle_info);
1465 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1467 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1474 unsigned long flen = 1;
1475 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1477 for (i=0, flen=1; handle_flags[i]; i++)
1478 if (hi->flags & 1 << i)
1479 flags[flen++] = handle_flags[i];
1481 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1483 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1486 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1487 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1488 || (hi->opserv_level > 0)) {
1489 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1492 if (hi->fakeident && hi->fakehost)
1493 reply("NSMSG_HANDLEINFO_FAKEIDENTHOST", hi->fakeident, hi->fakehost);
1494 else if (hi->fakeident)
1495 reply("NSMSG_HANDLEINFO_FAKEIDENT", hi->fakeident);
1496 else if (hi->fakehost)
1497 reply("NSMSG_HANDLEINFO_FAKEHOST", hi->fakehost);
1499 if (hi->last_quit_host[0])
1500 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1502 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1504 if (nickserv_conf.disable_nicks) {
1505 /* nicks disabled; don't show anything about registered nicks */
1506 } else if (hi->nicks) {
1507 struct nick_info *ni, *next_ni;
1508 for (ni = hi->nicks; ni; ni = next_ni) {
1509 herelen = strlen(ni->nick);
1510 if (pos + herelen + 1 > ArrayLength(buff)) {
1512 goto print_nicks_buff;
1516 memcpy(buff+pos, ni->nick, herelen);
1517 pos += herelen; buff[pos++] = ' ';
1521 reply("NSMSG_HANDLEINFO_NICKS", buff);
1526 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1529 if (hi->masks->used) {
1530 for (i=0; i < hi->masks->used; i++) {
1531 herelen = strlen(hi->masks->list[i]);
1532 if (pos + herelen + 1 > ArrayLength(buff)) {
1534 goto print_mask_buff;
1536 memcpy(buff+pos, hi->masks->list[i], herelen);
1537 pos += herelen; buff[pos++] = ' ';
1538 if (i+1 == hi->masks->used) {
1541 reply("NSMSG_HANDLEINFO_MASKS", buff);
1546 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1550 struct userData *chan, *next;
1553 for (chan = hi->channels; chan; chan = next) {
1554 next = chan->u_next;
1555 name = chan->channel->channel->name;
1556 herelen = strlen(name);
1557 if (pos + herelen + 7 > ArrayLength(buff)) {
1559 goto print_chans_buff;
1561 if (IsUserSuspended(chan))
1563 pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
1567 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1572 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1575 for (target = hi->users; target; target = next_un) {
1576 herelen = strlen(target->nick);
1577 if (pos + herelen + 1 > ArrayLength(buff)) {
1579 goto print_cnick_buff;
1581 next_un = target->next_authed;
1583 memcpy(buff+pos, target->nick, herelen);
1584 pos += herelen; buff[pos++] = ' ';
1588 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1593 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1596 static NICKSERV_FUNC(cmd_userinfo)
1598 struct userNode *target;
1600 NICKSERV_MIN_PARMS(2);
1601 if (!(target = GetUserH(argv[1]))) {
1602 reply("MSG_NICK_UNKNOWN", argv[1]);
1605 if (target->handle_info)
1606 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1608 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1612 static NICKSERV_FUNC(cmd_nickinfo)
1614 struct nick_info *ni;
1616 NICKSERV_MIN_PARMS(2);
1617 if (!(ni = get_nick_info(argv[1]))) {
1618 reply("MSG_NICK_UNKNOWN", argv[1]);
1621 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1625 static NICKSERV_FUNC(cmd_notes)
1627 struct handle_info *hi;
1628 struct handle_note *prev, *note;
1631 NICKSERV_MIN_PARMS(2);
1632 if (!(hi = get_victim_oper(user, argv[1])))
1635 WALK_NOTES(hi, prev, note) {
1636 char set_time[INTERVALLEN];
1637 intervalString(set_time, now - note->set, user->handle_info);
1638 if (note->expires) {
1639 char exp_time[INTERVALLEN];
1640 intervalString(exp_time, note->expires - now, user->handle_info);
1641 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1643 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1647 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1651 static NICKSERV_FUNC(cmd_rename_handle)
1653 struct handle_info *hi;
1654 char msgbuf[MAXLEN], *old_handle;
1657 NICKSERV_MIN_PARMS(3);
1658 if (!(hi = get_victim_oper(user, argv[1])))
1660 if (!is_valid_handle(argv[2])) {
1661 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1664 if (get_handle_info(argv[2])) {
1665 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1668 if (hi->fakehost && hi->fakehost[0] == '.' &&
1669 (strlen(argv[2]) + strlen(hi->fakehost+1) +
1670 strlen(titlehost_suffix) + 2) > HOSTLEN) {
1671 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED_RENAME");
1675 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1676 hi->handle = strdup(argv[2]);
1677 dict_insert(nickserv_handle_dict, hi->handle, hi);
1678 for (nn=0; nn<rf_list_used; nn++)
1679 rf_list[nn](hi, old_handle);
1680 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1681 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1682 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1684 apply_fakehost(hi, NULL);
1688 static failpw_func_t *failpw_func_list;
1689 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1692 reg_failpw_func(failpw_func_t func)
1694 if (failpw_func_used == failpw_func_size) {
1695 if (failpw_func_size) {
1696 failpw_func_size <<= 1;
1697 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1699 failpw_func_size = 8;
1700 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1703 failpw_func_list[failpw_func_used++] = func;
1706 static NICKSERV_FUNC(cmd_auth)
1708 int pw_arg, used, maxlogins;
1709 struct handle_info *hi;
1711 struct userNode *other;
1713 if (user->handle_info) {
1714 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1717 if (IsStamped(user)) {
1718 /* Unauthenticated users might still have been stamped
1719 previously and could therefore have a hidden host;
1720 do not allow them to authenticate. */
1721 reply("NSMSG_STAMPED_AUTH");
1725 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1727 } else if (argc == 2) {
1728 if (nickserv_conf.disable_nicks) {
1729 if (!(hi = get_handle_info(user->nick))) {
1730 reply("NSMSG_HANDLE_NOT_FOUND");
1734 /* try to look up their handle from their nick */
1735 struct nick_info *ni;
1736 ni = get_nick_info(user->nick);
1738 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1745 reply("MSG_MISSING_PARAMS", argv[0]);
1746 svccmd_send_help(user, nickserv, cmd);
1750 reply("NSMSG_HANDLE_NOT_FOUND");
1753 /* Responses from here on look up the language used by the handle they asked about. */
1754 passwd = argv[pw_arg];
1755 if (!valid_user_for(user, hi)) {
1756 if (hi->email_addr && nickserv_conf.email_enabled)
1757 send_message_type(4, user, cmd->parent->bot,
1758 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1761 send_message_type(4, user, cmd->parent->bot,
1762 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1764 argv[pw_arg] = "BADMASK";
1767 if (!checkpass(passwd, hi->passwd)) {
1769 send_message_type(4, user, cmd->parent->bot,
1770 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1771 argv[pw_arg] = "BADPASS";
1772 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1773 if (nickserv_conf.autogag_enabled) {
1774 if (!user->auth_policer.params) {
1775 user->auth_policer.last_req = now;
1776 user->auth_policer.params = nickserv_conf.auth_policer_params;
1778 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1780 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1781 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1782 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1784 argv[pw_arg] = "GAGGED";
1789 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1790 send_message_type(4, user, cmd->parent->bot,
1791 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1792 argv[pw_arg] = "SUSPENDED";
1795 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1796 for (used = 0, other = hi->users; other; other = other->next_authed) {
1797 if (++used >= maxlogins) {
1798 send_message_type(4, user, cmd->parent->bot,
1799 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1801 argv[pw_arg] = "MAXLOGINS";
1805 if (HANDLE_FLAGGED(hi, AUTOHIDE)) {
1806 //ok we have a fakehost set... but we need to set mode +x
1807 irc_svsmode(nickserv,user,"+x");
1810 set_user_handle_info(user, hi, 1);
1811 if (nickserv_conf.email_required && !hi->email_addr)
1812 reply("NSMSG_PLEASE_SET_EMAIL");
1813 if (!is_secure_password(hi->handle, passwd, NULL))
1814 reply("NSMSG_WEAK_PASSWORD");
1815 if (hi->passwd[0] != '$')
1816 cryptpass(passwd, hi->passwd);
1817 if (!hi->masks->used) {
1819 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1820 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1821 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1823 argv[pw_arg] = "****";
1824 reply("NSMSG_AUTH_SUCCESS");
1828 struct handle_info *checklogin(const char *user, const char *pass, const char *numeric, const char *hostmask, const char *ipmask)
1830 struct handle_info *hi;
1831 unsigned int match = 0, ii = 0;
1832 hi = dict_find(nickserv_handle_dict, user, NULL);
1835 /* If no hostmasks on the account, allow it. */
1836 if (hi->masks->used) {
1837 /* If any hostmask matches, allow it. */
1838 for (ii=0; ii<hi->masks->used; ii++)
1839 if (match_ircglob(hostmask, hi->masks->list[ii]) || match_ircglob(ipmask, hi->masks->list[ii])) {
1846 if(!checkpass(pass, hi->passwd))
1848 if (HANDLE_FLAGGED(hi, SUSPENDED))
1850 /** following in one of the next commits
1851 struct last_login *login,*clogin,*old;
1852 unsigned int ii = 0;
1853 login = calloc(1, sizeof(*login));
1854 login->last_login = hi->last_login;
1855 login->hostmask = strdup(hostmask);
1856 login->authtime = now;
1857 login->quittime = 0;
1860 login->loc_pending = strdup(numeric);
1861 for (clogin = hi->last_login; clogin != NULL && ii < 9; clogin = clogin->last_login) {
1862 if(ii == 8 && clogin->last_login) {
1863 old = clogin->last_login;
1864 clogin->last_login = NULL;
1865 free(old->hostmask);
1868 if(old->loc_pending)
1869 free(old->loc_pending);
1873 hi->last_login = login;
1878 char *getfakehost(const char *user)
1880 struct handle_info *hi;
1881 hi = dict_find(nickserv_handle_dict, user, NULL);
1884 return generate_fakehost(hi);
1887 static allowauth_func_t *allowauth_func_list;
1888 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1891 reg_allowauth_func(allowauth_func_t func)
1893 if (allowauth_func_used == allowauth_func_size) {
1894 if (allowauth_func_size) {
1895 allowauth_func_size <<= 1;
1896 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1898 allowauth_func_size = 8;
1899 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1902 allowauth_func_list[allowauth_func_used++] = func;
1905 static NICKSERV_FUNC(cmd_allowauth)
1907 struct userNode *target;
1908 struct handle_info *hi;
1911 NICKSERV_MIN_PARMS(2);
1912 if (!(target = GetUserH(argv[1]))) {
1913 reply("MSG_NICK_UNKNOWN", argv[1]);
1916 if (target->handle_info) {
1917 reply("NSMSG_USER_PREV_AUTH", target->nick);
1920 if (IsStamped(target)) {
1921 /* Unauthenticated users might still have been stamped
1922 previously and could therefore have a hidden host;
1923 do not allow them to authenticate to an account. */
1924 reply("NSMSG_USER_PREV_STAMP", target->nick);
1929 else if (!(hi = get_handle_info(argv[2]))) {
1930 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1934 if (hi->opserv_level > user->handle_info->opserv_level) {
1935 reply("MSG_USER_OUTRANKED", hi->handle);
1938 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1939 || (hi->opserv_level > 0))
1940 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1941 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1944 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1945 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1946 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1947 if (nickserv_conf.email_enabled)
1948 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1950 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1951 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1953 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1955 for (n=0; n<allowauth_func_used; n++)
1956 allowauth_func_list[n](user, target, hi);
1960 static NICKSERV_FUNC(cmd_authcookie)
1962 struct handle_info *hi;
1964 NICKSERV_MIN_PARMS(2);
1965 if (user->handle_info) {
1966 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1969 if (IsStamped(user)) {
1970 /* Unauthenticated users might still have been stamped
1971 previously and could therefore have a hidden host;
1972 do not allow them to authenticate to an account. */
1973 reply("NSMSG_STAMPED_AUTHCOOKIE");
1976 if (!(hi = get_handle_info(argv[1]))) {
1977 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1980 if (!hi->email_addr) {
1981 reply("MSG_SET_EMAIL_ADDR");
1984 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1988 static NICKSERV_FUNC(cmd_delcookie)
1990 struct handle_info *hi;
1992 hi = user->handle_info;
1994 reply("NSMSG_NO_COOKIE");
1997 switch (hi->cookie->type) {
2000 reply("NSMSG_MUST_TIME_OUT");
2003 nickserv_eat_cookie(hi->cookie);
2004 reply("NSMSG_ATE_COOKIE");
2010 static NICKSERV_FUNC(cmd_odelcookie)
2012 struct handle_info *hi;
2014 NICKSERV_MIN_PARMS(2);
2016 if (!(hi = get_victim_oper(user, argv[1])))
2020 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
2024 nickserv_eat_cookie(hi->cookie);
2025 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
2030 static NICKSERV_FUNC(cmd_resetpass)
2032 struct handle_info *hi;
2033 char crypted[MD5_CRYPT_LENGTH];
2035 NICKSERV_MIN_PARMS(3);
2036 if (user->handle_info) {
2037 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2040 if (IsStamped(user)) {
2041 /* Unauthenticated users might still have been stamped
2042 previously and could therefore have a hidden host;
2043 do not allow them to activate an account. */
2044 reply("NSMSG_STAMPED_RESETPASS");
2047 if (!(hi = get_handle_info(argv[1]))) {
2048 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2051 if (!hi->email_addr) {
2052 reply("MSG_SET_EMAIL_ADDR");
2055 cryptpass(argv[2], crypted);
2057 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
2061 static NICKSERV_FUNC(cmd_cookie)
2063 struct handle_info *hi;
2066 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
2069 NICKSERV_MIN_PARMS(3);
2070 if (!(hi = get_handle_info(argv[1]))) {
2071 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2077 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2078 reply("NSMSG_HANDLE_SUSPENDED");
2083 reply("NSMSG_NO_COOKIE");
2087 /* Check validity of operation before comparing cookie to
2088 * prohibit guessing by authed users. */
2089 if (user->handle_info
2090 && (hi->cookie->type != EMAIL_CHANGE)
2091 && (hi->cookie->type != PASSWORD_CHANGE)) {
2092 reply("NSMSG_CANNOT_COOKIE");
2096 if (strcmp(cookie, hi->cookie->cookie)) {
2097 reply("NSMSG_BAD_COOKIE");
2101 switch (hi->cookie->type) {
2103 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2104 set_user_handle_info(user, hi, 1);
2105 reply("NSMSG_HANDLE_ACTIVATED");
2107 case PASSWORD_CHANGE:
2108 set_user_handle_info(user, hi, 1);
2109 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2110 reply("NSMSG_PASSWORD_CHANGED");
2113 nickserv_set_email_addr(hi, hi->cookie->data);
2114 reply("NSMSG_EMAIL_CHANGED");
2117 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2118 set_user_handle_info(user, hi, 1);
2119 nickserv_addmask(user, hi, mask);
2120 reply("NSMSG_AUTH_SUCCESS");
2125 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2126 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2130 nickserv_eat_cookie(hi->cookie);
2135 static NICKSERV_FUNC(cmd_oregnick) {
2137 struct handle_info *target;
2138 struct nick_info *ni;
2140 NICKSERV_MIN_PARMS(3);
2141 if (!(target = modcmd_get_handle_info(user, argv[1])))
2144 if (!is_registerable_nick(nick)) {
2145 reply("NSMSG_BAD_NICK", nick);
2148 ni = dict_find(nickserv_nick_dict, nick, NULL);
2150 reply("NSMSG_NICK_EXISTS", nick);
2153 register_nick(nick, target);
2154 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2158 static NICKSERV_FUNC(cmd_regnick) {
2160 struct nick_info *ni;
2162 if (!is_registerable_nick(user->nick)) {
2163 reply("NSMSG_BAD_NICK", user->nick);
2166 /* count their nicks, see if it's too many */
2167 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2168 if (n >= nickserv_conf.nicks_per_handle) {
2169 reply("NSMSG_TOO_MANY_NICKS");
2172 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2174 reply("NSMSG_NICK_EXISTS", user->nick);
2177 register_nick(user->nick, user->handle_info);
2178 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2182 static NICKSERV_FUNC(cmd_pass)
2184 struct handle_info *hi;
2185 const char *old_pass, *new_pass;
2187 NICKSERV_MIN_PARMS(3);
2188 hi = user->handle_info;
2192 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2193 if (!checkpass(old_pass, hi->passwd)) {
2194 argv[1] = "BADPASS";
2195 reply("NSMSG_PASSWORD_INVALID");
2198 cryptpass(new_pass, hi->passwd);
2200 reply("NSMSG_PASS_SUCCESS");
2205 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2208 char *new_mask = canonicalize_hostmask(strdup(mask));
2209 for (i=0; i<hi->masks->used; i++) {
2210 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2211 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2216 string_list_append(hi->masks, new_mask);
2217 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2221 static NICKSERV_FUNC(cmd_addmask)
2224 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2225 int res = nickserv_addmask(user, user->handle_info, mask);
2229 if (!is_gline(argv[1])) {
2230 reply("NSMSG_MASK_INVALID", argv[1]);
2233 return nickserv_addmask(user, user->handle_info, argv[1]);
2237 static NICKSERV_FUNC(cmd_oaddmask)
2239 struct handle_info *hi;
2241 NICKSERV_MIN_PARMS(3);
2242 if (!(hi = get_victim_oper(user, argv[1])))
2244 return nickserv_addmask(user, hi, argv[2]);
2248 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2251 for (i=0; i<hi->masks->used; i++) {
2252 if (!strcmp(del_mask, hi->masks->list[i])) {
2253 char *old_mask = hi->masks->list[i];
2254 if (hi->masks->used == 1 && !force) {
2255 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2258 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2259 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2264 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2268 static NICKSERV_FUNC(cmd_delmask)
2270 NICKSERV_MIN_PARMS(2);
2271 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2274 static NICKSERV_FUNC(cmd_odelmask)
2276 struct handle_info *hi;
2277 NICKSERV_MIN_PARMS(3);
2278 if (!(hi = get_victim_oper(user, argv[1])))
2280 return nickserv_delmask(user, hi, argv[2], 1);
2284 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2285 unsigned int nn, add = 1, pos;
2286 unsigned long added, removed, flag;
2288 for (added=removed=nn=0; str[nn]; nn++) {
2290 case '+': add = 1; break;
2291 case '-': add = 0; break;
2293 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2294 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2297 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2298 /* cheesy avoidance of looking up the flag name.. */
2299 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2302 flag = 1 << (pos - 1);
2304 added |= flag, removed &= ~flag;
2306 removed |= flag, added &= ~flag;
2311 *premoved = removed;
2316 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2318 unsigned long before, after, added, removed;
2319 struct userNode *uNode;
2321 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2322 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2324 hi->flags = (hi->flags | added) & ~removed;
2325 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2327 /* Strip helping flag if they're only a support helper and not
2328 * currently in #support. */
2329 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2330 struct channelList *schannels;
2332 schannels = chanserv_support_channels();
2333 for (ii = 0; ii < schannels->used; ++ii)
2334 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2336 if (ii == schannels->used)
2337 HANDLE_CLEAR_FLAG(hi, HELPING);
2340 if (after && !before) {
2341 /* Add user to current helper list. */
2342 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2343 userList_append(&curr_helpers, uNode);
2344 } else if (!after && before) {
2345 /* Remove user from current helper list. */
2346 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2347 userList_remove(&curr_helpers, uNode);
2354 set_list(struct userNode *user, struct handle_info *hi, int override)
2358 char *set_display[] = {
2359 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2360 "EMAIL", "MAXLOGINS", "LANGUAGE", "DEVNULL"
2363 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2365 /* Do this so options are presented in a consistent order. */
2366 for (i = 0; i < ArrayLength(set_display); ++i)
2367 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2368 opt(user, hi, override, 0, NULL);
2371 static NICKSERV_FUNC(cmd_set)
2373 struct handle_info *hi;
2376 hi = user->handle_info;
2378 set_list(user, hi, 0);
2381 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2382 reply("NSMSG_INVALID_OPTION", argv[1]);
2385 return opt(user, hi, 0, argc-1, argv+1);
2388 static NICKSERV_FUNC(cmd_oset)
2390 struct handle_info *hi;
2391 struct svccmd *subcmd;
2393 char cmdname[MAXLEN];
2395 NICKSERV_MIN_PARMS(2);
2397 if (!(hi = get_victim_oper(user, argv[1])))
2401 set_list(user, hi, 0);
2405 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2406 reply("NSMSG_INVALID_OPTION", argv[2]);
2410 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2411 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2412 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2415 return opt(user, hi, 1, argc-2, argv+2);
2418 static OPTION_FUNC(opt_info)
2422 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2424 hi->infoline = NULL;
2426 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2430 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2431 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2435 static OPTION_FUNC(opt_devnull)
2437 const char *devnull;
2441 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2444 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2448 devnull = unsplit_string(argv+1, argc-1, NULL);
2449 if(devnull_check(devnull) == 1) {
2452 hi->devnull = strdup(devnull);
2457 devnull = hi->devnull ? hi->devnull : user_find_message(user, "MSG_NONE");
2458 send_message(user, nickserv, "NSMSG_SET_DEVNULL", devnull);
2462 void nickserv_devnull_delete(char *name) {
2464 struct handle_info *hi;
2466 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2468 if (hi->devnull && !irccasecmp(name, hi->devnull)) {
2475 void nickserv_devnull_rename(char *oldname, char *newname) {
2477 struct handle_info *hi;
2479 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2481 if (hi->devnull && !irccasecmp(oldname, hi->devnull)) {
2482 hi->devnull = strdup(newname);
2487 static OPTION_FUNC(opt_website)
2489 const char *website;
2492 if (!HANDLE_FLAGGED(user->handle_info, BOT)) {
2493 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2496 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2500 website = unsplit_string(argv+1, argc-1, NULL);
2501 hi->website = strdup(website);
2504 if (HANDLE_FLAGGED(user->handle_info, BOT)) {
2505 website = hi->website ? hi->website : user_find_message(user, "MSG_NONE");
2506 send_message(user, nickserv, "NSMSG_SET_WEBSITE", website);
2511 static OPTION_FUNC(opt_width)
2514 hi->screen_width = strtoul(argv[1], NULL, 0);
2516 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2517 hi->screen_width = MIN_LINE_SIZE;
2518 else if (hi->screen_width > MAX_LINE_SIZE)
2519 hi->screen_width = MAX_LINE_SIZE;
2521 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2525 static OPTION_FUNC(opt_tablewidth)
2528 hi->table_width = strtoul(argv[1], NULL, 0);
2530 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2531 hi->table_width = MIN_LINE_SIZE;
2532 else if (hi->screen_width > MAX_LINE_SIZE)
2533 hi->table_width = MAX_LINE_SIZE;
2535 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2539 static OPTION_FUNC(opt_color)
2542 if (enabled_string(argv[1]))
2543 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2544 else if (disabled_string(argv[1]))
2545 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2547 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2552 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2556 static OPTION_FUNC(opt_privmsg)
2559 if (enabled_string(argv[1]))
2560 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2561 else if (disabled_string(argv[1]))
2562 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2564 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2569 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2573 static OPTION_FUNC(opt_autohide)
2576 if (enabled_string(argv[1]))
2577 HANDLE_SET_FLAG(hi, AUTOHIDE);
2578 else if (disabled_string(argv[1]))
2579 HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
2581 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2586 send_message(user, nickserv, "NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
2590 static OPTION_FUNC(opt_style)
2595 if (!irccasecmp(argv[1], "Zoot"))
2596 hi->userlist_style = HI_STYLE_ZOOT;
2597 else if (!irccasecmp(argv[1], "def"))
2598 hi->userlist_style = HI_STYLE_DEF;
2601 switch (hi->userlist_style) {
2610 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2614 static OPTION_FUNC(opt_password)
2617 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2622 cryptpass(argv[1], hi->passwd);
2624 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2628 static OPTION_FUNC(opt_flags)
2631 unsigned int ii, flen;
2634 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2639 nickserv_apply_flags(user, hi, argv[1]);
2641 for (ii = flen = 0; handle_flags[ii]; ii++)
2642 if (hi->flags & (1 << ii))
2643 flags[flen++] = handle_flags[ii];
2646 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2648 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2652 static OPTION_FUNC(opt_email)
2656 if (!is_valid_email_addr(argv[1])) {
2657 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2660 if ((str = mail_prohibited_address(argv[1]))) {
2661 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2664 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2665 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2667 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2669 nickserv_set_email_addr(hi, argv[1]);
2671 nickserv_eat_cookie(hi->cookie);
2672 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2675 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2679 static OPTION_FUNC(opt_maxlogins)
2681 unsigned char maxlogins;
2683 maxlogins = strtoul(argv[1], NULL, 0);
2684 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2685 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2688 hi->maxlogins = maxlogins;
2690 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2691 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2695 static OPTION_FUNC(opt_language)
2697 struct language *lang;
2699 lang = language_find(argv[1]);
2700 if (irccasecmp(lang->name, argv[1]))
2701 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2702 hi->language = lang;
2704 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2708 static OPTION_FUNC(opt_karma)
2711 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2716 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2717 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2718 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2719 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2721 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2725 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2730 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2731 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2733 if ((user->handle_info->opserv_level < target->opserv_level)
2734 || ((user->handle_info->opserv_level == target->opserv_level)
2735 && (user->handle_info->opserv_level < 1000))) {
2736 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2739 if ((user->handle_info->opserv_level < new_level)
2740 || ((user->handle_info->opserv_level == new_level)
2741 && (user->handle_info->opserv_level < 1000))) {
2742 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2745 if (user->handle_info == target) {
2746 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2749 if (target->opserv_level == new_level)
2751 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2752 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2753 target->opserv_level = new_level;
2757 static OPTION_FUNC(opt_level)
2762 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2766 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2767 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2771 static OPTION_FUNC(opt_epithet)
2774 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2778 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2779 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2782 if ((epithet[0] == '*') && !epithet[1])
2785 hi->epithet = strdup(epithet);
2789 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2791 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2795 static OPTION_FUNC(opt_title)
2800 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2804 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2806 if (strchr(title, '.')) {
2807 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2810 if ((strlen(user->handle_info->handle) + strlen(title) +
2811 strlen(titlehost_suffix) + 2) > HOSTLEN) {
2812 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2817 if (!strcmp(title, "*")) {
2818 hi->fakehost = NULL;
2820 hi->fakehost = malloc(strlen(title)+2);
2821 hi->fakehost[0] = '.';
2822 strcpy(hi->fakehost+1, title);
2824 apply_fakehost(hi, NULL);
2825 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2826 title = hi->fakehost + 1;
2830 title = user_find_message(user, "MSG_NONE");
2831 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2835 static OPTION_FUNC(opt_fakehost)
2837 char mask[USERLEN + HOSTLEN + 2];
2841 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2845 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2846 if(strlen(argv[1]) >= sizeof(mask)) {
2847 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
2851 safestrncpy(mask, argv[1], sizeof(mask));
2853 if ((host = strrchr(mask, '@')) && host != mask) {
2854 /* If ident@host was used and the user doesn't have access to set idents, do not change anything. */
2855 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2867 if (ident && strlen(ident) > USERLEN) {
2868 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2872 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
2873 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2877 if (host && host[0]) {
2879 if (!strcmp(host, "*"))
2880 hi->fakehost = NULL;
2882 hi->fakehost = strdup(host);
2883 host = hi->fakehost;
2886 host = generate_fakehost(hi);
2889 free(hi->fakeident);
2890 if (!strcmp(ident, "*"))
2891 hi->fakeident = NULL;
2893 hi->fakeident = strdup(ident);
2894 ident = hi->fakeident;
2897 ident = generate_fakeident(hi, NULL);
2899 apply_fakehost(hi, NULL);
2901 host = generate_fakehost(hi);
2902 ident = generate_fakeident(hi, NULL);
2905 host = (char *) user_find_message(user, "MSG_NONE");
2907 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
2909 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
2913 static OPTION_FUNC(opt_fakeident)
2918 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2922 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2924 if (strlen(ident) > USERLEN) {
2925 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2928 free(hi->fakeident);
2929 if (!strcmp(ident, "*"))
2930 hi->fakeident = NULL;
2932 hi->fakeident = strdup(ident);
2933 ident = hi->fakeident;
2934 apply_fakehost(hi, NULL);
2936 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
2938 ident = user_find_message(user, "MSG_NONE");
2939 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
2943 static NICKSERV_FUNC(cmd_reclaim)
2945 struct nick_info *ni;
2946 struct userNode *victim;
2948 NICKSERV_MIN_PARMS(2);
2949 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2951 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2954 if (ni->owner != user->handle_info) {
2955 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2958 victim = GetUserH(ni->nick);
2960 reply("MSG_NICK_UNKNOWN", ni->nick);
2963 if (victim == user) {
2964 reply("NSMSG_NICK_USER_YOU");
2967 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2968 switch (nickserv_conf.reclaim_action) {
2969 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2970 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2971 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2972 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2977 static NICKSERV_FUNC(cmd_unregnick)
2980 struct handle_info *hi;
2981 struct nick_info *ni;
2983 hi = user->handle_info;
2984 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2985 ni = dict_find(nickserv_nick_dict, nick, NULL);
2987 reply("NSMSG_UNKNOWN_NICK", nick);
2990 if (hi != ni->owner) {
2991 reply("NSMSG_NOT_YOUR_NICK", nick);
2994 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2999 static NICKSERV_FUNC(cmd_ounregnick)
3001 struct nick_info *ni;
3003 NICKSERV_MIN_PARMS(2);
3004 if (!(ni = get_nick_info(argv[1]))) {
3005 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
3008 if (!oper_outranks(user, ni->owner))
3010 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3015 static NICKSERV_FUNC(cmd_unregister)
3017 struct handle_info *hi;
3020 NICKSERV_MIN_PARMS(2);
3021 hi = user->handle_info;
3024 if (checkpass(passwd, hi->passwd)) {
3025 nickserv_unregister_handle(hi, user);
3028 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3029 reply("NSMSG_PASSWORD_INVALID");
3034 static NICKSERV_FUNC(cmd_ounregister)
3036 struct handle_info *hi;
3037 char reason[MAXLEN];
3040 NICKSERV_MIN_PARMS(2);
3041 if (!(hi = get_victim_oper(user, argv[1])))
3044 if (HANDLE_FLAGGED(hi, NODELETE)) {
3045 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
3049 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
3051 ((hi->flags & nickserv_conf.ounregister_flags)
3053 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
3054 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
3058 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
3059 global_message(MESSAGE_RECIPIENT_STAFF, reason);
3060 nickserv_unregister_handle(hi, user);
3064 static NICKSERV_FUNC(cmd_status)
3066 if (nickserv_conf.disable_nicks) {
3067 reply("NSMSG_GLOBAL_STATS_NONICK",
3068 dict_size(nickserv_handle_dict));
3070 if (user->handle_info) {
3072 struct nick_info *ni;
3073 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
3074 reply("NSMSG_HANDLE_STATS", cnt);
3076 reply("NSMSG_HANDLE_NONE");
3078 reply("NSMSG_GLOBAL_STATS",
3079 dict_size(nickserv_handle_dict),
3080 dict_size(nickserv_nick_dict));
3085 static NICKSERV_FUNC(cmd_ghost)
3087 struct userNode *target;
3088 char reason[MAXLEN];
3090 NICKSERV_MIN_PARMS(2);
3091 if (!(target = GetUserH(argv[1]))) {
3092 reply("MSG_NICK_UNKNOWN", argv[1]);
3095 if (target == user) {
3096 reply("NSMSG_CANNOT_GHOST_SELF");
3099 if (!target->handle_info || (target->handle_info != user->handle_info)) {
3100 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
3103 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
3104 DelUser(target, nickserv, 1, reason);
3105 reply("NSMSG_GHOST_KILLED", argv[1]);
3109 static NICKSERV_FUNC(cmd_vacation)
3111 HANDLE_SET_FLAG(user->handle_info, FROZEN);
3112 reply("NSMSG_ON_VACATION");
3116 static NICKSERV_FUNC(cmd_addnote)
3118 struct handle_info *hi;
3119 unsigned long duration;
3122 struct handle_note *prev;
3123 struct handle_note *note;
3125 /* Parse parameters and figure out values for note's fields. */
3126 NICKSERV_MIN_PARMS(4);
3127 hi = get_victim_oper(user, argv[1]);
3130 if(!strcmp(argv[2], "0"))
3132 else if(!(duration = ParseInterval(argv[2])))
3134 reply("MSG_INVALID_DURATION", argv[2]);
3137 if (duration > 2*365*86400) {
3138 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
3141 unsplit_string(argv + 3, argc - 3, text);
3142 WALK_NOTES(hi, prev, note) {}
3143 id = prev ? (prev->id + 1) : 1;
3145 /* Create the new note structure. */
3146 note = calloc(1, sizeof(*note) + strlen(text));
3148 note->expires = duration ? (now + duration) : 0;
3151 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
3152 strcpy(note->note, text);
3157 reply("NSMSG_NOTE_ADDED", id, hi->handle);
3161 static NICKSERV_FUNC(cmd_delnote)
3163 struct handle_info *hi;
3164 struct handle_note *prev;
3165 struct handle_note *note;
3168 NICKSERV_MIN_PARMS(3);
3169 hi = get_victim_oper(user, argv[1]);
3172 id = strtoul(argv[2], NULL, 10);
3173 WALK_NOTES(hi, prev, note) {
3174 if (id == note->id) {
3176 prev->next = note->next;
3178 hi->notes = note->next;
3180 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3184 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3189 nickserv_saxdb_write(struct saxdb_context *ctx) {
3191 struct handle_info *hi;
3194 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3196 assert(hi->id != 0);
3197 saxdb_start_record(ctx, iter_key(it), 0);
3199 struct handle_cookie *cookie = hi->cookie;
3202 switch (cookie->type) {
3203 case ACTIVATION: type = KEY_ACTIVATION; break;
3204 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3205 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3206 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3207 default: type = NULL; break;
3210 saxdb_start_record(ctx, KEY_COOKIE, 0);
3211 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3212 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3214 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3215 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3216 saxdb_end_record(ctx);
3220 struct handle_note *prev, *note;
3221 saxdb_start_record(ctx, KEY_NOTES, 0);
3222 WALK_NOTES(hi, prev, note) {
3223 snprintf(flags, sizeof(flags), "%d", note->id);
3224 saxdb_start_record(ctx, flags, 0);
3226 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3227 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3228 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3229 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3230 saxdb_end_record(ctx);
3232 saxdb_end_record(ctx);
3235 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3237 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3239 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3241 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3245 for (ii=flen=0; handle_flags[ii]; ++ii)
3246 if (hi->flags & (1 << ii))
3247 flags[flen++] = handle_flags[ii];
3249 saxdb_write_string(ctx, KEY_FLAGS, flags);
3251 saxdb_write_int(ctx, KEY_ID, hi->id);
3253 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3255 saxdb_write_string(ctx, KEY_DEVNULL, hi->devnull);
3257 saxdb_write_string(ctx, KEY_WEBSITE, hi->website);
3258 if (hi->last_quit_host[0])
3259 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3260 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3262 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3263 if (hi->masks->used)
3264 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3266 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3268 struct string_list *slist;
3269 struct nick_info *ni;
3271 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3272 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3273 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3277 if (hi->opserv_level)
3278 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3279 if (hi->language != lang_C)
3280 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3281 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3282 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3283 if (hi->screen_width)
3284 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3285 if (hi->table_width)
3286 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3287 flags[0] = hi->userlist_style;
3289 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3290 saxdb_end_record(ctx);
3295 static handle_merge_func_t *handle_merge_func_list;
3296 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3299 reg_handle_merge_func(handle_merge_func_t func)
3301 if (handle_merge_func_used == handle_merge_func_size) {
3302 if (handle_merge_func_size) {
3303 handle_merge_func_size <<= 1;
3304 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3306 handle_merge_func_size = 8;
3307 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3310 handle_merge_func_list[handle_merge_func_used++] = func;
3313 static NICKSERV_FUNC(cmd_merge)
3315 struct handle_info *hi_from, *hi_to;
3316 struct userNode *last_user;
3317 struct userData *cList, *cListNext;
3318 unsigned int ii, jj, n;
3319 char buffer[MAXLEN];
3321 NICKSERV_MIN_PARMS(3);
3323 if (!(hi_from = get_victim_oper(user, argv[1])))
3325 if (!(hi_to = get_victim_oper(user, argv[2])))
3327 if (hi_to == hi_from) {
3328 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3332 for (n=0; n<handle_merge_func_used; n++)
3333 handle_merge_func_list[n](user, hi_to, hi_from);
3335 /* Append "from" handle's nicks to "to" handle's nick list. */
3337 struct nick_info *last_ni;
3338 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3339 last_ni->next = hi_from->nicks;
3341 while (hi_from->nicks) {
3342 hi_from->nicks->owner = hi_to;
3343 hi_from->nicks = hi_from->nicks->next;
3346 /* Merge the hostmasks. */
3347 for (ii=0; ii<hi_from->masks->used; ii++) {
3348 char *mask = hi_from->masks->list[ii];
3349 for (jj=0; jj<hi_to->masks->used; jj++)
3350 if (match_ircglobs(hi_to->masks->list[jj], mask))
3352 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3353 string_list_append(hi_to->masks, strdup(mask));
3356 /* Merge the lists of authed users. */
3358 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3359 last_user->next_authed = hi_from->users;
3361 hi_to->users = hi_from->users;
3363 /* Repoint the old "from" handle's users. */
3364 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3365 last_user->handle_info = hi_to;
3367 hi_from->users = NULL;
3369 /* Merge channel userlists. */
3370 for (cList=hi_from->channels; cList; cList=cListNext) {
3371 struct userData *cList2;
3372 cListNext = cList->u_next;
3373 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3374 if (cList->channel == cList2->channel)
3376 if (cList2 && (cList2->access >= cList->access)) {
3377 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);
3378 /* keep cList2 in hi_to; remove cList from hi_from */
3379 del_channel_user(cList, 1);
3382 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);
3383 /* remove the lower-ranking cList2 from hi_to */
3384 del_channel_user(cList2, 1);
3386 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3388 /* cList needs to be moved from hi_from to hi_to */
3389 cList->handle = hi_to;
3390 /* Remove from linked list for hi_from */
3391 assert(!cList->u_prev);
3392 hi_from->channels = cList->u_next;
3394 cList->u_next->u_prev = cList->u_prev;
3395 /* Add to linked list for hi_to */
3396 cList->u_prev = NULL;
3397 cList->u_next = hi_to->channels;
3398 if (hi_to->channels)
3399 hi_to->channels->u_prev = cList;
3400 hi_to->channels = cList;
3404 /* Do they get an OpServ level promotion? */
3405 if (hi_from->opserv_level > hi_to->opserv_level)
3406 hi_to->opserv_level = hi_from->opserv_level;
3408 /* What about last seen time? */
3409 if (hi_from->lastseen > hi_to->lastseen)
3410 hi_to->lastseen = hi_from->lastseen;
3412 /* New karma is the sum of the two original karmas. */
3413 hi_to->karma += hi_from->karma;
3415 /* Does a fakehost carry over? (This intentionally doesn't set it
3416 * for users previously attached to hi_to. They'll just have to
3419 if (hi_from->fakehost && !hi_to->fakehost)
3420 hi_to->fakehost = strdup(hi_from->fakehost);
3421 if (hi_from->fakeident && !hi_to->fakeident)
3422 hi_to->fakeident = strdup(hi_from->fakeident);
3424 /* Notify of success. */
3425 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3426 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3427 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3429 /* Unregister the "from" handle. */
3430 nickserv_unregister_handle(hi_from, NULL);
3435 #define NICKSERV_DISCRIM_FIELDS_AUTH 0x01
3436 #define NICKSERV_DISCRIM_FIELDS_EMAIL 0x02
3437 #define NICKSERV_DISCRIM_FIELDS_SEEN 0x04
3438 #define NICKSERV_DISCRIM_FIELDS_ACCESS 0x08
3439 #define NICKSERV_DISCRIM_FIELDS_FAKEHOST 0x10
3440 #define NICKSERV_DISCRIM_FIELDS_WEBSITE 0x20
3441 #define NICKSERV_DISCRIM_FIELDS_DEVNULL 0x40
3443 #define NICKSERV_DISCRIM_FIELD_COUNT 7
3445 struct nickserv_discrim {
3446 unsigned int show_fields;
3447 struct helpfile_table *output_table;
3448 int output_table_pos;
3449 unsigned int output_table_free_fields;
3451 unsigned long flags_on, flags_off;
3452 unsigned long min_registered, max_registered;
3453 unsigned long lastseen;
3455 int min_level, max_level;
3456 int min_karma, max_karma;
3457 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3458 const char *nickmask;
3459 const char *hostmask;
3460 const char *fakehostmask;
3461 const char *fakeidentmask;
3462 const char *website;
3463 const char *devnullclass;
3464 const char *handlemask;
3465 const char *emailmask;
3468 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi, struct nickserv_discrim *discrim);
3470 struct discrim_apply_info {
3471 struct nickserv_discrim *discrim;
3472 discrim_search_func func;
3473 struct userNode *source;
3474 unsigned int matched;
3477 static struct nickserv_discrim *
3478 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3481 struct nickserv_discrim *discrim;
3483 discrim = malloc(sizeof(*discrim));
3484 memset(discrim, 0, sizeof(*discrim));
3485 discrim->min_level = 0;
3486 discrim->max_level = INT_MAX;
3487 discrim->limit = 50;
3488 discrim->min_registered = 0;
3489 discrim->max_registered = ULONG_MAX;
3490 discrim->lastseen = ULONG_MAX;
3491 discrim->min_karma = INT_MIN;
3492 discrim->max_karma = INT_MAX;
3494 for (i=0; i<argc; i++) {
3495 if (i == argc - 1) {
3496 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3499 if (!irccasecmp(argv[i], "limit")) {
3500 discrim->limit = strtoul(argv[++i], NULL, 0);
3501 } else if (!irccasecmp(argv[i], "flags")) {
3502 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3503 } else if (!irccasecmp(argv[i], "fields")) {
3504 char *fields = argv[++i];
3505 char *delimiter = strstr(fields, ",");
3509 if(!irccasecmp(fields, "auth"))
3510 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_AUTH;
3511 else if(!irccasecmp(fields, "email"))
3512 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_EMAIL;
3513 else if(!irccasecmp(fields, "seen"))
3514 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_SEEN;
3515 else if(!irccasecmp(fields, "access"))
3516 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_ACCESS;
3517 else if(!irccasecmp(fields, "fakehost"))
3518 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_FAKEHOST;
3519 else if(!irccasecmp(fields, "website") && IsBot(user))
3520 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_WEBSITE;
3521 else if(!irccasecmp(fields, "devnull"))
3522 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_DEVNULL;
3524 send_message(user, nickserv, "MSG_INVALID_FIELD", fields);
3529 fields = delimiter+1;
3531 delimiter = strstr(fields, ",");
3537 } else if (!irccasecmp(argv[i], "registered")) {
3538 const char *cmp = argv[++i];
3539 if (cmp[0] == '<') {
3540 if (cmp[1] == '=') {
3541 discrim->min_registered = now - ParseInterval(cmp+2);
3543 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3545 } else if (cmp[0] == '=') {
3546 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3547 } else if (cmp[0] == '>') {
3548 if (cmp[1] == '=') {
3549 discrim->max_registered = now - ParseInterval(cmp+2);
3551 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3554 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3556 } else if (!irccasecmp(argv[i], "seen")) {
3557 discrim->lastseen = now - ParseInterval(argv[++i]);
3558 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3559 discrim->nickmask = argv[++i];
3560 } else if (!irccasecmp(argv[i], "hostmask")) {
3562 if (!irccasecmp(argv[i], "exact")) {
3563 if (i == argc - 1) {
3564 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3567 discrim->hostmask_type = EXACT;
3568 } else if (!irccasecmp(argv[i], "subset")) {
3569 if (i == argc - 1) {
3570 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3573 discrim->hostmask_type = SUBSET;
3574 } else if (!irccasecmp(argv[i], "superset")) {
3575 if (i == argc - 1) {
3576 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3579 discrim->hostmask_type = SUPERSET;
3580 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3581 if (i == argc - 1) {
3582 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3585 discrim->hostmask_type = LASTQUIT;
3588 discrim->hostmask_type = SUPERSET;
3590 discrim->hostmask = argv[++i];
3591 } else if (!irccasecmp(argv[i], "fakehost")) {
3592 if (!irccasecmp(argv[++i], "*")) {
3593 discrim->fakehostmask = 0;
3595 discrim->fakehostmask = argv[i];
3597 } else if (!irccasecmp(argv[i], "fakeident")) {
3598 if (!irccasecmp(argv[++i], "*")) {
3599 discrim->fakeidentmask = 0;
3601 discrim->fakeidentmask = argv[i];
3603 } else if (!irccasecmp(argv[i], "website")) {
3604 if (!irccasecmp(argv[++i], "*")) {
3605 discrim->website = 0;
3607 discrim->website = argv[i];
3609 } else if (!irccasecmp(argv[i], "devnull")) {
3610 if (!irccasecmp(argv[++i], "*")) {
3611 discrim->devnullclass = 0;
3613 discrim->devnullclass = argv[i];
3615 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3616 if (!irccasecmp(argv[++i], "*")) {
3617 discrim->handlemask = 0;
3619 discrim->handlemask = argv[i];
3621 } else if (!irccasecmp(argv[i], "email")) {
3622 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3623 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3625 } else if (!irccasecmp(argv[++i], "*")) {
3626 discrim->emailmask = 0;
3628 discrim->emailmask = argv[i];
3630 } else if (!irccasecmp(argv[i], "access")) {
3631 const char *cmp = argv[++i];
3632 if (cmp[0] == '<') {
3633 if (discrim->min_level == 0) discrim->min_level = 1;
3634 if (cmp[1] == '=') {
3635 discrim->max_level = strtoul(cmp+2, NULL, 0);
3637 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3639 } else if (cmp[0] == '=') {
3640 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3641 } else if (cmp[0] == '>') {
3642 if (cmp[1] == '=') {
3643 discrim->min_level = strtoul(cmp+2, NULL, 0);
3645 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3648 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3650 } else if (!irccasecmp(argv[i], "karma")) {
3651 const char *cmp = argv[++i];
3652 if (cmp[0] == '<') {
3653 if (cmp[1] == '=') {
3654 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3656 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3658 } else if (cmp[0] == '=') {
3659 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3660 } else if (cmp[0] == '>') {
3661 if (cmp[1] == '=') {
3662 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3664 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3667 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3670 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3681 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3683 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3684 || (discrim->flags_off & hi->flags)
3685 || (discrim->min_registered > hi->registered)
3686 || (discrim->max_registered < hi->registered)
3687 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3688 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3689 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3690 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3691 || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website)))
3692 || (discrim->devnullclass && (!hi->devnull || !match_ircglob(hi->devnull, discrim->devnullclass)))
3693 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3694 || (discrim->min_level > hi->opserv_level)
3695 || (discrim->max_level < hi->opserv_level)
3696 || (discrim->min_karma > hi->karma)
3697 || (discrim->max_karma < hi->karma)
3701 if (discrim->hostmask) {
3703 for (i=0; i<hi->masks->used; i++) {
3704 const char *mask = hi->masks->list[i];
3705 if ((discrim->hostmask_type == SUBSET)
3706 && (match_ircglobs(discrim->hostmask, mask))) break;
3707 else if ((discrim->hostmask_type == EXACT)
3708 && !irccasecmp(discrim->hostmask, mask)) break;
3709 else if ((discrim->hostmask_type == SUPERSET)
3710 && (match_ircglobs(mask, discrim->hostmask))) break;
3711 else if ((discrim->hostmask_type == LASTQUIT)
3712 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3714 if (i==hi->masks->used) return 0;
3716 if (discrim->nickmask) {
3717 struct nick_info *nick = hi->nicks;
3719 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3722 if (!nick) return 0;
3728 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3730 dict_iterator_t it, next;
3731 unsigned int matched;
3733 for (it = dict_first(nickserv_handle_dict), matched = 0;
3734 it && (matched < discrim->limit);
3736 next = iter_next(it);
3737 if (nickserv_discrim_match(discrim, iter_data(it))) {
3738 dsf(source, iter_data(it), discrim);
3746 search_print_func(struct userNode *source, struct handle_info *match, struct nickserv_discrim *discrim)
3748 if(discrim->show_fields) {
3750 if(discrim->output_table) {
3751 discrim->output_table->contents[++discrim->output_table_pos] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
3753 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
3754 discrim->output_table->contents[discrim->output_table_pos][i++] = match->handle;
3755 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
3756 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->email_addr ? match->email_addr : "*");
3757 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN) {
3759 char seenBuf[INTERVALLEN];
3762 } else if(match->lastseen == 0) {
3765 seen = intervalString(seenBuf, now - match->lastseen, source->handle_info);
3767 discrim->output_table_free_fields |= 1 << i;
3768 discrim->output_table->contents[discrim->output_table_pos][i++] = strdup(seen);
3770 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
3771 discrim->output_table->contents[discrim->output_table_pos][i++] = strtab(match->opserv_level);
3772 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
3773 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->fakehost ? match->fakehost : "*");
3774 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
3775 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->website ? match->website : "*");
3776 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
3777 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->devnull ? match->devnull : "*");
3781 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3785 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match), UNUSED_ARG(struct nickserv_discrim *discrim))
3790 search_unregister_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
3792 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3793 nickserv_unregister_handle(match, source);
3797 nickserv_sort_accounts_by_access(const void *a, const void *b)
3799 const struct handle_info *hi_a = *(const struct handle_info**)a;
3800 const struct handle_info *hi_b = *(const struct handle_info**)b;
3801 if (hi_a->opserv_level != hi_b->opserv_level)
3802 return hi_b->opserv_level - hi_a->opserv_level;
3803 return irccasecmp(hi_a->handle, hi_b->handle);
3807 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3809 struct handle_info_list hil;
3810 struct helpfile_table tbl;
3815 memset(&hil, 0, sizeof(hil));
3816 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3817 struct handle_info *hi = iter_data(it);
3818 if (hi->opserv_level)
3819 handle_info_list_append(&hil, hi);
3821 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3822 tbl.length = hil.used + 1;
3824 tbl.flags = TABLE_NO_FREE;
3825 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3826 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3829 for (ii = 0; ii < hil.used; ) {
3830 ary = malloc(tbl.width * sizeof(ary[0]));
3831 ary[0] = hil.list[ii]->handle;
3832 ary[1] = strtab(hil.list[ii]->opserv_level);
3833 tbl.contents[++ii] = ary;
3835 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3836 reply("MSG_MATCH_COUNT", hil.used);
3837 for (ii = 0; ii < hil.used; ii++)
3838 free(tbl.contents[ii]);
3843 static NICKSERV_FUNC(cmd_search)
3845 struct nickserv_discrim *discrim;
3846 discrim_search_func action;
3847 struct svccmd *subcmd;
3848 unsigned int matches;
3851 NICKSERV_MIN_PARMS(3);
3852 sprintf(buf, "search %s", argv[1]);
3853 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3854 if (!irccasecmp(argv[1], "print"))
3855 action = search_print_func;
3856 else if (!irccasecmp(argv[1], "count"))
3857 action = search_count_func;
3858 else if (!irccasecmp(argv[1], "unregister"))
3859 action = search_unregister_func;
3861 reply("NSMSG_INVALID_ACTION", argv[1]);
3865 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3868 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3872 if (action == search_print_func)
3873 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3874 else if (action == search_count_func)
3875 discrim->limit = INT_MAX;
3877 matches = nickserv_discrim_search(discrim, action, user);
3879 if(discrim->show_fields) {
3882 for(ii = 0; ii < NICKSERV_DISCRIM_FIELD_COUNT; ii++) {
3883 if(discrim->show_fields & (1 << ii)) width++;
3885 discrim->output_table = malloc(sizeof(discrim->output_table[0]));
3886 discrim->output_table->length = matches+1;
3887 discrim->output_table->width = width;
3888 discrim->output_table->flags = TABLE_NO_FREE;
3889 discrim->output_table->contents = malloc(discrim->output_table->length * sizeof(discrim->output_table->contents[0]));
3890 discrim->output_table->contents[0] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
3893 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
3894 discrim->output_table->contents[0][ii++] = "Auth";
3895 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
3896 discrim->output_table->contents[0][ii++] = "EMail";
3897 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN)
3898 discrim->output_table->contents[0][ii++] = "Seen";
3899 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
3900 discrim->output_table->contents[0][ii++] = "Access";
3901 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
3902 discrim->output_table->contents[0][ii++] = "Fakehost";
3903 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
3904 discrim->output_table->contents[0][ii++] = "Website";
3905 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
3906 discrim->output_table->contents[0][ii++] = "DevNull";
3908 nickserv_discrim_search(discrim, action, user);
3910 table_send(nickserv, user->nick, 0, NULL, *discrim->output_table);
3912 for(ii = 1; ii < discrim->output_table->length; ++ii) {
3914 for(ij = 0; ij < NICKSERV_DISCRIM_FIELD_COUNT; ij++) {
3915 if(discrim->output_table_free_fields & (1 << ij))
3916 free((char*)discrim->output_table->contents[ii][ij]);
3918 free(discrim->output_table->contents[ii]);
3920 free(discrim->output_table->contents[0]);
3921 free(discrim->output_table->contents);
3922 free(discrim->output_table);
3925 reply("MSG_MATCH_COUNT", matches);
3927 reply("MSG_NO_MATCHES");
3934 static MODCMD_FUNC(cmd_checkpass)
3936 struct handle_info *hi;
3938 NICKSERV_MIN_PARMS(3);
3939 if (!(hi = get_handle_info(argv[1]))) {
3940 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3943 if (checkpass(argv[2], hi->passwd))
3944 reply("CHECKPASS_YES");
3946 reply("CHECKPASS_NO");
3951 static MODCMD_FUNC(cmd_checkemail)
3953 struct handle_info *hi;
3955 NICKSERV_MIN_PARMS(3);
3956 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3959 if (!hi->email_addr)
3960 reply("CHECKEMAIL_NOT_SET");
3961 else if (!irccasecmp(argv[2], hi->email_addr))
3962 reply("CHECKEMAIL_YES");
3964 reply("CHECKEMAIL_NO");
3970 nickserv_db_read_handle(const char *handle, dict_t obj)
3973 struct string_list *masks, *slist;
3974 struct handle_info *hi;
3975 struct userNode *authed_users;
3976 struct userData *channel_list;
3981 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3982 id = str ? strtoul(str, NULL, 0) : 0;
3983 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3985 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3988 if ((hi = get_handle_info(handle))) {
3989 authed_users = hi->users;
3990 channel_list = hi->channels;
3992 hi->channels = NULL;
3993 dict_remove(nickserv_handle_dict, hi->handle);
3995 authed_users = NULL;
3996 channel_list = NULL;
3998 hi = register_handle(handle, str, id);
4000 hi->users = authed_users;
4001 while (authed_users) {
4002 authed_users->handle_info = hi;
4003 authed_users = authed_users->next_authed;
4006 hi->channels = channel_list;
4007 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
4008 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
4009 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
4010 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
4011 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
4012 hi->language = language_find(str ? str : "C");
4013 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
4014 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
4015 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
4017 hi->infoline = strdup(str);
4018 str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING);
4020 hi->website = strdup(str);
4021 str = database_get_data(obj, KEY_DEVNULL, RECDB_QSTRING);
4023 hi->devnull = strdup(str);
4024 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
4025 hi->registered = str ? strtoul(str, NULL, 0) : now;
4026 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
4027 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
4028 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
4029 hi->karma = str ? strtoul(str, NULL, 0) : 0;
4030 /* We want to read the nicks even if disable_nicks is set. This is so
4031 * that we don't lose the nick data entirely. */
4032 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
4034 for (ii=0; ii<slist->used; ii++)
4035 register_nick(slist->list[ii], hi);
4037 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
4039 for (ii=0; str[ii]; ii++)
4040 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
4042 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
4043 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
4044 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
4045 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
4046 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
4047 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
4048 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
4050 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
4052 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
4053 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
4055 nickserv_set_email_addr(hi, str);
4056 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
4058 hi->epithet = strdup(str);
4059 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
4061 hi->fakehost = strdup(str);
4062 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
4064 hi->fakeident = strdup(str);
4065 /* Read the "cookie" sub-database (if it exists). */
4066 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
4068 const char *data, *type, *expires, *cookie_str;
4069 struct handle_cookie *cookie;
4071 cookie = calloc(1, sizeof(*cookie));
4072 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
4073 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
4074 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
4075 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
4076 if (!type || !expires || !cookie_str) {
4077 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
4080 if (!irccasecmp(type, KEY_ACTIVATION))
4081 cookie->type = ACTIVATION;
4082 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
4083 cookie->type = PASSWORD_CHANGE;
4084 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
4085 cookie->type = EMAIL_CHANGE;
4086 else if (!irccasecmp(type, KEY_ALLOWAUTH))
4087 cookie->type = ALLOWAUTH;
4089 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
4092 cookie->expires = strtoul(expires, NULL, 0);
4093 if (cookie->expires < now)
4096 cookie->data = strdup(data);
4097 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
4101 nickserv_bake_cookie(cookie);
4103 nickserv_free_cookie(cookie);
4105 /* Read the "notes" sub-database (if it exists). */
4106 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
4109 struct handle_note *last_note;
4110 struct handle_note *note;
4113 for (it = dict_first(subdb); it; it = iter_next(it)) {
4114 const char *expires;
4118 const char *note_id;
4121 note_id = iter_key(it);
4122 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
4124 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
4127 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
4128 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
4129 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
4130 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
4131 if (!setter || !text || !set) {
4132 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
4135 note = calloc(1, sizeof(*note) + strlen(text));
4137 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
4138 note->set = strtoul(set, NULL, 10);
4139 note->id = strtoul(note_id, NULL, 10);
4140 safestrncpy(note->setter, setter, sizeof(note->setter));
4141 strcpy(note->note, text);
4143 last_note->next = note;
4152 nickserv_saxdb_read(dict_t db) {
4154 struct record_data *rd;
4156 for (it=dict_first(db); it; it=iter_next(it)) {
4158 nickserv_db_read_handle(iter_key(it), rd->d.object);
4163 static NICKSERV_FUNC(cmd_mergedb)
4165 struct timeval start, stop;
4168 NICKSERV_MIN_PARMS(2);
4169 gettimeofday(&start, NULL);
4170 if (!(db = parse_database(argv[1]))) {
4171 reply("NSMSG_DB_UNREADABLE", argv[1]);
4174 nickserv_saxdb_read(db);
4176 gettimeofday(&stop, NULL);
4177 stop.tv_sec -= start.tv_sec;
4178 stop.tv_usec -= start.tv_usec;
4179 if (stop.tv_usec < 0) {
4181 stop.tv_usec += 1000000;
4183 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
4188 expire_handles(UNUSED_ARG(void *data))
4190 dict_iterator_t it, next;
4191 unsigned long expiry;
4192 struct handle_info *hi;
4194 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4195 next = iter_next(it);
4197 if ((hi->opserv_level > 0)
4199 || HANDLE_FLAGGED(hi, FROZEN)
4200 || HANDLE_FLAGGED(hi, NODELETE)) {
4203 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4204 if ((now - hi->lastseen) > expiry) {
4205 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
4206 nickserv_unregister_handle(hi, NULL);
4210 if (nickserv_conf.handle_expire_frequency)
4211 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4215 nickserv_load_dict(const char *fname)
4219 if (!(file = fopen(fname, "r"))) {
4220 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4223 while (fgets(line, sizeof(line), file)) {
4226 if (line[strlen(line)-1] == '\n')
4227 line[strlen(line)-1] = 0;
4228 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4231 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4234 static enum reclaim_action
4235 reclaim_action_from_string(const char *str) {
4237 return RECLAIM_NONE;
4238 else if (!irccasecmp(str, "warn"))
4239 return RECLAIM_WARN;
4240 else if (!irccasecmp(str, "svsnick"))
4241 return RECLAIM_SVSNICK;
4242 else if (!irccasecmp(str, "kill"))
4243 return RECLAIM_KILL;
4245 return RECLAIM_NONE;
4249 nickserv_conf_read(void)
4251 dict_t conf_node, child;
4255 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4256 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4259 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4261 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4262 if (nickserv_conf.valid_handle_regex_set)
4263 regfree(&nickserv_conf.valid_handle_regex);
4265 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4266 nickserv_conf.valid_handle_regex_set = !err;
4267 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4269 nickserv_conf.valid_handle_regex_set = 0;
4271 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4272 if (nickserv_conf.valid_nick_regex_set)
4273 regfree(&nickserv_conf.valid_nick_regex);
4275 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4276 nickserv_conf.valid_nick_regex_set = !err;
4277 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4279 nickserv_conf.valid_nick_regex_set = 0;
4281 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4283 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4284 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4285 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4286 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4287 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4288 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4289 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4290 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4291 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4292 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4293 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4294 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4295 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4296 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4297 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4298 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4299 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4300 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4301 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4302 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4303 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4304 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4305 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4306 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4307 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
4308 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
4309 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4311 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4312 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4313 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4315 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4316 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4317 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4319 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4320 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4321 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4322 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4323 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4324 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4325 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4326 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4327 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4328 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4329 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4332 nickserv_conf.ounregister_flags = 0;
4334 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4337 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4339 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4341 nickserv_conf.handle_ts_mode = TS_IGNORE;
4342 else if (!irccasecmp(str, "ircu"))
4343 nickserv_conf.handle_ts_mode = TS_IRCU;
4345 nickserv_conf.handle_ts_mode = TS_IGNORE;
4346 if (!nickserv_conf.disable_nicks) {
4347 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4348 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4349 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4350 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4351 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4352 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4353 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4354 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4356 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4357 for (it=dict_first(child); it; it=iter_next(it)) {
4358 const char *key = iter_key(it), *value;
4362 if (!strncasecmp(key, "uc_", 3))
4363 flag = toupper(key[3]);
4364 else if (!strncasecmp(key, "lc_", 3))
4365 flag = tolower(key[3]);
4369 if ((pos = handle_inverse_flags[flag])) {
4370 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4371 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4374 if (nickserv_conf.weak_password_dict)
4375 dict_delete(nickserv_conf.weak_password_dict);
4376 nickserv_conf.weak_password_dict = dict_new();
4377 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4378 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4379 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4380 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4382 nickserv_load_dict(str);
4383 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4384 if (nickserv && str)
4385 NickChange(nickserv, str, 0);
4386 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4387 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4388 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4389 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4390 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4391 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4392 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4393 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4394 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4395 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4396 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4397 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4398 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4399 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4400 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4401 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4402 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4403 titlehost_suffix = str ? str : "example.net";
4404 str = conf_get_data("server/network", RECDB_QSTRING);
4405 nickserv_conf.network_name = str ? str : "some IRC network";
4406 if (!nickserv_conf.auth_policer_params) {
4407 nickserv_conf.auth_policer_params = policer_params_new();
4408 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4409 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4411 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4412 for (it=dict_first(child); it; it=iter_next(it))
4413 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4417 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4419 char newnick[NICKLEN+1];
4428 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4430 case RECLAIM_SVSNICK:
4432 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4433 } while (GetUserH(newnick));
4434 irc_svsnick(nickserv, user, newnick);
4437 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4438 DelUser(user, nickserv, 1, msg);
4444 nickserv_reclaim_p(void *data) {
4445 struct userNode *user = data;
4446 struct nick_info *ni = get_nick_info(user->nick);
4448 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4452 check_user_nick(struct userNode *user) {
4453 struct nick_info *ni;
4454 user->modes &= ~FLAGS_REGNICK;
4455 if (!(ni = get_nick_info(user->nick)))
4457 if (user->handle_info == ni->owner) {
4458 user->modes |= FLAGS_REGNICK;
4462 if (nickserv_conf.warn_nick_owned)
4463 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4464 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4466 if (nickserv_conf.auto_reclaim_delay)
4467 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4469 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4473 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4475 struct handle_info *hi = NULL;
4478 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4479 if ((hi == NULL) && (serial != 0)) {
4481 inttobase64(id, serial, IDLEN);
4482 hi = dict_find(nickserv_id_dict, id, NULL);
4486 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4487 && (timestamp != hi->registered)) {
4490 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4493 set_user_handle_info(user, hi, 0);
4495 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4500 handle_nick_change(struct userNode *user, const char *old_nick)
4502 struct handle_info *hi;
4504 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4505 dict_remove(nickserv_allow_auth_dict, old_nick);
4506 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4508 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4509 check_user_nick(user);
4513 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4515 dict_remove(nickserv_allow_auth_dict, user->nick);
4516 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4517 set_user_handle_info(user, NULL, 0);
4520 static struct modcmd *
4521 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4523 if (min_level > 0) {
4525 sprintf(buf, "%u", min_level);
4526 if (must_be_qualified) {
4527 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4529 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4531 } else if (min_level == 0) {
4532 if (must_be_qualified) {
4533 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4535 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4538 if (must_be_qualified) {
4539 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4541 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4547 nickserv_db_cleanup(void)
4549 unreg_del_user_func(nickserv_remove_user);
4550 userList_clean(&curr_helpers);
4551 policer_params_delete(nickserv_conf.auth_policer_params);
4552 dict_delete(nickserv_handle_dict);
4553 dict_delete(nickserv_nick_dict);
4554 dict_delete(nickserv_opt_dict);
4555 dict_delete(nickserv_allow_auth_dict);
4556 dict_delete(nickserv_email_dict);
4557 dict_delete(nickserv_id_dict);
4558 dict_delete(nickserv_conf.weak_password_dict);
4559 free(auth_func_list);
4560 free(unreg_func_list);
4562 free(allowauth_func_list);
4563 free(handle_merge_func_list);
4564 free(failpw_func_list);
4565 if (nickserv_conf.valid_handle_regex_set)
4566 regfree(&nickserv_conf.valid_handle_regex);
4567 if (nickserv_conf.valid_nick_regex_set)
4568 regfree(&nickserv_conf.valid_nick_regex);
4572 init_nickserv(const char *nick)
4575 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4576 reg_new_user_func(check_user_nick);
4577 reg_nick_change_func(handle_nick_change);
4578 reg_del_user_func(nickserv_remove_user);
4579 reg_account_func(handle_account);
4581 /* set up handle_inverse_flags */
4582 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4583 for (i=0; handle_flags[i]; i++) {
4584 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4585 flag_access_levels[i] = 0;
4588 conf_register_reload(nickserv_conf_read);
4589 nickserv_opt_dict = dict_new();
4590 nickserv_email_dict = dict_new();
4591 dict_set_free_keys(nickserv_email_dict, free);
4592 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4594 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4595 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4596 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4597 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4598 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4599 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4600 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4601 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4602 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4603 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4604 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4605 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4606 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4607 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4608 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4609 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4610 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4611 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4612 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4613 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4614 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4615 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4616 if (!nickserv_conf.disable_nicks) {
4617 /* nick management commands */
4618 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4619 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4620 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4621 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4622 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4623 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4625 if (nickserv_conf.email_enabled) {
4626 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4627 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4628 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4629 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4630 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4631 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4633 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4634 /* miscellaneous commands */
4635 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4636 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4637 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4638 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4639 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4640 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4642 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4643 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4644 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4645 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4646 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4647 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4648 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
4649 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4650 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4651 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4652 dict_insert(nickserv_opt_dict, "WEBSITE", opt_website);
4653 dict_insert(nickserv_opt_dict, "DEVNULL", opt_devnull);
4654 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4655 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4656 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4657 if (titlehost_suffix) {
4658 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4659 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4660 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4662 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4663 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4664 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4665 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4667 nickserv_handle_dict = dict_new();
4668 dict_set_free_keys(nickserv_handle_dict, free);
4669 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4671 nickserv_id_dict = dict_new();
4672 dict_set_free_keys(nickserv_id_dict, free);
4674 nickserv_nick_dict = dict_new();
4675 dict_set_free_data(nickserv_nick_dict, free);
4677 nickserv_allow_auth_dict = dict_new();
4679 userList_init(&curr_helpers);
4682 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4683 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4684 nickserv_service = service_register(nickserv);
4686 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4687 reg_exit_func(nickserv_db_cleanup);
4688 if(nickserv_conf.handle_expire_frequency)
4689 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4690 message_register_table(msgtab);