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_FLAGS", " Flags: %s" },
220 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
221 { "NSMSG_HANDLEINFO_FAKEIDENT", " Fake ident: %s" },
222 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
223 { "NSMSG_HANDLEINFO_FAKEIDENTHOST", " Fake host: %s@%s" },
224 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
225 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
226 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
227 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
228 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
229 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
230 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
231 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
232 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
233 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
234 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
235 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
236 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
237 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
238 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
239 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
240 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
241 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
242 { "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)." },
243 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
244 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
245 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
246 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
247 { "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." },
248 { "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." },
249 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
250 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
251 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
252 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
253 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
254 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
255 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
256 { "NSMSG_PASS_SUCCESS", "Password changed." },
257 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
258 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
259 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
260 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
261 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
262 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
263 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
264 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
265 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
266 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
267 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
268 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
269 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
270 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
271 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
272 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
273 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
274 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
275 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
276 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
277 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
278 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
279 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
280 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
281 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
282 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
283 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
284 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
285 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
286 { "NSMSG_NO_ACCESS", "Access denied." },
287 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
288 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
289 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
290 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
291 { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
292 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
293 { "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." },
294 { "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." },
295 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
296 { "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." },
297 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
298 { "NSMSG_SEARCH_MATCH", "Match: %s" },
299 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
300 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
301 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
302 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
303 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
304 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
305 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
306 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
307 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
308 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
309 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
310 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
311 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
312 { "NSMSG_SET_DEVNULL", "$bDEVNULL: $b%s" },
313 { "NSMSG_SET_AUTOHIDE", "$bAUTOHIDE: $b%s" },
314 { "NSMSG_SET_WEBSITE", "$bWEBSITE: $b%s" },
315 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
316 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
317 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
318 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
319 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
320 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
321 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
322 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
323 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
324 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
325 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
326 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
327 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
328 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
329 { "NSMSG_SET_FAKEIDENT", "$bFAKEIDENT: $b%s" },
330 { "NSMSG_SET_FAKEIDENTHOST", "$bFAKEHOST: $b%s@%s" },
331 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
332 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
333 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
334 { "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." },
335 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
336 { "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." },
337 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
338 { "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." },
339 { "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." },
340 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
341 { "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." },
342 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
343 { "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." },
344 { "CHECKPASS_YES", "Yes." },
345 { "CHECKPASS_NO", "No." },
346 { "CHECKEMAIL_NOT_SET", "No email set." },
347 { "CHECKEMAIL_YES", "Yes." },
348 { "CHECKEMAIL_NO", "No." },
352 enum reclaim_action {
358 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
359 static void nickserv_reclaim_p(void *data);
360 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
362 enum handle_ts_mode {
368 unsigned int disable_nicks : 1;
369 unsigned int valid_handle_regex_set : 1;
370 unsigned int valid_nick_regex_set : 1;
371 unsigned int autogag_enabled : 1;
372 unsigned int email_enabled : 1;
373 unsigned int email_required : 1;
374 unsigned int default_hostmask : 1;
375 unsigned int warn_nick_owned : 1;
376 unsigned int warn_clone_auth : 1;
377 unsigned long nicks_per_handle;
378 unsigned long password_min_length;
379 unsigned long password_min_digits;
380 unsigned long password_min_upper;
381 unsigned long password_min_lower;
382 unsigned long db_backup_frequency;
383 unsigned long handle_expire_frequency;
384 unsigned long autogag_duration;
385 unsigned long email_visible_level;
386 unsigned long cookie_timeout;
387 unsigned long handle_expire_delay;
388 unsigned long nochan_handle_expire_delay;
389 unsigned long modoper_level;
390 unsigned long set_epithet_level;
391 unsigned long set_title_level;
392 unsigned long set_fakehost_level;
393 unsigned long set_fakeident_level;
394 unsigned long handles_per_email;
395 unsigned long email_search_level;
396 const char *network_name;
397 regex_t valid_handle_regex;
398 regex_t valid_nick_regex;
399 dict_t weak_password_dict;
400 struct policer_params *auth_policer_params;
401 enum reclaim_action reclaim_action;
402 enum reclaim_action auto_reclaim_action;
403 enum handle_ts_mode handle_ts_mode;
404 unsigned long auto_reclaim_delay;
405 unsigned char default_maxlogins;
406 unsigned char hard_maxlogins;
407 unsigned long ounregister_inactive;
408 unsigned long ounregister_flags;
411 const char *titlehost_suffix = NULL;
413 /* We have 2^32 unique account IDs to use. */
414 unsigned long int highest_id = 0;
416 #define WALK_NOTES(HANDLE, PREV, NOTE) \
417 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
418 if (NOTE->expires && NOTE->expires < now) { \
419 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
421 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
425 canonicalize_hostmask(char *mask)
427 char *out = mask, *temp;
428 if ((temp = strchr(mask, '!'))) {
430 while (*temp) *out++ = *temp++;
436 static struct handle_info *
437 register_handle(const char *handle, const char *passwd, unsigned long id)
439 struct handle_info *hi;
441 char id_base64[IDLEN + 1];
444 /* Assign a unique account ID to the account; note that 0 is
445 an invalid account ID. 1 is therefore the first account ID. */
447 id = 1 + highest_id++;
449 /* Note: highest_id is and must always be the highest ID. */
450 if (id > highest_id) {
454 inttobase64(id_base64, id, IDLEN);
456 /* Make sure an account with the same ID doesn't exist. If a
457 duplicate is found, log some details and assign a new one.
458 This should be impossible, but it never hurts to expect it. */
459 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
460 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
465 hi = calloc(1, sizeof(*hi));
466 hi->userlist_style = HI_DEFAULT_STYLE;
467 hi->handle = strdup(handle);
468 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
470 dict_insert(nickserv_handle_dict, hi->handle, hi);
475 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
481 register_nick(const char *nick, struct handle_info *owner)
483 struct nick_info *ni;
484 ni = malloc(sizeof(struct nick_info));
485 safestrncpy(ni->nick, nick, sizeof(ni->nick));
487 ni->next = owner->nicks;
489 dict_insert(nickserv_nick_dict, ni->nick, ni);
493 delete_nick(struct nick_info *ni)
495 struct nick_info *last, *next;
496 struct userNode *user;
497 /* Check to see if we should mark a user as unregistered. */
498 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
499 user->modes &= ~FLAGS_REGNICK;
502 /* Remove ni from the nick_info linked list. */
503 if (ni == ni->owner->nicks) {
504 ni->owner->nicks = ni->next;
506 last = ni->owner->nicks;
512 last->next = next->next;
514 dict_remove(nickserv_nick_dict, ni->nick);
517 static unreg_func_t *unreg_func_list;
518 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
521 reg_unreg_func(unreg_func_t func)
523 if (unreg_func_used == unreg_func_size) {
524 if (unreg_func_size) {
525 unreg_func_size <<= 1;
526 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
529 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
532 unreg_func_list[unreg_func_used++] = func;
536 nickserv_free_cookie(void *data)
538 struct handle_cookie *cookie = data;
539 if (cookie->hi) cookie->hi->cookie = NULL;
540 if (cookie->data) free(cookie->data);
545 free_handle_info(void *vhi)
547 struct handle_info *hi = vhi;
550 inttobase64(id, hi->id, IDLEN);
551 dict_remove(nickserv_id_dict, id);
553 free_string_list(hi->masks);
557 delete_nick(hi->nicks);
565 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
566 nickserv_free_cookie(hi->cookie);
569 struct handle_note *note = hi->notes;
570 hi->notes = note->next;
573 if (hi->email_addr) {
574 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
575 handle_info_list_remove(hil, hi);
577 dict_remove(nickserv_email_dict, hi->email_addr);
582 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
585 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
589 for (n=0; n<unreg_func_used; n++)
590 unreg_func_list[n](notify, hi);
592 set_user_handle_info(hi->users, NULL, 0);
594 if (nickserv_conf.disable_nicks)
595 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
597 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
599 dict_remove(nickserv_handle_dict, hi->handle);
603 get_handle_info(const char *handle)
605 return dict_find(nickserv_handle_dict, handle, 0);
609 get_nick_info(const char *nick)
611 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
615 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
620 for (nn=0; nn<channel->members.used; ++nn) {
621 mn = channel->members.list[nn];
622 if ((mn->user != except) && (mn->user->handle_info == handle))
629 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
630 if (!user->handle_info) {
632 send_message(user, bot, "MSG_AUTHENTICATE");
636 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
638 send_message(user, bot, "NSMSG_NO_ACCESS");
642 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
644 send_message(user, bot, "MSG_OPER_SUSPENDED");
648 if (user->handle_info->opserv_level < min_level) {
650 send_message(user, bot, "NSMSG_NO_ACCESS");
658 is_valid_handle(const char *handle)
660 struct userNode *user;
661 /* cant register a juped nick/service nick as handle, to prevent confusion */
662 user = GetUserH(handle);
663 if (user && IsLocal(user))
665 /* check against maximum length */
666 if (strlen(handle) > NICKSERV_HANDLE_LEN)
668 /* for consistency, only allow account names that could be nicks */
669 if (!is_valid_nick(handle))
671 /* disallow account names that look like bad words */
672 if (opserv_bad_channel(handle))
674 /* test either regex or containing all valid chars */
675 if (nickserv_conf.valid_handle_regex_set) {
676 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
679 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
680 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
684 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
689 is_registerable_nick(const char *nick)
691 /* make sure it could be used as an account name */
692 if (!is_valid_handle(nick))
695 if (strlen(nick) > NICKLEN)
697 /* test either regex or as valid handle */
698 if (nickserv_conf.valid_nick_regex_set) {
699 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
702 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
703 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
711 is_valid_email_addr(const char *email)
713 return strchr(email, '@') != NULL;
717 visible_email_addr(struct userNode *user, struct handle_info *hi)
719 if (hi->email_addr) {
720 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
721 return hi->email_addr;
731 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
733 struct handle_info *hi;
734 struct userNode *target;
738 if (!(hi = get_handle_info(++name))) {
739 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
744 if (!(target = GetUserH(name))) {
745 send_message(user, service, "MSG_NICK_UNKNOWN", name);
748 if (IsLocal(target)) {
749 if (IsService(target))
750 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
752 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
755 if (!(hi = target->handle_info)) {
756 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
764 oper_outranks(struct userNode *user, struct handle_info *hi) {
765 if (user->handle_info->opserv_level > hi->opserv_level)
767 if (user->handle_info->opserv_level == hi->opserv_level) {
768 if ((user->handle_info->opserv_level == 1000)
769 || (user->handle_info == hi)
770 || ((user->handle_info->opserv_level == 0)
771 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
772 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
776 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
780 static struct handle_info *
781 get_victim_oper(struct userNode *user, const char *target)
783 struct handle_info *hi;
784 if (!(hi = smart_get_handle_info(nickserv, user, target)))
786 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
787 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
790 return oper_outranks(user, hi) ? hi : NULL;
794 valid_user_for(struct userNode *user, struct handle_info *hi)
798 /* If no hostmasks on the account, allow it. */
799 if (!hi->masks->used)
801 /* If any hostmask matches, allow it. */
802 for (ii=0; ii<hi->masks->used; ii++)
803 if (user_matches_glob(user, hi->masks->list[ii], 0))
805 /* If they are allowauthed to this account, allow it (removing the aa). */
806 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
807 dict_remove(nickserv_allow_auth_dict, user->nick);
810 /* The user is not allowed to use this account. */
815 is_secure_password(const char *handle, const char *pass, struct userNode *user)
818 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
822 if (len < nickserv_conf.password_min_length) {
824 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
827 if (!irccasecmp(pass, handle)) {
829 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
832 dict_find(nickserv_conf.weak_password_dict, pass, &p);
835 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
838 for (i=0; i<len; i++) {
839 if (isdigit(pass[i]))
841 if (isupper(pass[i]))
843 if (islower(pass[i]))
846 if ((cnt_lower < nickserv_conf.password_min_lower)
847 || (cnt_upper < nickserv_conf.password_min_upper)
848 || (cnt_digits < nickserv_conf.password_min_digits)) {
850 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
856 static auth_func_t *auth_func_list;
857 static unsigned int auth_func_size = 0, auth_func_used = 0;
860 reg_auth_func(auth_func_t func)
862 if (auth_func_used == auth_func_size) {
863 if (auth_func_size) {
864 auth_func_size <<= 1;
865 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
868 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
871 auth_func_list[auth_func_used++] = func;
874 static handle_rename_func_t *rf_list;
875 static unsigned int rf_list_size, rf_list_used;
878 reg_handle_rename_func(handle_rename_func_t func)
880 if (rf_list_used == rf_list_size) {
883 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
886 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
889 rf_list[rf_list_used++] = func;
893 generate_fakehost(struct handle_info *handle)
895 extern const char *hidden_host_suffix;
896 static char buffer[HOSTLEN+1];
898 if (!handle->fakehost) {
899 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
901 } else if (handle->fakehost[0] == '.') {
902 /* A leading dot indicates the stored value is actually a title. */
903 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, titlehost_suffix);
905 } else if (handle->fakehost[0] == '$') {
906 /* A leading $ indicates the stored value begins with the user handle. */
907 snprintf(buffer, sizeof(buffer), "%s%s", handle->handle, handle->fakehost+1);
910 return handle->fakehost;
914 generate_fakeident(struct handle_info *handle, struct userNode *user)
916 static char buffer[USERLEN+1];
918 if (!handle->fakeident) {
921 safestrncpy(buffer, user->ident, sizeof(buffer));
924 return handle->fakeident;
928 apply_fakehost(struct handle_info *handle, struct userNode *user)
930 struct userNode *target;
931 char *fakehost, *fakeident;
936 fakehost = generate_fakehost(handle);
939 fakeident = generate_fakeident(handle, user);
940 assign_fakehost(user, fakehost, fakeident, 0, 1);
944 for (target = handle->users; target; target = target->next_authed) {
945 fakeident = generate_fakeident(handle, target);
946 assign_fakehost(target, fakehost, fakeident, 0, 1);
951 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
954 struct handle_info *old_info;
956 /* This can happen if somebody uses COOKIE while authed, or if
957 * they re-auth to their current handle (which is silly, but users
959 if (user->handle_info == hi)
962 if (user->handle_info) {
963 struct userNode *other;
966 userList_remove(&curr_helpers, user);
968 /* remove from next_authed linked list */
969 if (user->handle_info->users == user) {
970 user->handle_info->users = user->next_authed;
971 } else if (user->handle_info->users != NULL) {
972 for (other = user->handle_info->users;
973 other->next_authed != user;
974 other = other->next_authed) ;
975 other->next_authed = user->next_authed;
977 /* No users authed to the account - can happen if they get
978 * killed for authing. */
980 /* if nobody left on old handle, and they're not an oper, remove !god */
981 if (!user->handle_info->users && !user->handle_info->opserv_level)
982 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
983 /* record them as being last seen at this time */
984 user->handle_info->lastseen = now;
985 /* and record their hostmask */
986 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
988 old_info = user->handle_info;
989 user->handle_info = hi;
990 if (hi && !hi->users && !hi->opserv_level)
991 HANDLE_CLEAR_FLAG(hi, HELPING);
992 for (n=0; n<auth_func_used; n++) {
993 auth_func_list[n](user, old_info);
998 struct nick_info *ni;
1000 HANDLE_CLEAR_FLAG(hi, FROZEN);
1001 if (nickserv_conf.warn_clone_auth) {
1002 struct userNode *other;
1003 for (other = hi->users; other; other = other->next_authed)
1004 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
1006 user->next_authed = hi->users;
1009 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
1010 userList_append(&curr_helpers, user);
1012 if (hi->fakehost || hi->fakeident || old_info)
1013 apply_fakehost(hi, user);
1016 if (!nickserv_conf.disable_nicks) {
1017 struct nick_info *ni2;
1018 for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
1019 if (!irccasecmp(user->nick, ni2->nick)) {
1020 user->modes |= FLAGS_REGNICK;
1025 StampUser(user, hi->handle, hi->registered, hi->id);
1028 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
1029 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
1031 /* We cannot clear the user's account ID, unfortunately. */
1032 user->next_authed = NULL;
1036 static struct handle_info*
1037 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
1039 struct handle_info *hi;
1040 struct nick_info *ni;
1041 char crypted[MD5_CRYPT_LENGTH];
1043 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
1044 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
1048 if (!is_secure_password(handle, passwd, user))
1051 cryptpass(passwd, crypted);
1052 hi = register_handle(handle, crypted, 0);
1053 hi->masks = alloc_string_list(1);
1055 hi->language = lang_C;
1056 hi->registered = now;
1058 hi->flags = HI_DEFAULT_FLAGS;
1059 if (settee && !no_auth)
1060 set_user_handle_info(settee, hi, 1);
1063 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1064 else if (nickserv_conf.disable_nicks)
1065 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1066 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1067 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1069 register_nick(user->nick, hi);
1070 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1072 if (settee && (user != settee))
1073 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1078 nickserv_bake_cookie(struct handle_cookie *cookie)
1080 cookie->hi->cookie = cookie;
1081 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1085 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1087 struct handle_cookie *cookie;
1088 char subject[128], body[4096], *misc;
1089 const char *netname, *fmt;
1093 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1097 cookie = calloc(1, sizeof(*cookie));
1099 cookie->type = type;
1100 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1101 cookie->expires = now + nickserv_conf.cookie_timeout;
1102 inttobase64(cookie->cookie, rand(), 5);
1103 inttobase64(cookie->cookie+5, rand(), 5);
1105 netname = nickserv_conf.network_name;
1108 switch (cookie->type) {
1110 hi->passwd[0] = 0; /* invalidate password */
1111 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1112 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1113 snprintf(subject, sizeof(subject), fmt, netname);
1114 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1115 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1118 case PASSWORD_CHANGE:
1119 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1120 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1121 snprintf(subject, sizeof(subject), fmt, netname);
1122 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1123 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1126 misc = hi->email_addr;
1127 hi->email_addr = cookie->data;
1129 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1130 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1131 snprintf(subject, sizeof(subject), fmt, netname);
1132 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1133 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1134 mail_send(nickserv, hi, subject, body, 1);
1135 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1136 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1138 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1139 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1140 snprintf(subject, sizeof(subject), fmt, netname);
1141 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1142 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1143 mail_send(nickserv, hi, subject, body, 1);
1146 hi->email_addr = misc;
1149 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1150 snprintf(subject, sizeof(subject), fmt, netname);
1151 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1152 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1153 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1156 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1160 mail_send(nickserv, hi, subject, body, first_time);
1161 nickserv_bake_cookie(cookie);
1165 nickserv_eat_cookie(struct handle_cookie *cookie)
1167 cookie->hi->cookie = NULL;
1168 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1169 nickserv_free_cookie(cookie);
1173 nickserv_free_email_addr(void *data)
1175 handle_info_list_clean(data);
1180 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1182 struct handle_info_list *hil;
1183 /* Remove from old handle_info_list ... */
1184 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1185 handle_info_list_remove(hil, hi);
1186 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1187 hi->email_addr = NULL;
1189 /* Add to the new list.. */
1190 if (new_email_addr) {
1191 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1192 hil = calloc(1, sizeof(*hil));
1193 hil->tag = strdup(new_email_addr);
1194 handle_info_list_init(hil);
1195 dict_insert(nickserv_email_dict, hil->tag, hil);
1197 handle_info_list_append(hil, hi);
1198 hi->email_addr = hil->tag;
1202 static NICKSERV_FUNC(cmd_register)
1205 struct handle_info *hi;
1206 const char *email_addr, *password;
1209 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1210 /* Require the first handle registered to belong to someone +o. */
1211 reply("NSMSG_REQUIRE_OPER");
1215 if (user->handle_info) {
1216 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1220 if (IsRegistering(user)) {
1221 reply("NSMSG_ALREADY_REGISTERING");
1225 if (IsStamped(user)) {
1226 /* Unauthenticated users might still have been stamped
1227 previously and could therefore have a hidden host;
1228 do not allow them to register a new account. */
1229 reply("NSMSG_STAMPED_REGISTER");
1233 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1235 if (!is_valid_handle(argv[1])) {
1236 reply("NSMSG_BAD_HANDLE", argv[1]);
1240 if ((argc >= 4) && nickserv_conf.email_enabled) {
1241 struct handle_info_list *hil;
1244 /* Remember email address. */
1245 email_addr = argv[3];
1247 /* Check that the email address looks valid.. */
1248 if (!is_valid_email_addr(email_addr)) {
1249 reply("NSMSG_BAD_EMAIL_ADDR");
1253 /* .. and that we are allowed to send to it. */
1254 if ((str = mail_prohibited_address(email_addr))) {
1255 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1259 /* If we do email verify, make sure we don't spam the address. */
1260 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1262 for (nn=0; nn<hil->used; nn++) {
1263 if (hil->list[nn]->cookie) {
1264 reply("NSMSG_EMAIL_UNACTIVATED");
1268 if (hil->used >= nickserv_conf.handles_per_email) {
1269 reply("NSMSG_EMAIL_OVERUSED");
1282 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1284 /* Add any masks they should get. */
1285 if (nickserv_conf.default_hostmask) {
1286 string_list_append(hi->masks, strdup("*@*"));
1288 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1289 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1290 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1293 /* If they're the first to register, give them level 1000. */
1294 if (dict_size(nickserv_handle_dict) == 1) {
1295 hi->opserv_level = 1000;
1296 reply("NSMSG_ROOT_HANDLE", argv[1]);
1299 /* Set their email address. */
1301 nickserv_set_email_addr(hi, email_addr);
1303 /* If they need to do email verification, tell them. */
1305 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1307 /* Set registering flag.. */
1308 user->modes |= FLAGS_REGISTERING;
1313 static NICKSERV_FUNC(cmd_oregister)
1316 struct userNode *settee;
1317 struct handle_info *hi;
1318 const char *pass, *email;
1320 NICKSERV_MIN_PARMS(3);
1325 if (!is_valid_handle(argv[1])) {
1326 reply("NSMSG_BAD_HANDLE", argv[1]);
1330 if (argc < 5 || !nickserv_conf.email_enabled) {
1335 if (!is_valid_email_addr(email)) {
1336 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
1339 if ((str = mail_prohibited_address(email))) {
1340 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", email, str);
1345 if (argc < 4 || !strcmp(argv[3], "*")) {
1348 } else if (strchr(argv[3], '@')) {
1349 mask = canonicalize_hostmask(strdup(argv[3]));
1351 settee = GetUserH(argv[4]);
1353 reply("MSG_NICK_UNKNOWN", argv[4]);
1360 } else if ((settee = GetUserH(argv[3]))) {
1361 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1363 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1366 if (settee && settee->handle_info) {
1367 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1371 if (!(hi = nickserv_register(user, settee, argv[1], pass, 0))) {
1376 string_list_append(hi->masks, mask);
1378 nickserv_set_email_addr(hi, email);
1382 static NICKSERV_FUNC(cmd_handleinfo)
1385 unsigned int i, pos=0, herelen;
1386 struct userNode *target, *next_un;
1387 struct handle_info *hi;
1388 const char *nsmsg_none;
1392 if (!(hi = user->handle_info)) {
1393 reply("NSMSG_MUST_AUTH");
1396 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1400 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1401 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1402 feh = hi->registered;
1403 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1406 intervalString(buff, now - hi->lastseen, user->handle_info);
1407 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1409 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1412 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1413 if (oper_has_access(user, cmd->parent->bot, 200, 1))
1414 reply("NSMSG_HANDLEINFO_DEVNULL", (hi->devnull ? hi->devnull : nsmsg_none));
1415 if (user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1416 reply("NSMSG_HANDLEINFO_WEBSITE", (hi->website ? hi->website : nsmsg_none));
1417 if (HANDLE_FLAGGED(hi, FROZEN))
1418 reply("NSMSG_HANDLEINFO_VACATION");
1420 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1421 struct do_not_register *dnr;
1422 if ((dnr = chanserv_is_dnr(NULL, hi)))
1423 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1424 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1426 } else if (hi != user->handle_info)
1430 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1432 if (nickserv_conf.email_enabled)
1433 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1437 switch (hi->cookie->type) {
1438 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1439 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1440 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1441 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1442 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1447 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1448 reply("NSMSG_HANDLEINFO_ID", hi->id);
1450 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1452 reply("NSMSG_HANDLEINFO_NO_NOTES");
1454 struct handle_note *prev, *note;
1456 WALK_NOTES(hi, prev, note) {
1457 char set_time[INTERVALLEN];
1458 intervalString(set_time, now - note->set, user->handle_info);
1459 if (note->expires) {
1460 char exp_time[INTERVALLEN];
1461 intervalString(exp_time, note->expires - now, user->handle_info);
1462 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1464 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1471 unsigned long flen = 1;
1472 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1474 for (i=0, flen=1; handle_flags[i]; i++)
1475 if (hi->flags & 1 << i)
1476 flags[flen++] = handle_flags[i];
1478 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1480 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1483 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1484 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1485 || (hi->opserv_level > 0)) {
1486 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1489 if (hi->fakeident && hi->fakehost)
1490 reply("NSMSG_HANDLEINFO_FAKEIDENTHOST", hi->fakeident, hi->fakehost);
1491 else if (hi->fakeident)
1492 reply("NSMSG_HANDLEINFO_FAKEIDENT", hi->fakeident);
1493 else if (hi->fakehost)
1494 reply("NSMSG_HANDLEINFO_FAKEHOST", hi->fakehost);
1496 if (hi->last_quit_host[0])
1497 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1499 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1501 if (nickserv_conf.disable_nicks) {
1502 /* nicks disabled; don't show anything about registered nicks */
1503 } else if (hi->nicks) {
1504 struct nick_info *ni, *next_ni;
1505 for (ni = hi->nicks; ni; ni = next_ni) {
1506 herelen = strlen(ni->nick);
1507 if (pos + herelen + 1 > ArrayLength(buff)) {
1509 goto print_nicks_buff;
1513 memcpy(buff+pos, ni->nick, herelen);
1514 pos += herelen; buff[pos++] = ' ';
1518 reply("NSMSG_HANDLEINFO_NICKS", buff);
1523 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1526 if (hi->masks->used) {
1527 for (i=0; i < hi->masks->used; i++) {
1528 herelen = strlen(hi->masks->list[i]);
1529 if (pos + herelen + 1 > ArrayLength(buff)) {
1531 goto print_mask_buff;
1533 memcpy(buff+pos, hi->masks->list[i], herelen);
1534 pos += herelen; buff[pos++] = ' ';
1535 if (i+1 == hi->masks->used) {
1538 reply("NSMSG_HANDLEINFO_MASKS", buff);
1543 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1547 struct userData *chan, *next;
1550 for (chan = hi->channels; chan; chan = next) {
1551 next = chan->u_next;
1552 name = chan->channel->channel->name;
1553 herelen = strlen(name);
1554 if (pos + herelen + 7 > ArrayLength(buff)) {
1556 goto print_chans_buff;
1558 if (IsUserSuspended(chan))
1560 pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
1564 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1569 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1572 for (target = hi->users; target; target = next_un) {
1573 herelen = strlen(target->nick);
1574 if (pos + herelen + 1 > ArrayLength(buff)) {
1576 goto print_cnick_buff;
1578 next_un = target->next_authed;
1580 memcpy(buff+pos, target->nick, herelen);
1581 pos += herelen; buff[pos++] = ' ';
1585 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1590 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1593 static NICKSERV_FUNC(cmd_userinfo)
1595 struct userNode *target;
1597 NICKSERV_MIN_PARMS(2);
1598 if (!(target = GetUserH(argv[1]))) {
1599 reply("MSG_NICK_UNKNOWN", argv[1]);
1602 if (target->handle_info)
1603 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1605 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1609 static NICKSERV_FUNC(cmd_nickinfo)
1611 struct nick_info *ni;
1613 NICKSERV_MIN_PARMS(2);
1614 if (!(ni = get_nick_info(argv[1]))) {
1615 reply("MSG_NICK_UNKNOWN", argv[1]);
1618 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1622 static NICKSERV_FUNC(cmd_notes)
1624 struct handle_info *hi;
1625 struct handle_note *prev, *note;
1628 NICKSERV_MIN_PARMS(2);
1629 if (!(hi = get_victim_oper(user, argv[1])))
1632 WALK_NOTES(hi, prev, note) {
1633 char set_time[INTERVALLEN];
1634 intervalString(set_time, now - note->set, user->handle_info);
1635 if (note->expires) {
1636 char exp_time[INTERVALLEN];
1637 intervalString(exp_time, note->expires - now, user->handle_info);
1638 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1640 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1644 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1648 static NICKSERV_FUNC(cmd_rename_handle)
1650 struct handle_info *hi;
1651 char msgbuf[MAXLEN], *old_handle;
1654 NICKSERV_MIN_PARMS(3);
1655 if (!(hi = get_victim_oper(user, argv[1])))
1657 if (!is_valid_handle(argv[2])) {
1658 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1661 if (get_handle_info(argv[2])) {
1662 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1665 if (hi->fakehost && hi->fakehost[0] == '.' &&
1666 (strlen(argv[2]) + strlen(hi->fakehost+1) +
1667 strlen(titlehost_suffix) + 2) > HOSTLEN) {
1668 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED_RENAME");
1672 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1673 hi->handle = strdup(argv[2]);
1674 dict_insert(nickserv_handle_dict, hi->handle, hi);
1675 for (nn=0; nn<rf_list_used; nn++)
1676 rf_list[nn](hi, old_handle);
1677 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1678 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1679 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1681 apply_fakehost(hi, NULL);
1685 static failpw_func_t *failpw_func_list;
1686 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1689 reg_failpw_func(failpw_func_t func)
1691 if (failpw_func_used == failpw_func_size) {
1692 if (failpw_func_size) {
1693 failpw_func_size <<= 1;
1694 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1696 failpw_func_size = 8;
1697 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1700 failpw_func_list[failpw_func_used++] = func;
1703 static NICKSERV_FUNC(cmd_auth)
1705 int pw_arg, used, maxlogins;
1706 struct handle_info *hi;
1708 struct userNode *other;
1710 if (user->handle_info) {
1711 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1714 if (IsStamped(user)) {
1715 /* Unauthenticated users might still have been stamped
1716 previously and could therefore have a hidden host;
1717 do not allow them to authenticate. */
1718 reply("NSMSG_STAMPED_AUTH");
1722 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1724 } else if (argc == 2) {
1725 if (nickserv_conf.disable_nicks) {
1726 if (!(hi = get_handle_info(user->nick))) {
1727 reply("NSMSG_HANDLE_NOT_FOUND");
1731 /* try to look up their handle from their nick */
1732 struct nick_info *ni;
1733 ni = get_nick_info(user->nick);
1735 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1742 reply("MSG_MISSING_PARAMS", argv[0]);
1743 svccmd_send_help(user, nickserv, cmd);
1747 reply("NSMSG_HANDLE_NOT_FOUND");
1750 /* Responses from here on look up the language used by the handle they asked about. */
1751 passwd = argv[pw_arg];
1752 if (!valid_user_for(user, hi)) {
1753 if (hi->email_addr && nickserv_conf.email_enabled)
1754 send_message_type(4, user, cmd->parent->bot,
1755 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1758 send_message_type(4, user, cmd->parent->bot,
1759 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1761 argv[pw_arg] = "BADMASK";
1764 if (!checkpass(passwd, hi->passwd)) {
1766 send_message_type(4, user, cmd->parent->bot,
1767 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1768 argv[pw_arg] = "BADPASS";
1769 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1770 if (nickserv_conf.autogag_enabled) {
1771 if (!user->auth_policer.params) {
1772 user->auth_policer.last_req = now;
1773 user->auth_policer.params = nickserv_conf.auth_policer_params;
1775 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1777 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1778 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1779 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1781 argv[pw_arg] = "GAGGED";
1786 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1787 send_message_type(4, user, cmd->parent->bot,
1788 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1789 argv[pw_arg] = "SUSPENDED";
1792 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1793 for (used = 0, other = hi->users; other; other = other->next_authed) {
1794 if (++used >= maxlogins) {
1795 send_message_type(4, user, cmd->parent->bot,
1796 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1798 argv[pw_arg] = "MAXLOGINS";
1802 if (HANDLE_FLAGGED(hi, AUTOHIDE)) {
1803 //ok we have a fakehost set... but we need to set mode +x
1804 irc_svsmode(nickserv,user,"+x");
1807 set_user_handle_info(user, hi, 1);
1808 if (nickserv_conf.email_required && !hi->email_addr)
1809 reply("NSMSG_PLEASE_SET_EMAIL");
1810 if (!is_secure_password(hi->handle, passwd, NULL))
1811 reply("NSMSG_WEAK_PASSWORD");
1812 if (hi->passwd[0] != '$')
1813 cryptpass(passwd, hi->passwd);
1814 if (!hi->masks->used) {
1816 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1817 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1818 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1820 argv[pw_arg] = "****";
1821 reply("NSMSG_AUTH_SUCCESS");
1825 struct handle_info *checklogin(const char *user, const char *pass, const char *numeric, const char *hostmask)
1827 struct handle_info *hi;
1828 hi = dict_find(nickserv_handle_dict, user, NULL);
1831 if(!checkpass(pass, hi->passwd))
1833 if (HANDLE_FLAGGED(hi, SUSPENDED))
1835 /** following in one of the next commits
1836 struct last_login *login,*clogin,*old;
1837 unsigned int ii = 0;
1838 login = calloc(1, sizeof(*login));
1839 login->last_login = hi->last_login;
1840 login->hostmask = strdup(hostmask);
1841 login->authtime = now;
1842 login->quittime = 0;
1845 login->loc_pending = strdup(numeric);
1846 for (clogin = hi->last_login; clogin != NULL && ii < 9; clogin = clogin->last_login) {
1847 if(ii == 8 && clogin->last_login) {
1848 old = clogin->last_login;
1849 clogin->last_login = NULL;
1850 free(old->hostmask);
1853 if(old->loc_pending)
1854 free(old->loc_pending);
1858 hi->last_login = login;
1863 char *getfakehost(const char *user)
1865 struct handle_info *hi;
1866 hi = dict_find(nickserv_handle_dict, user, NULL);
1869 return generate_fakehost(hi);
1872 static allowauth_func_t *allowauth_func_list;
1873 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1876 reg_allowauth_func(allowauth_func_t func)
1878 if (allowauth_func_used == allowauth_func_size) {
1879 if (allowauth_func_size) {
1880 allowauth_func_size <<= 1;
1881 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1883 allowauth_func_size = 8;
1884 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1887 allowauth_func_list[allowauth_func_used++] = func;
1890 static NICKSERV_FUNC(cmd_allowauth)
1892 struct userNode *target;
1893 struct handle_info *hi;
1896 NICKSERV_MIN_PARMS(2);
1897 if (!(target = GetUserH(argv[1]))) {
1898 reply("MSG_NICK_UNKNOWN", argv[1]);
1901 if (target->handle_info) {
1902 reply("NSMSG_USER_PREV_AUTH", target->nick);
1905 if (IsStamped(target)) {
1906 /* Unauthenticated users might still have been stamped
1907 previously and could therefore have a hidden host;
1908 do not allow them to authenticate to an account. */
1909 reply("NSMSG_USER_PREV_STAMP", target->nick);
1914 else if (!(hi = get_handle_info(argv[2]))) {
1915 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1919 if (hi->opserv_level > user->handle_info->opserv_level) {
1920 reply("MSG_USER_OUTRANKED", hi->handle);
1923 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1924 || (hi->opserv_level > 0))
1925 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1926 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1929 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1930 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1931 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1932 if (nickserv_conf.email_enabled)
1933 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1935 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1936 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1938 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1940 for (n=0; n<allowauth_func_used; n++)
1941 allowauth_func_list[n](user, target, hi);
1945 static NICKSERV_FUNC(cmd_authcookie)
1947 struct handle_info *hi;
1949 NICKSERV_MIN_PARMS(2);
1950 if (user->handle_info) {
1951 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1954 if (IsStamped(user)) {
1955 /* Unauthenticated users might still have been stamped
1956 previously and could therefore have a hidden host;
1957 do not allow them to authenticate to an account. */
1958 reply("NSMSG_STAMPED_AUTHCOOKIE");
1961 if (!(hi = get_handle_info(argv[1]))) {
1962 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1965 if (!hi->email_addr) {
1966 reply("MSG_SET_EMAIL_ADDR");
1969 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1973 static NICKSERV_FUNC(cmd_delcookie)
1975 struct handle_info *hi;
1977 hi = user->handle_info;
1979 reply("NSMSG_NO_COOKIE");
1982 switch (hi->cookie->type) {
1985 reply("NSMSG_MUST_TIME_OUT");
1988 nickserv_eat_cookie(hi->cookie);
1989 reply("NSMSG_ATE_COOKIE");
1995 static NICKSERV_FUNC(cmd_odelcookie)
1997 struct handle_info *hi;
1999 NICKSERV_MIN_PARMS(2);
2001 if (!(hi = get_victim_oper(user, argv[1])))
2005 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
2009 nickserv_eat_cookie(hi->cookie);
2010 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
2015 static NICKSERV_FUNC(cmd_resetpass)
2017 struct handle_info *hi;
2018 char crypted[MD5_CRYPT_LENGTH];
2020 NICKSERV_MIN_PARMS(3);
2021 if (user->handle_info) {
2022 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2025 if (IsStamped(user)) {
2026 /* Unauthenticated users might still have been stamped
2027 previously and could therefore have a hidden host;
2028 do not allow them to activate an account. */
2029 reply("NSMSG_STAMPED_RESETPASS");
2032 if (!(hi = get_handle_info(argv[1]))) {
2033 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2036 if (!hi->email_addr) {
2037 reply("MSG_SET_EMAIL_ADDR");
2040 cryptpass(argv[2], crypted);
2042 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
2046 static NICKSERV_FUNC(cmd_cookie)
2048 struct handle_info *hi;
2051 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
2054 NICKSERV_MIN_PARMS(3);
2055 if (!(hi = get_handle_info(argv[1]))) {
2056 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2062 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2063 reply("NSMSG_HANDLE_SUSPENDED");
2068 reply("NSMSG_NO_COOKIE");
2072 /* Check validity of operation before comparing cookie to
2073 * prohibit guessing by authed users. */
2074 if (user->handle_info
2075 && (hi->cookie->type != EMAIL_CHANGE)
2076 && (hi->cookie->type != PASSWORD_CHANGE)) {
2077 reply("NSMSG_CANNOT_COOKIE");
2081 if (strcmp(cookie, hi->cookie->cookie)) {
2082 reply("NSMSG_BAD_COOKIE");
2086 switch (hi->cookie->type) {
2088 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2089 set_user_handle_info(user, hi, 1);
2090 reply("NSMSG_HANDLE_ACTIVATED");
2092 case PASSWORD_CHANGE:
2093 set_user_handle_info(user, hi, 1);
2094 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2095 reply("NSMSG_PASSWORD_CHANGED");
2098 nickserv_set_email_addr(hi, hi->cookie->data);
2099 reply("NSMSG_EMAIL_CHANGED");
2102 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2103 set_user_handle_info(user, hi, 1);
2104 nickserv_addmask(user, hi, mask);
2105 reply("NSMSG_AUTH_SUCCESS");
2110 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2111 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2115 nickserv_eat_cookie(hi->cookie);
2120 static NICKSERV_FUNC(cmd_oregnick) {
2122 struct handle_info *target;
2123 struct nick_info *ni;
2125 NICKSERV_MIN_PARMS(3);
2126 if (!(target = modcmd_get_handle_info(user, argv[1])))
2129 if (!is_registerable_nick(nick)) {
2130 reply("NSMSG_BAD_NICK", nick);
2133 ni = dict_find(nickserv_nick_dict, nick, NULL);
2135 reply("NSMSG_NICK_EXISTS", nick);
2138 register_nick(nick, target);
2139 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2143 static NICKSERV_FUNC(cmd_regnick) {
2145 struct nick_info *ni;
2147 if (!is_registerable_nick(user->nick)) {
2148 reply("NSMSG_BAD_NICK", user->nick);
2151 /* count their nicks, see if it's too many */
2152 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2153 if (n >= nickserv_conf.nicks_per_handle) {
2154 reply("NSMSG_TOO_MANY_NICKS");
2157 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2159 reply("NSMSG_NICK_EXISTS", user->nick);
2162 register_nick(user->nick, user->handle_info);
2163 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2167 static NICKSERV_FUNC(cmd_pass)
2169 struct handle_info *hi;
2170 const char *old_pass, *new_pass;
2172 NICKSERV_MIN_PARMS(3);
2173 hi = user->handle_info;
2177 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2178 if (!checkpass(old_pass, hi->passwd)) {
2179 argv[1] = "BADPASS";
2180 reply("NSMSG_PASSWORD_INVALID");
2183 cryptpass(new_pass, hi->passwd);
2185 reply("NSMSG_PASS_SUCCESS");
2190 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2193 char *new_mask = canonicalize_hostmask(strdup(mask));
2194 for (i=0; i<hi->masks->used; i++) {
2195 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2196 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2201 string_list_append(hi->masks, new_mask);
2202 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2206 static NICKSERV_FUNC(cmd_addmask)
2209 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2210 int res = nickserv_addmask(user, user->handle_info, mask);
2214 if (!is_gline(argv[1])) {
2215 reply("NSMSG_MASK_INVALID", argv[1]);
2218 return nickserv_addmask(user, user->handle_info, argv[1]);
2222 static NICKSERV_FUNC(cmd_oaddmask)
2224 struct handle_info *hi;
2226 NICKSERV_MIN_PARMS(3);
2227 if (!(hi = get_victim_oper(user, argv[1])))
2229 return nickserv_addmask(user, hi, argv[2]);
2233 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2236 for (i=0; i<hi->masks->used; i++) {
2237 if (!strcmp(del_mask, hi->masks->list[i])) {
2238 char *old_mask = hi->masks->list[i];
2239 if (hi->masks->used == 1 && !force) {
2240 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2243 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2244 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2249 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2253 static NICKSERV_FUNC(cmd_delmask)
2255 NICKSERV_MIN_PARMS(2);
2256 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2259 static NICKSERV_FUNC(cmd_odelmask)
2261 struct handle_info *hi;
2262 NICKSERV_MIN_PARMS(3);
2263 if (!(hi = get_victim_oper(user, argv[1])))
2265 return nickserv_delmask(user, hi, argv[2], 1);
2269 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2270 unsigned int nn, add = 1, pos;
2271 unsigned long added, removed, flag;
2273 for (added=removed=nn=0; str[nn]; nn++) {
2275 case '+': add = 1; break;
2276 case '-': add = 0; break;
2278 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2279 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2282 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2283 /* cheesy avoidance of looking up the flag name.. */
2284 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2287 flag = 1 << (pos - 1);
2289 added |= flag, removed &= ~flag;
2291 removed |= flag, added &= ~flag;
2296 *premoved = removed;
2301 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2303 unsigned long before, after, added, removed;
2304 struct userNode *uNode;
2306 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2307 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2309 hi->flags = (hi->flags | added) & ~removed;
2310 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2312 /* Strip helping flag if they're only a support helper and not
2313 * currently in #support. */
2314 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2315 struct channelList *schannels;
2317 schannels = chanserv_support_channels();
2318 for (ii = 0; ii < schannels->used; ++ii)
2319 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2321 if (ii == schannels->used)
2322 HANDLE_CLEAR_FLAG(hi, HELPING);
2325 if (after && !before) {
2326 /* Add user to current helper list. */
2327 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2328 userList_append(&curr_helpers, uNode);
2329 } else if (!after && before) {
2330 /* Remove user from current helper list. */
2331 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2332 userList_remove(&curr_helpers, uNode);
2339 set_list(struct userNode *user, struct handle_info *hi, int override)
2343 char *set_display[] = {
2344 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2345 "EMAIL", "MAXLOGINS", "LANGUAGE", "DEVNULL"
2348 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2350 /* Do this so options are presented in a consistent order. */
2351 for (i = 0; i < ArrayLength(set_display); ++i)
2352 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2353 opt(user, hi, override, 0, NULL);
2356 static NICKSERV_FUNC(cmd_set)
2358 struct handle_info *hi;
2361 hi = user->handle_info;
2363 set_list(user, hi, 0);
2366 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2367 reply("NSMSG_INVALID_OPTION", argv[1]);
2370 return opt(user, hi, 0, argc-1, argv+1);
2373 static NICKSERV_FUNC(cmd_oset)
2375 struct handle_info *hi;
2376 struct svccmd *subcmd;
2378 char cmdname[MAXLEN];
2380 NICKSERV_MIN_PARMS(2);
2382 if (!(hi = get_victim_oper(user, argv[1])))
2386 set_list(user, hi, 0);
2390 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2391 reply("NSMSG_INVALID_OPTION", argv[2]);
2395 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2396 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2397 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2400 return opt(user, hi, 1, argc-2, argv+2);
2403 static OPTION_FUNC(opt_info)
2407 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2409 hi->infoline = NULL;
2411 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2415 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2416 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2420 static OPTION_FUNC(opt_devnull)
2422 const char *devnull;
2426 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2429 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2433 devnull = unsplit_string(argv+1, argc-1, NULL);
2434 if(devnull_check(devnull) == 1) {
2435 hi->devnull = strdup(devnull);
2440 devnull = hi->devnull ? hi->devnull : user_find_message(user, "MSG_NONE");
2441 send_message(user, nickserv, "NSMSG_SET_DEVNULL", devnull);
2445 void nickserv_devnull_delete(char *name) {
2447 struct handle_info *hi;
2449 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2451 if (hi->devnull && !irccasecmp(name, hi->devnull)) {
2458 void nickserv_devnull_rename(char *oldname, char *newname) {
2460 struct handle_info *hi;
2462 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2464 if (hi->devnull && !irccasecmp(oldname, hi->devnull)) {
2465 hi->devnull = strdup(newname);
2470 static OPTION_FUNC(opt_website)
2472 const char *website;
2475 if (!HANDLE_FLAGGED(user->handle_info, BOT)) {
2476 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2479 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2483 website = unsplit_string(argv+1, argc-1, NULL);
2484 hi->website = strdup(website);
2487 if (HANDLE_FLAGGED(user->handle_info, BOT)) {
2488 website = hi->website ? hi->website : user_find_message(user, "MSG_NONE");
2489 send_message(user, nickserv, "NSMSG_SET_WEBSITE", website);
2494 static OPTION_FUNC(opt_width)
2497 hi->screen_width = strtoul(argv[1], NULL, 0);
2499 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2500 hi->screen_width = MIN_LINE_SIZE;
2501 else if (hi->screen_width > MAX_LINE_SIZE)
2502 hi->screen_width = MAX_LINE_SIZE;
2504 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2508 static OPTION_FUNC(opt_tablewidth)
2511 hi->table_width = strtoul(argv[1], NULL, 0);
2513 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2514 hi->table_width = MIN_LINE_SIZE;
2515 else if (hi->screen_width > MAX_LINE_SIZE)
2516 hi->table_width = MAX_LINE_SIZE;
2518 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2522 static OPTION_FUNC(opt_color)
2525 if (enabled_string(argv[1]))
2526 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2527 else if (disabled_string(argv[1]))
2528 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2530 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2535 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2539 static OPTION_FUNC(opt_privmsg)
2542 if (enabled_string(argv[1]))
2543 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2544 else if (disabled_string(argv[1]))
2545 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2547 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2552 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2556 static OPTION_FUNC(opt_autohide)
2559 if (enabled_string(argv[1]))
2560 HANDLE_SET_FLAG(hi, AUTOHIDE);
2561 else if (disabled_string(argv[1]))
2562 HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
2564 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2569 send_message(user, nickserv, "NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
2573 static OPTION_FUNC(opt_style)
2578 if (!irccasecmp(argv[1], "Zoot"))
2579 hi->userlist_style = HI_STYLE_ZOOT;
2580 else if (!irccasecmp(argv[1], "def"))
2581 hi->userlist_style = HI_STYLE_DEF;
2584 switch (hi->userlist_style) {
2593 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2597 static OPTION_FUNC(opt_password)
2600 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2605 cryptpass(argv[1], hi->passwd);
2607 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2611 static OPTION_FUNC(opt_flags)
2614 unsigned int ii, flen;
2617 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2622 nickserv_apply_flags(user, hi, argv[1]);
2624 for (ii = flen = 0; handle_flags[ii]; ii++)
2625 if (hi->flags & (1 << ii))
2626 flags[flen++] = handle_flags[ii];
2629 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2631 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2635 static OPTION_FUNC(opt_email)
2639 if (!is_valid_email_addr(argv[1])) {
2640 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2643 if ((str = mail_prohibited_address(argv[1]))) {
2644 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2647 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2648 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2650 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2652 nickserv_set_email_addr(hi, argv[1]);
2654 nickserv_eat_cookie(hi->cookie);
2655 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2658 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2662 static OPTION_FUNC(opt_maxlogins)
2664 unsigned char maxlogins;
2666 maxlogins = strtoul(argv[1], NULL, 0);
2667 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2668 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2671 hi->maxlogins = maxlogins;
2673 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2674 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2678 static OPTION_FUNC(opt_language)
2680 struct language *lang;
2682 lang = language_find(argv[1]);
2683 if (irccasecmp(lang->name, argv[1]))
2684 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2685 hi->language = lang;
2687 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2691 static OPTION_FUNC(opt_karma)
2694 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2699 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2700 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2701 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2702 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2704 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2708 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2713 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2714 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2716 if ((user->handle_info->opserv_level < target->opserv_level)
2717 || ((user->handle_info->opserv_level == target->opserv_level)
2718 && (user->handle_info->opserv_level < 1000))) {
2719 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2722 if ((user->handle_info->opserv_level < new_level)
2723 || ((user->handle_info->opserv_level == new_level)
2724 && (user->handle_info->opserv_level < 1000))) {
2725 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2728 if (user->handle_info == target) {
2729 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2732 if (target->opserv_level == new_level)
2734 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2735 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2736 target->opserv_level = new_level;
2740 static OPTION_FUNC(opt_level)
2745 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2749 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2750 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2754 static OPTION_FUNC(opt_epithet)
2757 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2761 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2762 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2765 if ((epithet[0] == '*') && !epithet[1])
2768 hi->epithet = strdup(epithet);
2772 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2774 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2778 static OPTION_FUNC(opt_title)
2783 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2787 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2789 if (strchr(title, '.')) {
2790 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2793 if ((strlen(user->handle_info->handle) + strlen(title) +
2794 strlen(titlehost_suffix) + 2) > HOSTLEN) {
2795 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2800 if (!strcmp(title, "*")) {
2801 hi->fakehost = NULL;
2803 hi->fakehost = malloc(strlen(title)+2);
2804 hi->fakehost[0] = '.';
2805 strcpy(hi->fakehost+1, title);
2807 apply_fakehost(hi, NULL);
2808 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2809 title = hi->fakehost + 1;
2813 title = user_find_message(user, "MSG_NONE");
2814 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2818 static OPTION_FUNC(opt_fakehost)
2820 char mask[USERLEN + HOSTLEN + 2];
2824 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2828 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2829 if(strlen(argv[1]) >= sizeof(mask)) {
2830 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
2834 safestrncpy(mask, argv[1], sizeof(mask));
2836 if ((host = strrchr(mask, '@')) && host != mask) {
2837 /* If ident@host was used and the user doesn't have access to set idents, do not change anything. */
2838 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2850 if (ident && strlen(ident) > USERLEN) {
2851 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2855 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
2856 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2860 if (host && host[0]) {
2862 if (!strcmp(host, "*"))
2863 hi->fakehost = NULL;
2865 hi->fakehost = strdup(host);
2866 host = hi->fakehost;
2869 host = generate_fakehost(hi);
2872 free(hi->fakeident);
2873 if (!strcmp(ident, "*"))
2874 hi->fakeident = NULL;
2876 hi->fakeident = strdup(ident);
2877 ident = hi->fakeident;
2880 ident = generate_fakeident(hi, NULL);
2882 apply_fakehost(hi, NULL);
2884 host = generate_fakehost(hi);
2885 ident = generate_fakeident(hi, NULL);
2888 host = (char *) user_find_message(user, "MSG_NONE");
2890 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
2892 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
2896 static OPTION_FUNC(opt_fakeident)
2901 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2905 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2907 if (strlen(ident) > USERLEN) {
2908 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2911 free(hi->fakeident);
2912 if (!strcmp(ident, "*"))
2913 hi->fakeident = NULL;
2915 hi->fakeident = strdup(ident);
2916 ident = hi->fakeident;
2917 apply_fakehost(hi, NULL);
2919 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
2921 ident = user_find_message(user, "MSG_NONE");
2922 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
2926 static NICKSERV_FUNC(cmd_reclaim)
2928 struct handle_info *hi;
2929 struct nick_info *ni;
2930 struct userNode *victim;
2932 NICKSERV_MIN_PARMS(2);
2933 hi = user->handle_info;
2934 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2936 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2939 if (ni->owner != user->handle_info) {
2940 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2943 victim = GetUserH(ni->nick);
2945 reply("MSG_NICK_UNKNOWN", ni->nick);
2948 if (victim == user) {
2949 reply("NSMSG_NICK_USER_YOU");
2952 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2953 switch (nickserv_conf.reclaim_action) {
2954 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2955 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2956 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2957 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2962 static NICKSERV_FUNC(cmd_unregnick)
2965 struct handle_info *hi;
2966 struct nick_info *ni;
2968 hi = user->handle_info;
2969 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2970 ni = dict_find(nickserv_nick_dict, nick, NULL);
2972 reply("NSMSG_UNKNOWN_NICK", nick);
2975 if (hi != ni->owner) {
2976 reply("NSMSG_NOT_YOUR_NICK", nick);
2979 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2984 static NICKSERV_FUNC(cmd_ounregnick)
2986 struct nick_info *ni;
2988 NICKSERV_MIN_PARMS(2);
2989 if (!(ni = get_nick_info(argv[1]))) {
2990 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2993 if (!oper_outranks(user, ni->owner))
2995 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3000 static NICKSERV_FUNC(cmd_unregister)
3002 struct handle_info *hi;
3005 NICKSERV_MIN_PARMS(2);
3006 hi = user->handle_info;
3009 if (checkpass(passwd, hi->passwd)) {
3010 nickserv_unregister_handle(hi, user);
3013 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3014 reply("NSMSG_PASSWORD_INVALID");
3019 static NICKSERV_FUNC(cmd_ounregister)
3021 struct handle_info *hi;
3022 char reason[MAXLEN];
3025 NICKSERV_MIN_PARMS(2);
3026 if (!(hi = get_victim_oper(user, argv[1])))
3029 if (HANDLE_FLAGGED(hi, NODELETE)) {
3030 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
3034 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
3036 ((hi->flags & nickserv_conf.ounregister_flags)
3038 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
3039 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
3043 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
3044 global_message(MESSAGE_RECIPIENT_STAFF, reason);
3045 nickserv_unregister_handle(hi, user);
3049 static NICKSERV_FUNC(cmd_status)
3051 if (nickserv_conf.disable_nicks) {
3052 reply("NSMSG_GLOBAL_STATS_NONICK",
3053 dict_size(nickserv_handle_dict));
3055 if (user->handle_info) {
3057 struct nick_info *ni;
3058 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
3059 reply("NSMSG_HANDLE_STATS", cnt);
3061 reply("NSMSG_HANDLE_NONE");
3063 reply("NSMSG_GLOBAL_STATS",
3064 dict_size(nickserv_handle_dict),
3065 dict_size(nickserv_nick_dict));
3070 static NICKSERV_FUNC(cmd_ghost)
3072 struct userNode *target;
3073 char reason[MAXLEN];
3075 NICKSERV_MIN_PARMS(2);
3076 if (!(target = GetUserH(argv[1]))) {
3077 reply("MSG_NICK_UNKNOWN", argv[1]);
3080 if (target == user) {
3081 reply("NSMSG_CANNOT_GHOST_SELF");
3084 if (!target->handle_info || (target->handle_info != user->handle_info)) {
3085 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
3088 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
3089 DelUser(target, nickserv, 1, reason);
3090 reply("NSMSG_GHOST_KILLED", argv[1]);
3094 static NICKSERV_FUNC(cmd_vacation)
3096 HANDLE_SET_FLAG(user->handle_info, FROZEN);
3097 reply("NSMSG_ON_VACATION");
3101 static NICKSERV_FUNC(cmd_addnote)
3103 struct handle_info *hi;
3104 unsigned long duration;
3107 struct handle_note *prev;
3108 struct handle_note *note;
3110 /* Parse parameters and figure out values for note's fields. */
3111 NICKSERV_MIN_PARMS(4);
3112 hi = get_victim_oper(user, argv[1]);
3115 if(!strcmp(argv[2], "0"))
3117 else if(!(duration = ParseInterval(argv[2])))
3119 reply("MSG_INVALID_DURATION", argv[2]);
3122 if (duration > 2*365*86400) {
3123 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
3126 unsplit_string(argv + 3, argc - 3, text);
3127 WALK_NOTES(hi, prev, note) {}
3128 id = prev ? (prev->id + 1) : 1;
3130 /* Create the new note structure. */
3131 note = calloc(1, sizeof(*note) + strlen(text));
3133 note->expires = duration ? (now + duration) : 0;
3136 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
3137 strcpy(note->note, text);
3142 reply("NSMSG_NOTE_ADDED", id, hi->handle);
3146 static NICKSERV_FUNC(cmd_delnote)
3148 struct handle_info *hi;
3149 struct handle_note *prev;
3150 struct handle_note *note;
3153 NICKSERV_MIN_PARMS(3);
3154 hi = get_victim_oper(user, argv[1]);
3157 id = strtoul(argv[2], NULL, 10);
3158 WALK_NOTES(hi, prev, note) {
3159 if (id == note->id) {
3161 prev->next = note->next;
3163 hi->notes = note->next;
3165 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3169 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3174 nickserv_saxdb_write(struct saxdb_context *ctx) {
3176 struct handle_info *hi;
3179 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3181 assert(hi->id != 0);
3182 saxdb_start_record(ctx, iter_key(it), 0);
3184 struct handle_cookie *cookie = hi->cookie;
3187 switch (cookie->type) {
3188 case ACTIVATION: type = KEY_ACTIVATION; break;
3189 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3190 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3191 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3192 default: type = NULL; break;
3195 saxdb_start_record(ctx, KEY_COOKIE, 0);
3196 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3197 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3199 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3200 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3201 saxdb_end_record(ctx);
3205 struct handle_note *prev, *note;
3206 saxdb_start_record(ctx, KEY_NOTES, 0);
3207 WALK_NOTES(hi, prev, note) {
3208 snprintf(flags, sizeof(flags), "%d", note->id);
3209 saxdb_start_record(ctx, flags, 0);
3211 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3212 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3213 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3214 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3215 saxdb_end_record(ctx);
3217 saxdb_end_record(ctx);
3220 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3222 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3224 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3226 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3230 for (ii=flen=0; handle_flags[ii]; ++ii)
3231 if (hi->flags & (1 << ii))
3232 flags[flen++] = handle_flags[ii];
3234 saxdb_write_string(ctx, KEY_FLAGS, flags);
3236 saxdb_write_int(ctx, KEY_ID, hi->id);
3238 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3240 saxdb_write_string(ctx, KEY_DEVNULL, hi->devnull);
3242 saxdb_write_string(ctx, KEY_WEBSITE, hi->website);
3243 if (hi->last_quit_host[0])
3244 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3245 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3247 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3248 if (hi->masks->used)
3249 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3251 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3253 struct string_list *slist;
3254 struct nick_info *ni;
3256 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3257 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3258 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3262 if (hi->opserv_level)
3263 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3264 if (hi->language != lang_C)
3265 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3266 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3267 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3268 if (hi->screen_width)
3269 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3270 if (hi->table_width)
3271 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3272 flags[0] = hi->userlist_style;
3274 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3275 saxdb_end_record(ctx);
3280 static handle_merge_func_t *handle_merge_func_list;
3281 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3284 reg_handle_merge_func(handle_merge_func_t func)
3286 if (handle_merge_func_used == handle_merge_func_size) {
3287 if (handle_merge_func_size) {
3288 handle_merge_func_size <<= 1;
3289 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3291 handle_merge_func_size = 8;
3292 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3295 handle_merge_func_list[handle_merge_func_used++] = func;
3298 static NICKSERV_FUNC(cmd_merge)
3300 struct handle_info *hi_from, *hi_to;
3301 struct userNode *last_user;
3302 struct userData *cList, *cListNext;
3303 unsigned int ii, jj, n;
3304 char buffer[MAXLEN];
3306 NICKSERV_MIN_PARMS(3);
3308 if (!(hi_from = get_victim_oper(user, argv[1])))
3310 if (!(hi_to = get_victim_oper(user, argv[2])))
3312 if (hi_to == hi_from) {
3313 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3317 for (n=0; n<handle_merge_func_used; n++)
3318 handle_merge_func_list[n](user, hi_to, hi_from);
3320 /* Append "from" handle's nicks to "to" handle's nick list. */
3322 struct nick_info *last_ni;
3323 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3324 last_ni->next = hi_from->nicks;
3326 while (hi_from->nicks) {
3327 hi_from->nicks->owner = hi_to;
3328 hi_from->nicks = hi_from->nicks->next;
3331 /* Merge the hostmasks. */
3332 for (ii=0; ii<hi_from->masks->used; ii++) {
3333 char *mask = hi_from->masks->list[ii];
3334 for (jj=0; jj<hi_to->masks->used; jj++)
3335 if (match_ircglobs(hi_to->masks->list[jj], mask))
3337 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3338 string_list_append(hi_to->masks, strdup(mask));
3341 /* Merge the lists of authed users. */
3343 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3344 last_user->next_authed = hi_from->users;
3346 hi_to->users = hi_from->users;
3348 /* Repoint the old "from" handle's users. */
3349 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3350 last_user->handle_info = hi_to;
3352 hi_from->users = NULL;
3354 /* Merge channel userlists. */
3355 for (cList=hi_from->channels; cList; cList=cListNext) {
3356 struct userData *cList2;
3357 cListNext = cList->u_next;
3358 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3359 if (cList->channel == cList2->channel)
3361 if (cList2 && (cList2->access >= cList->access)) {
3362 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);
3363 /* keep cList2 in hi_to; remove cList from hi_from */
3364 del_channel_user(cList, 1);
3367 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);
3368 /* remove the lower-ranking cList2 from hi_to */
3369 del_channel_user(cList2, 1);
3371 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3373 /* cList needs to be moved from hi_from to hi_to */
3374 cList->handle = hi_to;
3375 /* Remove from linked list for hi_from */
3376 assert(!cList->u_prev);
3377 hi_from->channels = cList->u_next;
3379 cList->u_next->u_prev = cList->u_prev;
3380 /* Add to linked list for hi_to */
3381 cList->u_prev = NULL;
3382 cList->u_next = hi_to->channels;
3383 if (hi_to->channels)
3384 hi_to->channels->u_prev = cList;
3385 hi_to->channels = cList;
3389 /* Do they get an OpServ level promotion? */
3390 if (hi_from->opserv_level > hi_to->opserv_level)
3391 hi_to->opserv_level = hi_from->opserv_level;
3393 /* What about last seen time? */
3394 if (hi_from->lastseen > hi_to->lastseen)
3395 hi_to->lastseen = hi_from->lastseen;
3397 /* New karma is the sum of the two original karmas. */
3398 hi_to->karma += hi_from->karma;
3400 /* Does a fakehost carry over? (This intentionally doesn't set it
3401 * for users previously attached to hi_to. They'll just have to
3404 if (hi_from->fakehost && !hi_to->fakehost)
3405 hi_to->fakehost = strdup(hi_from->fakehost);
3406 if (hi_from->fakeident && !hi_to->fakeident)
3407 hi_to->fakeident = strdup(hi_from->fakeident);
3409 /* Notify of success. */
3410 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3411 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3412 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3414 /* Unregister the "from" handle. */
3415 nickserv_unregister_handle(hi_from, NULL);
3420 struct nickserv_discrim {
3421 unsigned long flags_on, flags_off;
3422 unsigned long min_registered, max_registered;
3423 unsigned long lastseen;
3425 int min_level, max_level;
3426 int min_karma, max_karma;
3427 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3428 const char *nickmask;
3429 const char *hostmask;
3430 const char *fakehostmask;
3431 const char *fakeidentmask;
3432 const char *website;
3433 const char *devnullclass;
3434 const char *handlemask;
3435 const char *emailmask;
3438 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3440 struct discrim_apply_info {
3441 struct nickserv_discrim *discrim;
3442 discrim_search_func func;
3443 struct userNode *source;
3444 unsigned int matched;
3447 static struct nickserv_discrim *
3448 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3451 struct nickserv_discrim *discrim;
3453 discrim = malloc(sizeof(*discrim));
3454 memset(discrim, 0, sizeof(*discrim));
3455 discrim->min_level = 0;
3456 discrim->max_level = INT_MAX;
3457 discrim->limit = 50;
3458 discrim->min_registered = 0;
3459 discrim->max_registered = ULONG_MAX;
3460 discrim->lastseen = ULONG_MAX;
3461 discrim->min_karma = INT_MIN;
3462 discrim->max_karma = INT_MAX;
3464 for (i=0; i<argc; i++) {
3465 if (i == argc - 1) {
3466 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3469 if (!irccasecmp(argv[i], "limit")) {
3470 discrim->limit = strtoul(argv[++i], NULL, 0);
3471 } else if (!irccasecmp(argv[i], "flags")) {
3472 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3473 } else if (!irccasecmp(argv[i], "registered")) {
3474 const char *cmp = argv[++i];
3475 if (cmp[0] == '<') {
3476 if (cmp[1] == '=') {
3477 discrim->min_registered = now - ParseInterval(cmp+2);
3479 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3481 } else if (cmp[0] == '=') {
3482 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3483 } else if (cmp[0] == '>') {
3484 if (cmp[1] == '=') {
3485 discrim->max_registered = now - ParseInterval(cmp+2);
3487 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3490 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3492 } else if (!irccasecmp(argv[i], "seen")) {
3493 discrim->lastseen = now - ParseInterval(argv[++i]);
3494 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3495 discrim->nickmask = argv[++i];
3496 } else if (!irccasecmp(argv[i], "hostmask")) {
3498 if (!irccasecmp(argv[i], "exact")) {
3499 if (i == argc - 1) {
3500 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3503 discrim->hostmask_type = EXACT;
3504 } else if (!irccasecmp(argv[i], "subset")) {
3505 if (i == argc - 1) {
3506 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3509 discrim->hostmask_type = SUBSET;
3510 } else if (!irccasecmp(argv[i], "superset")) {
3511 if (i == argc - 1) {
3512 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3515 discrim->hostmask_type = SUPERSET;
3516 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3517 if (i == argc - 1) {
3518 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3521 discrim->hostmask_type = LASTQUIT;
3524 discrim->hostmask_type = SUPERSET;
3526 discrim->hostmask = argv[++i];
3527 } else if (!irccasecmp(argv[i], "fakehost")) {
3528 if (!irccasecmp(argv[++i], "*")) {
3529 discrim->fakehostmask = 0;
3531 discrim->fakehostmask = argv[i];
3533 } else if (!irccasecmp(argv[i], "fakeident")) {
3534 if (!irccasecmp(argv[++i], "*")) {
3535 discrim->fakeidentmask = 0;
3537 discrim->fakeidentmask = argv[i];
3539 } else if (!irccasecmp(argv[i], "website")) {
3540 if (!irccasecmp(argv[++i], "*")) {
3541 discrim->website = 0;
3543 discrim->website = argv[i];
3545 } else if (!irccasecmp(argv[i], "devnull")) {
3546 if (!irccasecmp(argv[++i], "*")) {
3547 discrim->devnullclass = 0;
3549 discrim->devnullclass = argv[i];
3551 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3552 if (!irccasecmp(argv[++i], "*")) {
3553 discrim->handlemask = 0;
3555 discrim->handlemask = argv[i];
3557 } else if (!irccasecmp(argv[i], "email")) {
3558 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3559 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3561 } else if (!irccasecmp(argv[++i], "*")) {
3562 discrim->emailmask = 0;
3564 discrim->emailmask = argv[i];
3566 } else if (!irccasecmp(argv[i], "access")) {
3567 const char *cmp = argv[++i];
3568 if (cmp[0] == '<') {
3569 if (discrim->min_level == 0) discrim->min_level = 1;
3570 if (cmp[1] == '=') {
3571 discrim->max_level = strtoul(cmp+2, NULL, 0);
3573 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3575 } else if (cmp[0] == '=') {
3576 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3577 } else if (cmp[0] == '>') {
3578 if (cmp[1] == '=') {
3579 discrim->min_level = strtoul(cmp+2, NULL, 0);
3581 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3584 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3586 } else if (!irccasecmp(argv[i], "karma")) {
3587 const char *cmp = argv[++i];
3588 if (cmp[0] == '<') {
3589 if (cmp[1] == '=') {
3590 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3592 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3594 } else if (cmp[0] == '=') {
3595 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3596 } else if (cmp[0] == '>') {
3597 if (cmp[1] == '=') {
3598 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3600 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3603 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3606 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3617 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3619 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3620 || (discrim->flags_off & hi->flags)
3621 || (discrim->min_registered > hi->registered)
3622 || (discrim->max_registered < hi->registered)
3623 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3624 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3625 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3626 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3627 || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website)))
3628 || (discrim->devnullclass && (!hi->devnull || !match_ircglob(hi->devnull, discrim->devnullclass)))
3629 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3630 || (discrim->min_level > hi->opserv_level)
3631 || (discrim->max_level < hi->opserv_level)
3632 || (discrim->min_karma > hi->karma)
3633 || (discrim->max_karma < hi->karma)
3637 if (discrim->hostmask) {
3639 for (i=0; i<hi->masks->used; i++) {
3640 const char *mask = hi->masks->list[i];
3641 if ((discrim->hostmask_type == SUBSET)
3642 && (match_ircglobs(discrim->hostmask, mask))) break;
3643 else if ((discrim->hostmask_type == EXACT)
3644 && !irccasecmp(discrim->hostmask, mask)) break;
3645 else if ((discrim->hostmask_type == SUPERSET)
3646 && (match_ircglobs(mask, discrim->hostmask))) break;
3647 else if ((discrim->hostmask_type == LASTQUIT)
3648 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3650 if (i==hi->masks->used) return 0;
3652 if (discrim->nickmask) {
3653 struct nick_info *nick = hi->nicks;
3655 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3658 if (!nick) return 0;
3664 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3666 dict_iterator_t it, next;
3667 unsigned int matched;
3669 for (it = dict_first(nickserv_handle_dict), matched = 0;
3670 it && (matched < discrim->limit);
3672 next = iter_next(it);
3673 if (nickserv_discrim_match(discrim, iter_data(it))) {
3674 dsf(source, iter_data(it));
3682 search_print_func(struct userNode *source, struct handle_info *match)
3684 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3688 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3693 search_unregister_func (struct userNode *source, struct handle_info *match)
3695 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3696 nickserv_unregister_handle(match, source);
3700 nickserv_sort_accounts_by_access(const void *a, const void *b)
3702 const struct handle_info *hi_a = *(const struct handle_info**)a;
3703 const struct handle_info *hi_b = *(const struct handle_info**)b;
3704 if (hi_a->opserv_level != hi_b->opserv_level)
3705 return hi_b->opserv_level - hi_a->opserv_level;
3706 return irccasecmp(hi_a->handle, hi_b->handle);
3710 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3712 struct handle_info_list hil;
3713 struct helpfile_table tbl;
3718 memset(&hil, 0, sizeof(hil));
3719 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3720 struct handle_info *hi = iter_data(it);
3721 if (hi->opserv_level)
3722 handle_info_list_append(&hil, hi);
3724 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3725 tbl.length = hil.used + 1;
3727 tbl.flags = TABLE_NO_FREE;
3728 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3729 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3732 for (ii = 0; ii < hil.used; ) {
3733 ary = malloc(tbl.width * sizeof(ary[0]));
3734 ary[0] = hil.list[ii]->handle;
3735 ary[1] = strtab(hil.list[ii]->opserv_level);
3736 tbl.contents[++ii] = ary;
3738 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3739 reply("MSG_MATCH_COUNT", hil.used);
3740 for (ii = 0; ii < hil.used; ii++)
3741 free(tbl.contents[ii]);
3746 static NICKSERV_FUNC(cmd_search)
3748 struct nickserv_discrim *discrim;
3749 discrim_search_func action;
3750 struct svccmd *subcmd;
3751 unsigned int matches;
3754 NICKSERV_MIN_PARMS(3);
3755 sprintf(buf, "search %s", argv[1]);
3756 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3757 if (!irccasecmp(argv[1], "print"))
3758 action = search_print_func;
3759 else if (!irccasecmp(argv[1], "count"))
3760 action = search_count_func;
3761 else if (!irccasecmp(argv[1], "unregister"))
3762 action = search_unregister_func;
3764 reply("NSMSG_INVALID_ACTION", argv[1]);
3768 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3771 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3775 if (action == search_print_func)
3776 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3777 else if (action == search_count_func)
3778 discrim->limit = INT_MAX;
3780 matches = nickserv_discrim_search(discrim, action, user);
3783 reply("MSG_MATCH_COUNT", matches);
3785 reply("MSG_NO_MATCHES");
3791 static MODCMD_FUNC(cmd_checkpass)
3793 struct handle_info *hi;
3795 NICKSERV_MIN_PARMS(3);
3796 if (!(hi = get_handle_info(argv[1]))) {
3797 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3800 if (checkpass(argv[2], hi->passwd))
3801 reply("CHECKPASS_YES");
3803 reply("CHECKPASS_NO");
3808 static MODCMD_FUNC(cmd_checkemail)
3810 struct handle_info *hi;
3812 NICKSERV_MIN_PARMS(3);
3813 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3816 if (!hi->email_addr)
3817 reply("CHECKEMAIL_NOT_SET");
3818 else if (!irccasecmp(argv[2], hi->email_addr))
3819 reply("CHECKEMAIL_YES");
3821 reply("CHECKEMAIL_NO");
3827 nickserv_db_read_handle(const char *handle, dict_t obj)
3830 struct string_list *masks, *slist;
3831 struct handle_info *hi;
3832 struct userNode *authed_users;
3833 struct userData *channel_list;
3838 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3839 id = str ? strtoul(str, NULL, 0) : 0;
3840 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3842 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3845 if ((hi = get_handle_info(handle))) {
3846 authed_users = hi->users;
3847 channel_list = hi->channels;
3849 hi->channels = NULL;
3850 dict_remove(nickserv_handle_dict, hi->handle);
3852 authed_users = NULL;
3853 channel_list = NULL;
3855 hi = register_handle(handle, str, id);
3857 hi->users = authed_users;
3858 while (authed_users) {
3859 authed_users->handle_info = hi;
3860 authed_users = authed_users->next_authed;
3863 hi->channels = channel_list;
3864 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3865 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3866 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3867 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3868 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3869 hi->language = language_find(str ? str : "C");
3870 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3871 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3872 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3874 hi->infoline = strdup(str);
3875 str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING);
3877 hi->website = strdup(str);
3878 str = database_get_data(obj, KEY_DEVNULL, RECDB_QSTRING);
3880 hi->devnull = strdup(str);
3881 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3882 hi->registered = str ? strtoul(str, NULL, 0) : now;
3883 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3884 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3885 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3886 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3887 /* We want to read the nicks even if disable_nicks is set. This is so
3888 * that we don't lose the nick data entirely. */
3889 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3891 for (ii=0; ii<slist->used; ii++)
3892 register_nick(slist->list[ii], hi);
3894 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3896 for (ii=0; str[ii]; ii++)
3897 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3899 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3900 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3901 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3902 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3903 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3904 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3905 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3907 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3909 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3910 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3912 nickserv_set_email_addr(hi, str);
3913 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3915 hi->epithet = strdup(str);
3916 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3918 hi->fakehost = strdup(str);
3919 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
3921 hi->fakeident = strdup(str);
3922 /* Read the "cookie" sub-database (if it exists). */
3923 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3925 const char *data, *type, *expires, *cookie_str;
3926 struct handle_cookie *cookie;
3928 cookie = calloc(1, sizeof(*cookie));
3929 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3930 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3931 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3932 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3933 if (!type || !expires || !cookie_str) {
3934 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3937 if (!irccasecmp(type, KEY_ACTIVATION))
3938 cookie->type = ACTIVATION;
3939 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3940 cookie->type = PASSWORD_CHANGE;
3941 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3942 cookie->type = EMAIL_CHANGE;
3943 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3944 cookie->type = ALLOWAUTH;
3946 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3949 cookie->expires = strtoul(expires, NULL, 0);
3950 if (cookie->expires < now)
3953 cookie->data = strdup(data);
3954 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3958 nickserv_bake_cookie(cookie);
3960 nickserv_free_cookie(cookie);
3962 /* Read the "notes" sub-database (if it exists). */
3963 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3966 struct handle_note *last_note;
3967 struct handle_note *note;
3970 for (it = dict_first(subdb); it; it = iter_next(it)) {
3971 const char *expires;
3975 const char *note_id;
3978 note_id = iter_key(it);
3979 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3981 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
3984 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3985 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3986 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3987 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3988 if (!setter || !text || !set) {
3989 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
3992 note = calloc(1, sizeof(*note) + strlen(text));
3994 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3995 note->set = strtoul(set, NULL, 10);
3996 note->id = strtoul(note_id, NULL, 10);
3997 safestrncpy(note->setter, setter, sizeof(note->setter));
3998 strcpy(note->note, text);
4000 last_note->next = note;
4009 nickserv_saxdb_read(dict_t db) {
4011 struct record_data *rd;
4013 for (it=dict_first(db); it; it=iter_next(it)) {
4015 nickserv_db_read_handle(iter_key(it), rd->d.object);
4020 static NICKSERV_FUNC(cmd_mergedb)
4022 struct timeval start, stop;
4025 NICKSERV_MIN_PARMS(2);
4026 gettimeofday(&start, NULL);
4027 if (!(db = parse_database(argv[1]))) {
4028 reply("NSMSG_DB_UNREADABLE", argv[1]);
4031 nickserv_saxdb_read(db);
4033 gettimeofday(&stop, NULL);
4034 stop.tv_sec -= start.tv_sec;
4035 stop.tv_usec -= start.tv_usec;
4036 if (stop.tv_usec < 0) {
4038 stop.tv_usec += 1000000;
4040 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
4045 expire_handles(UNUSED_ARG(void *data))
4047 dict_iterator_t it, next;
4048 unsigned long expiry;
4049 struct handle_info *hi;
4051 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4052 next = iter_next(it);
4054 if ((hi->opserv_level > 0)
4056 || HANDLE_FLAGGED(hi, FROZEN)
4057 || HANDLE_FLAGGED(hi, NODELETE)) {
4060 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4061 if ((now - hi->lastseen) > expiry) {
4062 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
4063 nickserv_unregister_handle(hi, NULL);
4067 if (nickserv_conf.handle_expire_frequency)
4068 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4072 nickserv_load_dict(const char *fname)
4076 if (!(file = fopen(fname, "r"))) {
4077 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4080 while (fgets(line, sizeof(line), file)) {
4083 if (line[strlen(line)-1] == '\n')
4084 line[strlen(line)-1] = 0;
4085 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4088 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4091 static enum reclaim_action
4092 reclaim_action_from_string(const char *str) {
4094 return RECLAIM_NONE;
4095 else if (!irccasecmp(str, "warn"))
4096 return RECLAIM_WARN;
4097 else if (!irccasecmp(str, "svsnick"))
4098 return RECLAIM_SVSNICK;
4099 else if (!irccasecmp(str, "kill"))
4100 return RECLAIM_KILL;
4102 return RECLAIM_NONE;
4106 nickserv_conf_read(void)
4108 dict_t conf_node, child;
4112 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4113 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4116 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4118 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4119 if (nickserv_conf.valid_handle_regex_set)
4120 regfree(&nickserv_conf.valid_handle_regex);
4122 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4123 nickserv_conf.valid_handle_regex_set = !err;
4124 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4126 nickserv_conf.valid_handle_regex_set = 0;
4128 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4129 if (nickserv_conf.valid_nick_regex_set)
4130 regfree(&nickserv_conf.valid_nick_regex);
4132 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4133 nickserv_conf.valid_nick_regex_set = !err;
4134 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4136 nickserv_conf.valid_nick_regex_set = 0;
4138 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4140 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4141 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4142 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4143 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4144 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4145 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4146 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4147 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4148 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4149 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4150 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4151 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4152 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4153 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4154 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4155 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4156 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4157 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4158 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4159 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4160 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4161 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4162 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4163 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4164 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
4165 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
4166 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4168 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4169 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4170 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4172 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4173 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4174 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4176 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4177 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4178 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4179 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4180 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4181 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4182 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4183 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4184 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4185 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4186 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4189 nickserv_conf.ounregister_flags = 0;
4191 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4194 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4196 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4198 nickserv_conf.handle_ts_mode = TS_IGNORE;
4199 else if (!irccasecmp(str, "ircu"))
4200 nickserv_conf.handle_ts_mode = TS_IRCU;
4202 nickserv_conf.handle_ts_mode = TS_IGNORE;
4203 if (!nickserv_conf.disable_nicks) {
4204 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4205 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4206 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4207 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4208 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4209 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4210 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4211 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4213 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4214 for (it=dict_first(child); it; it=iter_next(it)) {
4215 const char *key = iter_key(it), *value;
4219 if (!strncasecmp(key, "uc_", 3))
4220 flag = toupper(key[3]);
4221 else if (!strncasecmp(key, "lc_", 3))
4222 flag = tolower(key[3]);
4226 if ((pos = handle_inverse_flags[flag])) {
4227 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4228 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4231 if (nickserv_conf.weak_password_dict)
4232 dict_delete(nickserv_conf.weak_password_dict);
4233 nickserv_conf.weak_password_dict = dict_new();
4234 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4235 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4236 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4237 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4239 nickserv_load_dict(str);
4240 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4241 if (nickserv && str)
4242 NickChange(nickserv, str, 0);
4243 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4244 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4245 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4246 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4247 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4248 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4249 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4250 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4251 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4252 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4253 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4254 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4255 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4256 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4257 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4258 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4259 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4260 titlehost_suffix = str ? str : "example.net";
4261 str = conf_get_data("server/network", RECDB_QSTRING);
4262 nickserv_conf.network_name = str ? str : "some IRC network";
4263 if (!nickserv_conf.auth_policer_params) {
4264 nickserv_conf.auth_policer_params = policer_params_new();
4265 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4266 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4268 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4269 for (it=dict_first(child); it; it=iter_next(it))
4270 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4274 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4276 char newnick[NICKLEN+1];
4285 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4287 case RECLAIM_SVSNICK:
4289 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4290 } while (GetUserH(newnick));
4291 irc_svsnick(nickserv, user, newnick);
4294 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4295 DelUser(user, nickserv, 1, msg);
4301 nickserv_reclaim_p(void *data) {
4302 struct userNode *user = data;
4303 struct nick_info *ni = get_nick_info(user->nick);
4305 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4309 check_user_nick(struct userNode *user) {
4310 struct nick_info *ni;
4311 user->modes &= ~FLAGS_REGNICK;
4312 if (!(ni = get_nick_info(user->nick)))
4314 if (user->handle_info == ni->owner) {
4315 user->modes |= FLAGS_REGNICK;
4319 if (nickserv_conf.warn_nick_owned)
4320 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4321 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4323 if (nickserv_conf.auto_reclaim_delay)
4324 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4326 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4330 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4332 struct handle_info *hi = NULL;
4335 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4336 if ((hi == NULL) && (serial != 0)) {
4338 inttobase64(id, serial, IDLEN);
4339 hi = dict_find(nickserv_id_dict, id, NULL);
4343 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4344 && (timestamp != hi->registered)) {
4347 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4350 set_user_handle_info(user, hi, 0);
4352 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4357 handle_nick_change(struct userNode *user, const char *old_nick)
4359 struct handle_info *hi;
4361 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4362 dict_remove(nickserv_allow_auth_dict, old_nick);
4363 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4365 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4366 check_user_nick(user);
4370 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4372 dict_remove(nickserv_allow_auth_dict, user->nick);
4373 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4374 set_user_handle_info(user, NULL, 0);
4377 static struct modcmd *
4378 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4380 if (min_level > 0) {
4382 sprintf(buf, "%u", min_level);
4383 if (must_be_qualified) {
4384 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4386 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4388 } else if (min_level == 0) {
4389 if (must_be_qualified) {
4390 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4392 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4395 if (must_be_qualified) {
4396 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4398 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4404 nickserv_db_cleanup(void)
4406 unreg_del_user_func(nickserv_remove_user);
4407 userList_clean(&curr_helpers);
4408 policer_params_delete(nickserv_conf.auth_policer_params);
4409 dict_delete(nickserv_handle_dict);
4410 dict_delete(nickserv_nick_dict);
4411 dict_delete(nickserv_opt_dict);
4412 dict_delete(nickserv_allow_auth_dict);
4413 dict_delete(nickserv_email_dict);
4414 dict_delete(nickserv_id_dict);
4415 dict_delete(nickserv_conf.weak_password_dict);
4416 free(auth_func_list);
4417 free(unreg_func_list);
4419 free(allowauth_func_list);
4420 free(handle_merge_func_list);
4421 free(failpw_func_list);
4422 if (nickserv_conf.valid_handle_regex_set)
4423 regfree(&nickserv_conf.valid_handle_regex);
4424 if (nickserv_conf.valid_nick_regex_set)
4425 regfree(&nickserv_conf.valid_nick_regex);
4429 init_nickserv(const char *nick)
4432 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4433 reg_new_user_func(check_user_nick);
4434 reg_nick_change_func(handle_nick_change);
4435 reg_del_user_func(nickserv_remove_user);
4436 reg_account_func(handle_account);
4438 /* set up handle_inverse_flags */
4439 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4440 for (i=0; handle_flags[i]; i++) {
4441 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4442 flag_access_levels[i] = 0;
4445 conf_register_reload(nickserv_conf_read);
4446 nickserv_opt_dict = dict_new();
4447 nickserv_email_dict = dict_new();
4448 dict_set_free_keys(nickserv_email_dict, free);
4449 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4451 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4452 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4453 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4454 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4455 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4456 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4457 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4458 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4459 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4460 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4461 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4462 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4463 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4464 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4465 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4466 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4467 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4468 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4469 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4470 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4471 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4472 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4473 if (!nickserv_conf.disable_nicks) {
4474 /* nick management commands */
4475 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4476 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4477 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4478 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4479 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4480 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4482 if (nickserv_conf.email_enabled) {
4483 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4484 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4485 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4486 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4487 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4488 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4490 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4491 /* miscellaneous commands */
4492 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4493 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4494 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4495 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4496 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4497 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4499 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4500 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4501 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4502 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4503 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4504 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4505 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
4506 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4507 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4508 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4509 dict_insert(nickserv_opt_dict, "WEBSITE", opt_website);
4510 dict_insert(nickserv_opt_dict, "DEVNULL", opt_devnull);
4511 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4512 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4513 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4514 if (titlehost_suffix) {
4515 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4516 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4517 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4519 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4520 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4521 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4522 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4524 nickserv_handle_dict = dict_new();
4525 dict_set_free_keys(nickserv_handle_dict, free);
4526 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4528 nickserv_id_dict = dict_new();
4529 dict_set_free_keys(nickserv_id_dict, free);
4531 nickserv_nick_dict = dict_new();
4532 dict_set_free_data(nickserv_nick_dict, free);
4534 nickserv_allow_auth_dict = dict_new();
4536 userList_init(&curr_helpers);
4539 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4540 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4541 nickserv_service = service_register(nickserv);
4543 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4544 reg_exit_func(nickserv_db_cleanup);
4545 if(nickserv_conf.handle_expire_frequency)
4546 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4547 message_register_table(msgtab);