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))
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 struct nickserv_discrim {
3436 unsigned long flags_on, flags_off;
3437 unsigned long min_registered, max_registered;
3438 unsigned long lastseen;
3440 int min_level, max_level;
3441 int min_karma, max_karma;
3442 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3443 const char *nickmask;
3444 const char *hostmask;
3445 const char *fakehostmask;
3446 const char *fakeidentmask;
3447 const char *website;
3448 const char *devnullclass;
3449 const char *handlemask;
3450 const char *emailmask;
3453 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3455 struct discrim_apply_info {
3456 struct nickserv_discrim *discrim;
3457 discrim_search_func func;
3458 struct userNode *source;
3459 unsigned int matched;
3462 static struct nickserv_discrim *
3463 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3466 struct nickserv_discrim *discrim;
3468 discrim = malloc(sizeof(*discrim));
3469 memset(discrim, 0, sizeof(*discrim));
3470 discrim->min_level = 0;
3471 discrim->max_level = INT_MAX;
3472 discrim->limit = 50;
3473 discrim->min_registered = 0;
3474 discrim->max_registered = ULONG_MAX;
3475 discrim->lastseen = ULONG_MAX;
3476 discrim->min_karma = INT_MIN;
3477 discrim->max_karma = INT_MAX;
3479 for (i=0; i<argc; i++) {
3480 if (i == argc - 1) {
3481 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3484 if (!irccasecmp(argv[i], "limit")) {
3485 discrim->limit = strtoul(argv[++i], NULL, 0);
3486 } else if (!irccasecmp(argv[i], "flags")) {
3487 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3488 } else if (!irccasecmp(argv[i], "registered")) {
3489 const char *cmp = argv[++i];
3490 if (cmp[0] == '<') {
3491 if (cmp[1] == '=') {
3492 discrim->min_registered = now - ParseInterval(cmp+2);
3494 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3496 } else if (cmp[0] == '=') {
3497 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3498 } else if (cmp[0] == '>') {
3499 if (cmp[1] == '=') {
3500 discrim->max_registered = now - ParseInterval(cmp+2);
3502 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3505 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3507 } else if (!irccasecmp(argv[i], "seen")) {
3508 discrim->lastseen = now - ParseInterval(argv[++i]);
3509 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3510 discrim->nickmask = argv[++i];
3511 } else if (!irccasecmp(argv[i], "hostmask")) {
3513 if (!irccasecmp(argv[i], "exact")) {
3514 if (i == argc - 1) {
3515 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3518 discrim->hostmask_type = EXACT;
3519 } else if (!irccasecmp(argv[i], "subset")) {
3520 if (i == argc - 1) {
3521 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3524 discrim->hostmask_type = SUBSET;
3525 } else if (!irccasecmp(argv[i], "superset")) {
3526 if (i == argc - 1) {
3527 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3530 discrim->hostmask_type = SUPERSET;
3531 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3532 if (i == argc - 1) {
3533 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3536 discrim->hostmask_type = LASTQUIT;
3539 discrim->hostmask_type = SUPERSET;
3541 discrim->hostmask = argv[++i];
3542 } else if (!irccasecmp(argv[i], "fakehost")) {
3543 if (!irccasecmp(argv[++i], "*")) {
3544 discrim->fakehostmask = 0;
3546 discrim->fakehostmask = argv[i];
3548 } else if (!irccasecmp(argv[i], "fakeident")) {
3549 if (!irccasecmp(argv[++i], "*")) {
3550 discrim->fakeidentmask = 0;
3552 discrim->fakeidentmask = argv[i];
3554 } else if (!irccasecmp(argv[i], "website")) {
3555 if (!irccasecmp(argv[++i], "*")) {
3556 discrim->website = 0;
3558 discrim->website = argv[i];
3560 } else if (!irccasecmp(argv[i], "devnull")) {
3561 if (!irccasecmp(argv[++i], "*")) {
3562 discrim->devnullclass = 0;
3564 discrim->devnullclass = argv[i];
3566 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3567 if (!irccasecmp(argv[++i], "*")) {
3568 discrim->handlemask = 0;
3570 discrim->handlemask = argv[i];
3572 } else if (!irccasecmp(argv[i], "email")) {
3573 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3574 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3576 } else if (!irccasecmp(argv[++i], "*")) {
3577 discrim->emailmask = 0;
3579 discrim->emailmask = argv[i];
3581 } else if (!irccasecmp(argv[i], "access")) {
3582 const char *cmp = argv[++i];
3583 if (cmp[0] == '<') {
3584 if (discrim->min_level == 0) discrim->min_level = 1;
3585 if (cmp[1] == '=') {
3586 discrim->max_level = strtoul(cmp+2, NULL, 0);
3588 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3590 } else if (cmp[0] == '=') {
3591 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3592 } else if (cmp[0] == '>') {
3593 if (cmp[1] == '=') {
3594 discrim->min_level = strtoul(cmp+2, NULL, 0);
3596 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3599 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3601 } else if (!irccasecmp(argv[i], "karma")) {
3602 const char *cmp = argv[++i];
3603 if (cmp[0] == '<') {
3604 if (cmp[1] == '=') {
3605 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3607 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3609 } else if (cmp[0] == '=') {
3610 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3611 } else if (cmp[0] == '>') {
3612 if (cmp[1] == '=') {
3613 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3615 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3618 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3621 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3632 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3634 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3635 || (discrim->flags_off & hi->flags)
3636 || (discrim->min_registered > hi->registered)
3637 || (discrim->max_registered < hi->registered)
3638 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3639 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3640 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3641 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3642 || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website)))
3643 || (discrim->devnullclass && (!hi->devnull || !match_ircglob(hi->devnull, discrim->devnullclass)))
3644 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3645 || (discrim->min_level > hi->opserv_level)
3646 || (discrim->max_level < hi->opserv_level)
3647 || (discrim->min_karma > hi->karma)
3648 || (discrim->max_karma < hi->karma)
3652 if (discrim->hostmask) {
3654 for (i=0; i<hi->masks->used; i++) {
3655 const char *mask = hi->masks->list[i];
3656 if ((discrim->hostmask_type == SUBSET)
3657 && (match_ircglobs(discrim->hostmask, mask))) break;
3658 else if ((discrim->hostmask_type == EXACT)
3659 && !irccasecmp(discrim->hostmask, mask)) break;
3660 else if ((discrim->hostmask_type == SUPERSET)
3661 && (match_ircglobs(mask, discrim->hostmask))) break;
3662 else if ((discrim->hostmask_type == LASTQUIT)
3663 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3665 if (i==hi->masks->used) return 0;
3667 if (discrim->nickmask) {
3668 struct nick_info *nick = hi->nicks;
3670 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3673 if (!nick) return 0;
3679 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3681 dict_iterator_t it, next;
3682 unsigned int matched;
3684 for (it = dict_first(nickserv_handle_dict), matched = 0;
3685 it && (matched < discrim->limit);
3687 next = iter_next(it);
3688 if (nickserv_discrim_match(discrim, iter_data(it))) {
3689 dsf(source, iter_data(it));
3697 search_print_func(struct userNode *source, struct handle_info *match)
3699 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3703 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3708 search_unregister_func (struct userNode *source, struct handle_info *match)
3710 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3711 nickserv_unregister_handle(match, source);
3715 nickserv_sort_accounts_by_access(const void *a, const void *b)
3717 const struct handle_info *hi_a = *(const struct handle_info**)a;
3718 const struct handle_info *hi_b = *(const struct handle_info**)b;
3719 if (hi_a->opserv_level != hi_b->opserv_level)
3720 return hi_b->opserv_level - hi_a->opserv_level;
3721 return irccasecmp(hi_a->handle, hi_b->handle);
3725 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3727 struct handle_info_list hil;
3728 struct helpfile_table tbl;
3733 memset(&hil, 0, sizeof(hil));
3734 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3735 struct handle_info *hi = iter_data(it);
3736 if (hi->opserv_level)
3737 handle_info_list_append(&hil, hi);
3739 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3740 tbl.length = hil.used + 1;
3742 tbl.flags = TABLE_NO_FREE;
3743 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3744 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3747 for (ii = 0; ii < hil.used; ) {
3748 ary = malloc(tbl.width * sizeof(ary[0]));
3749 ary[0] = hil.list[ii]->handle;
3750 ary[1] = strtab(hil.list[ii]->opserv_level);
3751 tbl.contents[++ii] = ary;
3753 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3754 reply("MSG_MATCH_COUNT", hil.used);
3755 for (ii = 0; ii < hil.used; ii++)
3756 free(tbl.contents[ii]);
3761 static NICKSERV_FUNC(cmd_search)
3763 struct nickserv_discrim *discrim;
3764 discrim_search_func action;
3765 struct svccmd *subcmd;
3766 unsigned int matches;
3769 NICKSERV_MIN_PARMS(3);
3770 sprintf(buf, "search %s", argv[1]);
3771 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3772 if (!irccasecmp(argv[1], "print"))
3773 action = search_print_func;
3774 else if (!irccasecmp(argv[1], "count"))
3775 action = search_count_func;
3776 else if (!irccasecmp(argv[1], "unregister"))
3777 action = search_unregister_func;
3779 reply("NSMSG_INVALID_ACTION", argv[1]);
3783 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3786 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3790 if (action == search_print_func)
3791 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3792 else if (action == search_count_func)
3793 discrim->limit = INT_MAX;
3795 matches = nickserv_discrim_search(discrim, action, user);
3798 reply("MSG_MATCH_COUNT", matches);
3800 reply("MSG_NO_MATCHES");
3806 static MODCMD_FUNC(cmd_checkpass)
3808 struct handle_info *hi;
3810 NICKSERV_MIN_PARMS(3);
3811 if (!(hi = get_handle_info(argv[1]))) {
3812 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3815 if (checkpass(argv[2], hi->passwd))
3816 reply("CHECKPASS_YES");
3818 reply("CHECKPASS_NO");
3823 static MODCMD_FUNC(cmd_checkemail)
3825 struct handle_info *hi;
3827 NICKSERV_MIN_PARMS(3);
3828 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3831 if (!hi->email_addr)
3832 reply("CHECKEMAIL_NOT_SET");
3833 else if (!irccasecmp(argv[2], hi->email_addr))
3834 reply("CHECKEMAIL_YES");
3836 reply("CHECKEMAIL_NO");
3842 nickserv_db_read_handle(const char *handle, dict_t obj)
3845 struct string_list *masks, *slist;
3846 struct handle_info *hi;
3847 struct userNode *authed_users;
3848 struct userData *channel_list;
3853 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3854 id = str ? strtoul(str, NULL, 0) : 0;
3855 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3857 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3860 if ((hi = get_handle_info(handle))) {
3861 authed_users = hi->users;
3862 channel_list = hi->channels;
3864 hi->channels = NULL;
3865 dict_remove(nickserv_handle_dict, hi->handle);
3867 authed_users = NULL;
3868 channel_list = NULL;
3870 hi = register_handle(handle, str, id);
3872 hi->users = authed_users;
3873 while (authed_users) {
3874 authed_users->handle_info = hi;
3875 authed_users = authed_users->next_authed;
3878 hi->channels = channel_list;
3879 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3880 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3881 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3882 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3883 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3884 hi->language = language_find(str ? str : "C");
3885 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3886 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3887 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3889 hi->infoline = strdup(str);
3890 str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING);
3892 hi->website = strdup(str);
3893 str = database_get_data(obj, KEY_DEVNULL, RECDB_QSTRING);
3895 hi->devnull = strdup(str);
3896 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3897 hi->registered = str ? strtoul(str, NULL, 0) : now;
3898 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3899 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3900 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3901 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3902 /* We want to read the nicks even if disable_nicks is set. This is so
3903 * that we don't lose the nick data entirely. */
3904 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3906 for (ii=0; ii<slist->used; ii++)
3907 register_nick(slist->list[ii], hi);
3909 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3911 for (ii=0; str[ii]; ii++)
3912 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3914 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3915 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3916 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3917 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3918 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3919 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3920 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3922 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3924 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3925 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3927 nickserv_set_email_addr(hi, str);
3928 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3930 hi->epithet = strdup(str);
3931 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3933 hi->fakehost = strdup(str);
3934 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
3936 hi->fakeident = strdup(str);
3937 /* Read the "cookie" sub-database (if it exists). */
3938 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3940 const char *data, *type, *expires, *cookie_str;
3941 struct handle_cookie *cookie;
3943 cookie = calloc(1, sizeof(*cookie));
3944 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3945 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3946 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3947 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3948 if (!type || !expires || !cookie_str) {
3949 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3952 if (!irccasecmp(type, KEY_ACTIVATION))
3953 cookie->type = ACTIVATION;
3954 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3955 cookie->type = PASSWORD_CHANGE;
3956 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3957 cookie->type = EMAIL_CHANGE;
3958 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3959 cookie->type = ALLOWAUTH;
3961 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3964 cookie->expires = strtoul(expires, NULL, 0);
3965 if (cookie->expires < now)
3968 cookie->data = strdup(data);
3969 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3973 nickserv_bake_cookie(cookie);
3975 nickserv_free_cookie(cookie);
3977 /* Read the "notes" sub-database (if it exists). */
3978 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3981 struct handle_note *last_note;
3982 struct handle_note *note;
3985 for (it = dict_first(subdb); it; it = iter_next(it)) {
3986 const char *expires;
3990 const char *note_id;
3993 note_id = iter_key(it);
3994 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3996 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
3999 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
4000 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
4001 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
4002 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
4003 if (!setter || !text || !set) {
4004 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
4007 note = calloc(1, sizeof(*note) + strlen(text));
4009 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
4010 note->set = strtoul(set, NULL, 10);
4011 note->id = strtoul(note_id, NULL, 10);
4012 safestrncpy(note->setter, setter, sizeof(note->setter));
4013 strcpy(note->note, text);
4015 last_note->next = note;
4024 nickserv_saxdb_read(dict_t db) {
4026 struct record_data *rd;
4028 for (it=dict_first(db); it; it=iter_next(it)) {
4030 nickserv_db_read_handle(iter_key(it), rd->d.object);
4035 static NICKSERV_FUNC(cmd_mergedb)
4037 struct timeval start, stop;
4040 NICKSERV_MIN_PARMS(2);
4041 gettimeofday(&start, NULL);
4042 if (!(db = parse_database(argv[1]))) {
4043 reply("NSMSG_DB_UNREADABLE", argv[1]);
4046 nickserv_saxdb_read(db);
4048 gettimeofday(&stop, NULL);
4049 stop.tv_sec -= start.tv_sec;
4050 stop.tv_usec -= start.tv_usec;
4051 if (stop.tv_usec < 0) {
4053 stop.tv_usec += 1000000;
4055 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
4060 expire_handles(UNUSED_ARG(void *data))
4062 dict_iterator_t it, next;
4063 unsigned long expiry;
4064 struct handle_info *hi;
4066 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4067 next = iter_next(it);
4069 if ((hi->opserv_level > 0)
4071 || HANDLE_FLAGGED(hi, FROZEN)
4072 || HANDLE_FLAGGED(hi, NODELETE)) {
4075 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4076 if ((now - hi->lastseen) > expiry) {
4077 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
4078 nickserv_unregister_handle(hi, NULL);
4082 if (nickserv_conf.handle_expire_frequency)
4083 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4087 nickserv_load_dict(const char *fname)
4091 if (!(file = fopen(fname, "r"))) {
4092 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4095 while (fgets(line, sizeof(line), file)) {
4098 if (line[strlen(line)-1] == '\n')
4099 line[strlen(line)-1] = 0;
4100 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4103 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4106 static enum reclaim_action
4107 reclaim_action_from_string(const char *str) {
4109 return RECLAIM_NONE;
4110 else if (!irccasecmp(str, "warn"))
4111 return RECLAIM_WARN;
4112 else if (!irccasecmp(str, "svsnick"))
4113 return RECLAIM_SVSNICK;
4114 else if (!irccasecmp(str, "kill"))
4115 return RECLAIM_KILL;
4117 return RECLAIM_NONE;
4121 nickserv_conf_read(void)
4123 dict_t conf_node, child;
4127 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4128 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4131 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4133 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4134 if (nickserv_conf.valid_handle_regex_set)
4135 regfree(&nickserv_conf.valid_handle_regex);
4137 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4138 nickserv_conf.valid_handle_regex_set = !err;
4139 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4141 nickserv_conf.valid_handle_regex_set = 0;
4143 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4144 if (nickserv_conf.valid_nick_regex_set)
4145 regfree(&nickserv_conf.valid_nick_regex);
4147 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4148 nickserv_conf.valid_nick_regex_set = !err;
4149 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4151 nickserv_conf.valid_nick_regex_set = 0;
4153 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4155 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4156 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4157 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4158 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4159 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4160 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4161 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4162 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4163 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4164 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4165 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4166 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4167 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4168 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4169 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4170 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4171 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4172 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4173 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4174 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4175 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4176 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4177 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4178 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4179 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
4180 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
4181 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4183 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4184 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4185 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4187 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4188 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4189 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4191 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4192 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4193 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4194 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4195 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4196 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4197 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4198 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4199 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4200 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4201 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4204 nickserv_conf.ounregister_flags = 0;
4206 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4209 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4211 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4213 nickserv_conf.handle_ts_mode = TS_IGNORE;
4214 else if (!irccasecmp(str, "ircu"))
4215 nickserv_conf.handle_ts_mode = TS_IRCU;
4217 nickserv_conf.handle_ts_mode = TS_IGNORE;
4218 if (!nickserv_conf.disable_nicks) {
4219 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4220 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4221 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4222 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4223 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4224 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4225 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4226 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4228 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4229 for (it=dict_first(child); it; it=iter_next(it)) {
4230 const char *key = iter_key(it), *value;
4234 if (!strncasecmp(key, "uc_", 3))
4235 flag = toupper(key[3]);
4236 else if (!strncasecmp(key, "lc_", 3))
4237 flag = tolower(key[3]);
4241 if ((pos = handle_inverse_flags[flag])) {
4242 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4243 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4246 if (nickserv_conf.weak_password_dict)
4247 dict_delete(nickserv_conf.weak_password_dict);
4248 nickserv_conf.weak_password_dict = dict_new();
4249 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4250 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4251 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4252 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4254 nickserv_load_dict(str);
4255 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4256 if (nickserv && str)
4257 NickChange(nickserv, str, 0);
4258 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4259 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4260 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4261 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4262 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4263 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4264 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4265 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4266 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4267 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4268 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4269 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4270 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4271 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4272 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4273 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4274 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4275 titlehost_suffix = str ? str : "example.net";
4276 str = conf_get_data("server/network", RECDB_QSTRING);
4277 nickserv_conf.network_name = str ? str : "some IRC network";
4278 if (!nickserv_conf.auth_policer_params) {
4279 nickserv_conf.auth_policer_params = policer_params_new();
4280 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4281 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4283 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4284 for (it=dict_first(child); it; it=iter_next(it))
4285 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4289 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4291 char newnick[NICKLEN+1];
4300 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4302 case RECLAIM_SVSNICK:
4304 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4305 } while (GetUserH(newnick));
4306 irc_svsnick(nickserv, user, newnick);
4309 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4310 DelUser(user, nickserv, 1, msg);
4316 nickserv_reclaim_p(void *data) {
4317 struct userNode *user = data;
4318 struct nick_info *ni = get_nick_info(user->nick);
4320 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4324 check_user_nick(struct userNode *user) {
4325 struct nick_info *ni;
4326 user->modes &= ~FLAGS_REGNICK;
4327 if (!(ni = get_nick_info(user->nick)))
4329 if (user->handle_info == ni->owner) {
4330 user->modes |= FLAGS_REGNICK;
4334 if (nickserv_conf.warn_nick_owned)
4335 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4336 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4338 if (nickserv_conf.auto_reclaim_delay)
4339 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4341 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4345 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4347 struct handle_info *hi = NULL;
4350 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4351 if ((hi == NULL) && (serial != 0)) {
4353 inttobase64(id, serial, IDLEN);
4354 hi = dict_find(nickserv_id_dict, id, NULL);
4358 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4359 && (timestamp != hi->registered)) {
4362 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4365 set_user_handle_info(user, hi, 0);
4367 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4372 handle_nick_change(struct userNode *user, const char *old_nick)
4374 struct handle_info *hi;
4376 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4377 dict_remove(nickserv_allow_auth_dict, old_nick);
4378 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4380 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4381 check_user_nick(user);
4385 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4387 dict_remove(nickserv_allow_auth_dict, user->nick);
4388 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4389 set_user_handle_info(user, NULL, 0);
4392 static struct modcmd *
4393 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4395 if (min_level > 0) {
4397 sprintf(buf, "%u", min_level);
4398 if (must_be_qualified) {
4399 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4401 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4403 } else if (min_level == 0) {
4404 if (must_be_qualified) {
4405 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4407 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4410 if (must_be_qualified) {
4411 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4413 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4419 nickserv_db_cleanup(void)
4421 unreg_del_user_func(nickserv_remove_user);
4422 userList_clean(&curr_helpers);
4423 policer_params_delete(nickserv_conf.auth_policer_params);
4424 dict_delete(nickserv_handle_dict);
4425 dict_delete(nickserv_nick_dict);
4426 dict_delete(nickserv_opt_dict);
4427 dict_delete(nickserv_allow_auth_dict);
4428 dict_delete(nickserv_email_dict);
4429 dict_delete(nickserv_id_dict);
4430 dict_delete(nickserv_conf.weak_password_dict);
4431 free(auth_func_list);
4432 free(unreg_func_list);
4434 free(allowauth_func_list);
4435 free(handle_merge_func_list);
4436 free(failpw_func_list);
4437 if (nickserv_conf.valid_handle_regex_set)
4438 regfree(&nickserv_conf.valid_handle_regex);
4439 if (nickserv_conf.valid_nick_regex_set)
4440 regfree(&nickserv_conf.valid_nick_regex);
4444 init_nickserv(const char *nick)
4447 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4448 reg_new_user_func(check_user_nick);
4449 reg_nick_change_func(handle_nick_change);
4450 reg_del_user_func(nickserv_remove_user);
4451 reg_account_func(handle_account);
4453 /* set up handle_inverse_flags */
4454 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4455 for (i=0; handle_flags[i]; i++) {
4456 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4457 flag_access_levels[i] = 0;
4460 conf_register_reload(nickserv_conf_read);
4461 nickserv_opt_dict = dict_new();
4462 nickserv_email_dict = dict_new();
4463 dict_set_free_keys(nickserv_email_dict, free);
4464 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4466 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4467 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4468 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4469 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4470 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4471 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4472 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4473 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4474 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4475 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4476 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4477 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4478 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4479 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4480 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4481 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4482 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4483 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4484 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4485 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4486 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4487 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4488 if (!nickserv_conf.disable_nicks) {
4489 /* nick management commands */
4490 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4491 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4492 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4493 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4494 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4495 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4497 if (nickserv_conf.email_enabled) {
4498 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4499 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4500 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4501 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4502 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4503 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4505 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4506 /* miscellaneous commands */
4507 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4508 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4509 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4510 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4511 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4512 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4514 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4515 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4516 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4517 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4518 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4519 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4520 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
4521 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4522 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4523 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4524 dict_insert(nickserv_opt_dict, "WEBSITE", opt_website);
4525 dict_insert(nickserv_opt_dict, "DEVNULL", opt_devnull);
4526 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4527 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4528 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4529 if (titlehost_suffix) {
4530 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4531 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4532 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4534 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4535 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4536 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4537 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4539 nickserv_handle_dict = dict_new();
4540 dict_set_free_keys(nickserv_handle_dict, free);
4541 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4543 nickserv_id_dict = dict_new();
4544 dict_set_free_keys(nickserv_id_dict, free);
4546 nickserv_nick_dict = dict_new();
4547 dict_set_free_data(nickserv_nick_dict, free);
4549 nickserv_allow_auth_dict = dict_new();
4551 userList_init(&curr_helpers);
4554 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4555 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4556 nickserv_service = service_register(nickserv);
4558 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4559 reg_exit_func(nickserv_db_cleanup);
4560 if(nickserv_conf.handle_expire_frequency)
4561 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4562 message_register_table(msgtab);