1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2008 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for gag_create(), opserv_bad_channel() */
33 # include "rx/rxposix.h"
36 #define NICKSERV_CONF_NAME "services/nickserv"
38 #define KEY_DISABLE_NICKS "disable_nicks"
39 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
40 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
41 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
42 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
43 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
44 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
45 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
46 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
47 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
48 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
49 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
50 #define KEY_MODOPER_LEVEL "modoper_level"
51 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
52 #define KEY_SET_TITLE_LEVEL "set_title_level"
53 #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
54 #define KEY_SET_FAKEIDENT_LEVEL "set_fakeident_level"
55 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
56 #define KEY_FLAG_LEVELS "flag_levels"
57 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
58 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
59 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
60 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
61 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
62 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
63 #define KEY_DICT_FILE "dict_file"
64 #define KEY_NICK "nick"
65 #define KEY_LANGUAGE "language"
66 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
67 #define KEY_AUTOGAG_DURATION "autogag_duration"
68 #define KEY_AUTH_POLICER "auth_policer"
69 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
70 #define KEY_EMAIL_ENABLED "email_enabled"
71 #define KEY_EMAIL_REQUIRED "email_required"
72 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
73 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
74 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
75 #define KEY_OUNREGISTER_INACTIVE "ounregister_inactive"
76 #define KEY_OUNREGISTER_FLAGS "ounregister_flags"
77 #define KEY_HANDLE_TS_MODE "account_timestamp_mode"
80 #define KEY_PASSWD "passwd"
81 #define KEY_NICKS "nicks"
82 #define KEY_MASKS "masks"
83 #define KEY_OPSERV_LEVEL "opserv_level"
84 #define KEY_FLAGS "flags"
85 #define KEY_REGISTER_ON "register"
86 #define KEY_LAST_SEEN "lastseen"
87 #define KEY_INFO "info"
88 #define KEY_DEVNULL "devnull"
89 #define KEY_WEBSITE "website"
90 #define KEY_USERLIST_STYLE "user_style"
91 #define KEY_SCREEN_WIDTH "screen_width"
92 #define KEY_LAST_AUTHED_HOST "last_authed_host"
93 #define KEY_LAST_QUIT_HOST "last_quit_host"
94 #define KEY_EMAIL_ADDR "email_addr"
95 #define KEY_COOKIE "cookie"
96 #define KEY_COOKIE_DATA "data"
97 #define KEY_COOKIE_TYPE "type"
98 #define KEY_COOKIE_EXPIRES "expires"
99 #define KEY_ACTIVATION "activation"
100 #define KEY_PASSWORD_CHANGE "password change"
101 #define KEY_EMAIL_CHANGE "email change"
102 #define KEY_ALLOWAUTH "allowauth"
103 #define KEY_EPITHET "epithet"
104 #define KEY_TABLE_WIDTH "table_width"
105 #define KEY_MAXLOGINS "maxlogins"
106 #define KEY_FAKEHOST "fakehost"
107 #define KEY_FAKEIDENT "fakeident"
108 #define KEY_NOTES "notes"
109 #define KEY_NOTE_EXPIRES "expires"
110 #define KEY_NOTE_SET "set"
111 #define KEY_NOTE_SETTER "setter"
112 #define KEY_NOTE_NOTE "note"
113 #define KEY_KARMA "karma"
115 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
117 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
118 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
119 typedef OPTION_FUNC(option_func_t);
121 DEFINE_LIST(handle_info_list, struct handle_info*)
123 #define NICKSERV_MIN_PARMS(N) do { \
125 reply("MSG_MISSING_PARAMS", argv[0]); \
126 svccmd_send_help(user, nickserv, cmd); \
130 struct userNode *nickserv;
131 struct userList curr_helpers;
132 const char *handle_flags = HANDLE_FLAGS;
134 static struct module *nickserv_module;
135 static struct service *nickserv_service;
136 static struct log_type *NS_LOG;
137 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
138 static dict_t nickserv_id_dict; /* contains struct handle_info* */
139 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
140 static dict_t nickserv_opt_dict; /* contains option_func_t* */
141 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
142 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
143 static char handle_inverse_flags[256];
144 static unsigned int flag_access_levels[32];
145 static const struct message_entry msgtab[] = {
146 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
147 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
148 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
149 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
150 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
151 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
152 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
153 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
154 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
155 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
156 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
157 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
158 { "NSMSG_USE_COOKIE_REGISTER", "To activate your account, you must check your email for the \"cookie\" that has been mailed to it. When you have it, use the $bcookie$b command to complete registration." },
159 { "NSMSG_USE_COOKIE_RESETPASS", "A cookie has been mailed to your account's email address. You must check your email and use the $bcookie$b command to confirm the password change." },
160 { "NSMSG_USE_COOKIE_EMAIL_1", "A cookie has been mailed to the new address you requested. To finish setting your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
161 { "NSMSG_USE_COOKIE_EMAIL_2", "A cookie has been generated, and half mailed to each your old and new addresses. To finish changing your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
162 { "NSMSG_USE_COOKIE_AUTH", "A cookie has been generated and sent to your email address. Once you have checked your email and received the cookie, auth using the $bcookie$b command." },
163 { "NSMSG_COOKIE_LIVE", "Account $b%s$b already has a cookie active. Please either finish using that cookie, wait for it to expire, or auth to the account and use the $bdelcookie$b command." },
164 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
165 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
166 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
167 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
168 { "NSMSG_BAD_COOKIE", "That cookie is not the right one. Please make sure you are copying it EXACTLY from the email; it is case-sensitive, so $bABC$b is different from $babc$b." },
169 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
170 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
171 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
172 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
173 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
174 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
175 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
176 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
177 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
178 { "NSMSG_ATE_COOKIE_FOREIGN", "I ate the cookie for account $b%s$b. It may now have another." },
179 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
180 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
181 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
182 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
183 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
184 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
185 { "NSMSG_USE_AUTHCOOKIE", "Your hostmask is not valid for account $b%1$s$b. Please use the $bauthcookie$b command to grant yourself access. (/msg $S authcookie %1$s)" },
186 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
187 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
188 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
189 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
190 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
191 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
192 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
193 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
194 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
195 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
196 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
197 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
198 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
199 { "NSMSG_TITLE_TRUNCATED_RENAME", "That account name combined with the user's title would result in a truncated host; please choose a shorter account name." },
200 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
201 { "NSMSG_FAKEIDENT_INVALID", "Fake idents must be shorter than %d characters." },
202 { "NSMSG_FAKEMASK_INVALID", "Fake ident@hosts must be shorter than %d characters." },
203 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
204 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
205 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
206 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
207 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
208 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
209 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
210 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
211 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
212 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
213 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
214 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
215 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
216 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
217 { "NSMSG_HANDLEINFO_DEVNULL", " DevNull Class: %s" },
218 { "NSMSG_HANDLEINFO_WEBSITE", " Website: %s" },
219 { "NSMSG_HANDLEINFO_ACCESS", " Access: %i" },
220 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
221 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
222 { "NSMSG_HANDLEINFO_FAKEIDENT", " Fake ident: %s" },
223 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
224 { "NSMSG_HANDLEINFO_FAKEIDENTHOST", " Fake host: %s@%s" },
225 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
226 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
227 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
228 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
229 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
230 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
231 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
232 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
233 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
234 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
235 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
236 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
237 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
238 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
239 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
240 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
241 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
242 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
243 { "NSMSG_WEAK_PASSWORD", "WARNING: You are using a password that is considered weak (easy to guess). It is STRONGLY recommended you change it (now, if not sooner) by typing \"/msg $S@$s PASS oldpass newpass\" (with your current password and a new password)." },
244 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
245 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
246 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
247 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
248 { "NSMSG_AUTH_ALLOWED_MSG", "You may now authenticate to account $b%s$b by typing $b/msg $N@$s auth %s password$b (using your password). If you will be using this computer regularly, please type $b/msg $N addmask$b (AFTER you auth) to permanently add your hostmask." },
249 { "NSMSG_AUTH_ALLOWED_EMAIL", "You may also (after you auth) type $b/msg $N set email user@your.isp$b to set an email address. This will let you use the $bauthcookie$b command to be authenticated in the future." },
250 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
251 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
252 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
253 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
254 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
255 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
256 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
257 { "NSMSG_PASS_SUCCESS", "Password changed." },
258 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
259 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
260 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
261 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
262 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
263 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
264 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
265 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
266 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
267 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
268 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
269 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
270 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
271 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
272 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
273 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
274 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
275 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
276 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
277 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
278 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
279 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
280 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
281 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
282 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
283 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
284 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
285 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
286 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
287 { "NSMSG_NO_ACCESS", "Access denied." },
288 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
289 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
290 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
291 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
292 { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
293 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
294 { "NSMSG_BAD_HANDLE", "Account $b%s$b not registered because it is in use by a network service, is too long, or contains invalid characters." },
295 { "NSMSG_BAD_NICK", "Nickname $b%s$b not registered because it is in use by a network service, is too long, or contains invalid characters." },
296 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
297 { "NSMSG_FAIL_RENAME", "Account $b%s$b not renamed to $b%s$b because it is in use by a network services, or contains invalid characters." },
298 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
299 { "NSMSG_SEARCH_MATCH", "Match: %s" },
300 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
301 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
302 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
303 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
304 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
305 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
306 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
307 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
308 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
309 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
310 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
311 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
312 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
313 { "NSMSG_SET_DEVNULL", "$bDEVNULL: $b%s" },
314 { "NSMSG_SET_AUTOHIDE", "$bAUTOHIDE: $b%s" },
315 { "NSMSG_SET_WEBSITE", "$bWEBSITE: $b%s" },
316 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
317 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
318 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
319 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
320 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
321 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
322 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
323 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
324 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
325 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
326 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
327 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
328 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
329 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
330 { "NSMSG_SET_FAKEIDENT", "$bFAKEIDENT: $b%s" },
331 { "NSMSG_SET_FAKEIDENTHOST", "$bFAKEHOST: $b%s@%s" },
332 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
333 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
334 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
335 { "NSEMAIL_ACTIVATION_BODY", "This email has been sent to verify that this email address belongs to the person who tried to register an account on %1$s. Your cookie is:\n %2$s\nTo verify your email address and complete the account registration, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nThis command is only used once to complete your account registration, and never again. Once you have run this command, you will need to authenticate everytime you reconnect to the network. To do this, you will have to type this command every time you reconnect:\n /msg %3$s@%4$s AUTH %5$s your-password\n Please remember to fill in 'your-password' with the actual password you gave to us when you registered.\n\nIf you did NOT request this account, you do not need to do anything. Please contact the %1$s staff if you have questions, and be sure to check our website." },
336 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
337 { "NSEMAIL_PASSWORD_CHANGE_BODY", "This email has been sent to verify that you wish to change the password on your account %5$s. Your cookie is %2$s.\nTo complete the password change, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request your password to be changed, you do not need to do anything. Please contact the %1$s staff if you have questions." },
338 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
339 { "NSEMAIL_EMAIL_CHANGE_BODY_NEW", "This email has been sent to verify that your email address belongs to the same person as account %5$s on %1$s. The SECOND HALF of your cookie is %2$.*6$s.\nTo verify your address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s ?????%2$.*6$s\n(Replace the ????? with the FIRST HALF of the cookie, as sent to your OLD email address.)\nIf you did NOT request this email address to be associated with this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
340 { "NSEMAIL_EMAIL_CHANGE_BODY_OLD", "This email has been sent to verify that you want to change your email for account %5$s on %1$s from this address to %7$s. The FIRST HALF of your cookie is %2$.*6$s\nTo verify your new address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$.*6$s?????\n(Replace the ????? with the SECOND HALF of the cookie, as sent to your NEW email address.)\nIf you did NOT request this change of email address, you do not need to do anything. Please contact the %1$s staff if you have questions." },
341 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
342 { "NSEMAIL_EMAIL_VERIFY_BODY", "This email has been sent to verify that this address belongs to the same person as %5$s on %1$s. Your cookie is %2$s.\nTo verify your address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request this email address to be associated with this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
343 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
344 { "NSEMAIL_ALLOWAUTH_BODY", "This email has been sent to let you authenticate (auth) to account %5$s on %1$s. Your cookie is %2$s.\nTo auth to that account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request this authorization, you do not need to do anything. Please contact the %1$s staff if you have questions." },
345 { "CHECKPASS_YES", "Yes." },
346 { "CHECKPASS_NO", "No." },
347 { "CHECKEMAIL_NOT_SET", "No email set." },
348 { "CHECKEMAIL_YES", "Yes." },
349 { "CHECKEMAIL_NO", "No." },
353 enum reclaim_action {
359 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
360 static void nickserv_reclaim_p(void *data);
361 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
363 enum handle_ts_mode {
369 unsigned int disable_nicks : 1;
370 unsigned int valid_handle_regex_set : 1;
371 unsigned int valid_nick_regex_set : 1;
372 unsigned int autogag_enabled : 1;
373 unsigned int email_enabled : 1;
374 unsigned int email_required : 1;
375 unsigned int default_hostmask : 1;
376 unsigned int warn_nick_owned : 1;
377 unsigned int warn_clone_auth : 1;
378 unsigned long nicks_per_handle;
379 unsigned long password_min_length;
380 unsigned long password_min_digits;
381 unsigned long password_min_upper;
382 unsigned long password_min_lower;
383 unsigned long db_backup_frequency;
384 unsigned long handle_expire_frequency;
385 unsigned long autogag_duration;
386 unsigned long email_visible_level;
387 unsigned long cookie_timeout;
388 unsigned long handle_expire_delay;
389 unsigned long nochan_handle_expire_delay;
390 unsigned long modoper_level;
391 unsigned long set_epithet_level;
392 unsigned long set_title_level;
393 unsigned long set_fakehost_level;
394 unsigned long set_fakeident_level;
395 unsigned long handles_per_email;
396 unsigned long email_search_level;
397 const char *network_name;
398 regex_t valid_handle_regex;
399 regex_t valid_nick_regex;
400 dict_t weak_password_dict;
401 struct policer_params *auth_policer_params;
402 enum reclaim_action reclaim_action;
403 enum reclaim_action auto_reclaim_action;
404 enum handle_ts_mode handle_ts_mode;
405 unsigned long auto_reclaim_delay;
406 unsigned char default_maxlogins;
407 unsigned char hard_maxlogins;
408 unsigned long ounregister_inactive;
409 unsigned long ounregister_flags;
412 const char *titlehost_suffix = NULL;
414 /* We have 2^32 unique account IDs to use. */
415 unsigned long int highest_id = 0;
417 #define WALK_NOTES(HANDLE, PREV, NOTE) \
418 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
419 if (NOTE->expires && NOTE->expires < now) { \
420 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
422 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
426 canonicalize_hostmask(char *mask)
428 char *out = mask, *temp;
429 if ((temp = strchr(mask, '!'))) {
431 while (*temp) *out++ = *temp++;
437 static struct handle_info *
438 register_handle(const char *handle, const char *passwd, unsigned long id)
440 struct handle_info *hi;
442 char id_base64[IDLEN + 1];
445 /* Assign a unique account ID to the account; note that 0 is
446 an invalid account ID. 1 is therefore the first account ID. */
448 id = 1 + highest_id++;
450 /* Note: highest_id is and must always be the highest ID. */
451 if (id > highest_id) {
455 inttobase64(id_base64, id, IDLEN);
457 /* Make sure an account with the same ID doesn't exist. If a
458 duplicate is found, log some details and assign a new one.
459 This should be impossible, but it never hurts to expect it. */
460 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
461 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
466 hi = calloc(1, sizeof(*hi));
467 hi->userlist_style = HI_DEFAULT_STYLE;
468 hi->handle = strdup(handle);
469 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
471 dict_insert(nickserv_handle_dict, hi->handle, hi);
476 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
482 register_nick(const char *nick, struct handle_info *owner)
484 struct nick_info *ni;
485 ni = malloc(sizeof(struct nick_info));
486 safestrncpy(ni->nick, nick, sizeof(ni->nick));
488 ni->next = owner->nicks;
490 dict_insert(nickserv_nick_dict, ni->nick, ni);
494 delete_nick(struct nick_info *ni)
496 struct nick_info *last, *next;
497 struct userNode *user;
498 /* Check to see if we should mark a user as unregistered. */
499 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
500 user->modes &= ~FLAGS_REGNICK;
503 /* Remove ni from the nick_info linked list. */
504 if (ni == ni->owner->nicks) {
505 ni->owner->nicks = ni->next;
507 last = ni->owner->nicks;
513 last->next = next->next;
515 dict_remove(nickserv_nick_dict, ni->nick);
518 static unreg_func_t *unreg_func_list;
519 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
522 reg_unreg_func(unreg_func_t func)
524 if (unreg_func_used == unreg_func_size) {
525 if (unreg_func_size) {
526 unreg_func_size <<= 1;
527 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
530 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
533 unreg_func_list[unreg_func_used++] = func;
537 nickserv_free_cookie(void *data)
539 struct handle_cookie *cookie = data;
540 if (cookie->hi) cookie->hi->cookie = NULL;
541 if (cookie->data) free(cookie->data);
546 free_handle_info(void *vhi)
548 struct handle_info *hi = vhi;
551 inttobase64(id, hi->id, IDLEN);
552 dict_remove(nickserv_id_dict, id);
554 free_string_list(hi->masks);
558 delete_nick(hi->nicks);
566 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
567 nickserv_free_cookie(hi->cookie);
570 struct handle_note *note = hi->notes;
571 hi->notes = note->next;
574 if (hi->email_addr) {
575 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
576 handle_info_list_remove(hil, hi);
578 dict_remove(nickserv_email_dict, hi->email_addr);
583 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
586 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
590 for (n=0; n<unreg_func_used; n++)
591 unreg_func_list[n](notify, hi);
593 set_user_handle_info(hi->users, NULL, 0);
595 if (nickserv_conf.disable_nicks)
596 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
598 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
600 dict_remove(nickserv_handle_dict, hi->handle);
604 get_handle_info(const char *handle)
606 return dict_find(nickserv_handle_dict, handle, 0);
610 get_nick_info(const char *nick)
612 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
616 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
621 for (nn=0; nn<channel->members.used; ++nn) {
622 mn = channel->members.list[nn];
623 if ((mn->user != except) && (mn->user->handle_info == handle))
630 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
631 if (!user->handle_info) {
633 send_message(user, bot, "MSG_AUTHENTICATE");
637 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
639 send_message(user, bot, "NSMSG_NO_ACCESS");
643 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
645 send_message(user, bot, "MSG_OPER_SUSPENDED");
649 if (user->handle_info->opserv_level < min_level) {
651 send_message(user, bot, "NSMSG_NO_ACCESS");
659 is_valid_handle(const char *handle)
661 struct userNode *user;
662 /* cant register a juped nick/service nick as handle, to prevent confusion */
663 user = GetUserH(handle);
664 if (user && IsLocal(user))
666 /* check against maximum length */
667 if (strlen(handle) > NICKSERV_HANDLE_LEN)
669 /* for consistency, only allow account names that could be nicks */
670 if (!is_valid_nick(handle))
672 /* disallow account names that look like bad words */
673 if (opserv_bad_channel(handle))
675 /* test either regex or containing all valid chars */
676 if (nickserv_conf.valid_handle_regex_set) {
677 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
680 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
681 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
685 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
690 is_registerable_nick(const char *nick)
692 /* make sure it could be used as an account name */
693 if (!is_valid_handle(nick))
696 if (strlen(nick) > NICKLEN)
698 /* test either regex or as valid handle */
699 if (nickserv_conf.valid_nick_regex_set) {
700 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
703 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
704 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
712 is_valid_email_addr(const char *email)
714 return strchr(email, '@') != NULL;
718 visible_email_addr(struct userNode *user, struct handle_info *hi)
720 if (hi->email_addr) {
721 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
722 return hi->email_addr;
732 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
734 struct handle_info *hi;
735 struct userNode *target;
739 if (!(hi = get_handle_info(++name))) {
740 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
745 if (!(target = GetUserH(name))) {
746 send_message(user, service, "MSG_NICK_UNKNOWN", name);
749 if (IsLocal(target)) {
750 if (IsService(target))
751 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
753 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
756 if (!(hi = target->handle_info)) {
757 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
765 oper_outranks(struct userNode *user, struct handle_info *hi) {
766 if (user->handle_info->opserv_level > hi->opserv_level)
768 if (user->handle_info->opserv_level == hi->opserv_level) {
769 if ((user->handle_info->opserv_level == 1000)
770 || (user->handle_info == hi)
771 || ((user->handle_info->opserv_level == 0)
772 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
773 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
777 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
781 static struct handle_info *
782 get_victim_oper(struct userNode *user, const char *target)
784 struct handle_info *hi;
785 if (!(hi = smart_get_handle_info(nickserv, user, target)))
787 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
788 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
791 return oper_outranks(user, hi) ? hi : NULL;
795 valid_user_for(struct userNode *user, struct handle_info *hi)
799 /* If no hostmasks on the account, allow it. */
800 if (!hi->masks->used || IsDummy(user))
802 /* If any hostmask matches, allow it. */
803 for (ii=0; ii<hi->masks->used; ii++)
804 if (user_matches_glob(user, hi->masks->list[ii], 0))
806 /* If they are allowauthed to this account, allow it (removing the aa). */
807 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
808 dict_remove(nickserv_allow_auth_dict, user->nick);
811 /* The user is not allowed to use this account. */
816 is_secure_password(const char *handle, const char *pass, struct userNode *user)
819 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
823 if (len < nickserv_conf.password_min_length) {
825 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
828 if (!irccasecmp(pass, handle)) {
830 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
833 dict_find(nickserv_conf.weak_password_dict, pass, &p);
836 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
839 for (i=0; i<len; i++) {
840 if (isdigit(pass[i]))
842 if (isupper(pass[i]))
844 if (islower(pass[i]))
847 if ((cnt_lower < nickserv_conf.password_min_lower)
848 || (cnt_upper < nickserv_conf.password_min_upper)
849 || (cnt_digits < nickserv_conf.password_min_digits)) {
851 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
857 static auth_func_t *auth_func_list;
858 static unsigned int auth_func_size = 0, auth_func_used = 0;
861 reg_auth_func(auth_func_t func)
863 if (auth_func_used == auth_func_size) {
864 if (auth_func_size) {
865 auth_func_size <<= 1;
866 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
869 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
872 auth_func_list[auth_func_used++] = func;
875 static handle_rename_func_t *rf_list;
876 static unsigned int rf_list_size, rf_list_used;
879 reg_handle_rename_func(handle_rename_func_t func)
881 if (rf_list_used == rf_list_size) {
884 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
887 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
890 rf_list[rf_list_used++] = func;
894 generate_fakehost(struct handle_info *handle)
896 extern const char *hidden_host_suffix;
897 static char buffer[HOSTLEN+1];
899 if (!handle->fakehost) {
900 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
902 } else if (handle->fakehost[0] == '.') {
903 /* A leading dot indicates the stored value is actually a title. */
904 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, titlehost_suffix);
906 } else if (handle->fakehost[0] == '$') {
907 /* A leading $ indicates the stored value begins with the user handle. */
908 snprintf(buffer, sizeof(buffer), "%s%s", handle->handle, handle->fakehost+1);
911 return handle->fakehost;
915 generate_fakeident(struct handle_info *handle, struct userNode *user)
917 static char buffer[USERLEN+1];
919 if (!handle->fakeident) {
922 safestrncpy(buffer, user->ident, sizeof(buffer));
925 return handle->fakeident;
929 apply_fakehost(struct handle_info *handle, struct userNode *user)
931 struct userNode *target;
932 char *fakehost, *fakeident;
937 fakehost = generate_fakehost(handle);
940 fakeident = generate_fakeident(handle, user);
941 assign_fakehost(user, fakehost, fakeident, 0, 1);
945 for (target = handle->users; target; target = target->next_authed) {
946 fakeident = generate_fakeident(handle, target);
947 assign_fakehost(target, fakehost, fakeident, 0, 1);
952 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
955 struct handle_info *old_info;
957 /* This can happen if somebody uses COOKIE while authed, or if
958 * they re-auth to their current handle (which is silly, but users
960 if (user->handle_info == hi)
963 if (user->handle_info) {
964 struct userNode *other;
967 userList_remove(&curr_helpers, user);
969 /* remove from next_authed linked list */
970 if (user->handle_info->users == user) {
971 user->handle_info->users = user->next_authed;
972 } else if (user->handle_info->users != NULL) {
973 for (other = user->handle_info->users;
974 other->next_authed != user;
975 other = other->next_authed) ;
976 other->next_authed = user->next_authed;
978 /* No users authed to the account - can happen if they get
979 * killed for authing. */
981 /* if nobody left on old handle, and they're not an oper, remove !god */
982 if (!user->handle_info->users && !user->handle_info->opserv_level)
983 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
984 /* record them as being last seen at this time */
985 user->handle_info->lastseen = now;
986 /* and record their hostmask */
987 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
989 old_info = user->handle_info;
990 user->handle_info = hi;
991 if (hi && !hi->users && !hi->opserv_level)
992 HANDLE_CLEAR_FLAG(hi, HELPING);
993 for (n=0; n<auth_func_used; n++) {
994 auth_func_list[n](user, old_info);
999 struct nick_info *ni;
1001 HANDLE_CLEAR_FLAG(hi, FROZEN);
1002 if (nickserv_conf.warn_clone_auth) {
1003 struct userNode *other;
1004 for (other = hi->users; other; other = other->next_authed)
1005 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
1007 user->next_authed = hi->users;
1010 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
1011 userList_append(&curr_helpers, user);
1013 if (hi->fakehost || hi->fakeident || old_info)
1014 apply_fakehost(hi, user);
1017 if (!nickserv_conf.disable_nicks) {
1018 struct nick_info *ni2;
1019 for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
1020 if (!irccasecmp(user->nick, ni2->nick)) {
1021 user->modes |= FLAGS_REGNICK;
1026 StampUser(user, hi->handle, hi->registered, hi->id);
1029 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
1030 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
1032 /* We cannot clear the user's account ID, unfortunately. */
1033 user->next_authed = NULL;
1037 static struct handle_info*
1038 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
1040 struct handle_info *hi;
1041 struct nick_info *ni;
1042 char crypted[MD5_CRYPT_LENGTH];
1044 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
1045 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
1049 if (!is_secure_password(handle, passwd, user))
1052 cryptpass(passwd, crypted);
1053 hi = register_handle(handle, crypted, 0);
1054 hi->masks = alloc_string_list(1);
1056 hi->language = lang_C;
1057 hi->registered = now;
1059 hi->flags = HI_DEFAULT_FLAGS;
1060 if (settee && !no_auth)
1061 set_user_handle_info(settee, hi, 1);
1064 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1065 else if (nickserv_conf.disable_nicks)
1066 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1067 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1068 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1070 register_nick(user->nick, hi);
1071 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1073 if (settee && (user != settee))
1074 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1079 nickserv_bake_cookie(struct handle_cookie *cookie)
1081 cookie->hi->cookie = cookie;
1082 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1086 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1088 struct handle_cookie *cookie;
1089 char subject[128], body[4096], *misc;
1090 const char *netname, *fmt;
1094 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1098 cookie = calloc(1, sizeof(*cookie));
1100 cookie->type = type;
1101 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1102 cookie->expires = now + nickserv_conf.cookie_timeout;
1103 inttobase64(cookie->cookie, rand(), 5);
1104 inttobase64(cookie->cookie+5, rand(), 5);
1106 netname = nickserv_conf.network_name;
1109 switch (cookie->type) {
1111 hi->passwd[0] = 0; /* invalidate password */
1112 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1113 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1114 snprintf(subject, sizeof(subject), fmt, netname);
1115 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1116 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1119 case PASSWORD_CHANGE:
1120 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1121 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1122 snprintf(subject, sizeof(subject), fmt, netname);
1123 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1124 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1127 misc = hi->email_addr;
1128 hi->email_addr = cookie->data;
1130 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1131 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1132 snprintf(subject, sizeof(subject), fmt, netname);
1133 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1134 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1135 mail_send(nickserv, hi, subject, body, 1);
1136 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1137 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1139 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1140 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1141 snprintf(subject, sizeof(subject), fmt, netname);
1142 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1143 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1144 mail_send(nickserv, hi, subject, body, 1);
1147 hi->email_addr = misc;
1150 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1151 snprintf(subject, sizeof(subject), fmt, netname);
1152 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1153 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1154 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1157 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1161 mail_send(nickserv, hi, subject, body, first_time);
1162 nickserv_bake_cookie(cookie);
1166 nickserv_eat_cookie(struct handle_cookie *cookie)
1168 cookie->hi->cookie = NULL;
1169 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1170 nickserv_free_cookie(cookie);
1174 nickserv_free_email_addr(void *data)
1176 handle_info_list_clean(data);
1181 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1183 struct handle_info_list *hil;
1184 /* Remove from old handle_info_list ... */
1185 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1186 handle_info_list_remove(hil, hi);
1187 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1188 hi->email_addr = NULL;
1190 /* Add to the new list.. */
1191 if (new_email_addr) {
1192 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1193 hil = calloc(1, sizeof(*hil));
1194 hil->tag = strdup(new_email_addr);
1195 handle_info_list_init(hil);
1196 dict_insert(nickserv_email_dict, hil->tag, hil);
1198 handle_info_list_append(hil, hi);
1199 hi->email_addr = hil->tag;
1203 static NICKSERV_FUNC(cmd_register)
1206 struct handle_info *hi;
1207 const char *email_addr, *password;
1210 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1211 /* Require the first handle registered to belong to someone +o. */
1212 reply("NSMSG_REQUIRE_OPER");
1216 if (user->handle_info) {
1217 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1221 if (IsRegistering(user)) {
1222 reply("NSMSG_ALREADY_REGISTERING");
1226 if (IsStamped(user)) {
1227 /* Unauthenticated users might still have been stamped
1228 previously and could therefore have a hidden host;
1229 do not allow them to register a new account. */
1230 reply("NSMSG_STAMPED_REGISTER");
1234 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1236 if (!is_valid_handle(argv[1])) {
1237 reply("NSMSG_BAD_HANDLE", argv[1]);
1241 if ((argc >= 4) && nickserv_conf.email_enabled) {
1242 struct handle_info_list *hil;
1245 /* Remember email address. */
1246 email_addr = argv[3];
1248 /* Check that the email address looks valid.. */
1249 if (!is_valid_email_addr(email_addr)) {
1250 reply("NSMSG_BAD_EMAIL_ADDR");
1254 /* .. and that we are allowed to send to it. */
1255 if ((str = mail_prohibited_address(email_addr))) {
1256 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1260 /* If we do email verify, make sure we don't spam the address. */
1261 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1263 for (nn=0; nn<hil->used; nn++) {
1264 if (hil->list[nn]->cookie) {
1265 reply("NSMSG_EMAIL_UNACTIVATED");
1269 if (hil->used >= nickserv_conf.handles_per_email) {
1270 reply("NSMSG_EMAIL_OVERUSED");
1283 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1285 /* Add any masks they should get. */
1286 if (nickserv_conf.default_hostmask) {
1287 string_list_append(hi->masks, strdup("*@*"));
1289 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1290 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1291 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1294 /* If they're the first to register, give them level 1000. */
1295 if (dict_size(nickserv_handle_dict) == 1) {
1296 hi->opserv_level = 1000;
1297 reply("NSMSG_ROOT_HANDLE", argv[1]);
1300 /* Set their email address. */
1302 nickserv_set_email_addr(hi, email_addr);
1304 /* If they need to do email verification, tell them. */
1306 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1308 /* Set registering flag.. */
1309 user->modes |= FLAGS_REGISTERING;
1314 static NICKSERV_FUNC(cmd_oregister)
1317 struct userNode *settee;
1318 struct handle_info *hi;
1319 const char *pass, *email;
1321 NICKSERV_MIN_PARMS(3);
1326 if (!is_valid_handle(argv[1])) {
1327 reply("NSMSG_BAD_HANDLE", argv[1]);
1331 if (argc < 5 || !nickserv_conf.email_enabled) {
1336 if (!is_valid_email_addr(email)) {
1337 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
1340 if ((str = mail_prohibited_address(email))) {
1341 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", email, str);
1346 if (argc < 4 || !strcmp(argv[3], "*")) {
1349 } else if (strchr(argv[3], '@')) {
1350 mask = canonicalize_hostmask(strdup(argv[3]));
1352 settee = GetUserH(argv[4]);
1354 reply("MSG_NICK_UNKNOWN", argv[4]);
1361 } else if ((settee = GetUserH(argv[3]))) {
1362 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1364 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1367 if (settee && settee->handle_info) {
1368 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1372 if (!(hi = nickserv_register(user, settee, argv[1], pass, 0))) {
1377 string_list_append(hi->masks, mask);
1379 nickserv_set_email_addr(hi, email);
1383 static NICKSERV_FUNC(cmd_handleinfo)
1386 unsigned int i, pos=0, herelen;
1387 struct userNode *target, *next_un;
1388 struct handle_info *hi;
1389 const char *nsmsg_none;
1393 if (!(hi = user->handle_info)) {
1394 reply("NSMSG_MUST_AUTH");
1397 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1401 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1402 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1403 feh = hi->registered;
1404 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1407 intervalString(buff, now - hi->lastseen, user->handle_info);
1408 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1410 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1413 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1414 if (oper_has_access(user, cmd->parent->bot, 200, 1))
1415 reply("NSMSG_HANDLEINFO_DEVNULL", (hi->devnull ? hi->devnull : nsmsg_none));
1416 if (user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1417 reply("NSMSG_HANDLEINFO_WEBSITE", (hi->website ? hi->website : nsmsg_none));
1418 if(hi->opserv_level > 0 && user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1419 reply("NSMSG_HANDLEINFO_ACCESS", hi->opserv_level);
1420 if (HANDLE_FLAGGED(hi, FROZEN))
1421 reply("NSMSG_HANDLEINFO_VACATION");
1423 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1424 struct do_not_register *dnr;
1425 if ((dnr = chanserv_is_dnr(NULL, hi)))
1426 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1427 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1429 } else if (hi != user->handle_info)
1433 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1435 if (nickserv_conf.email_enabled)
1436 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1440 switch (hi->cookie->type) {
1441 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1442 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1443 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1444 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1445 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1450 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1451 reply("NSMSG_HANDLEINFO_ID", hi->id);
1453 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1455 reply("NSMSG_HANDLEINFO_NO_NOTES");
1457 struct handle_note *prev, *note;
1459 WALK_NOTES(hi, prev, note) {
1460 char set_time[INTERVALLEN];
1461 intervalString(set_time, now - note->set, user->handle_info);
1462 if (note->expires) {
1463 char exp_time[INTERVALLEN];
1464 intervalString(exp_time, note->expires - now, user->handle_info);
1465 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1467 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1474 unsigned long flen = 1;
1475 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1477 for (i=0, flen=1; handle_flags[i]; i++)
1478 if (hi->flags & 1 << i)
1479 flags[flen++] = handle_flags[i];
1481 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1483 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1486 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1487 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1488 || (hi->opserv_level > 0)) {
1489 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1492 if (hi->fakeident && hi->fakehost)
1493 reply("NSMSG_HANDLEINFO_FAKEIDENTHOST", hi->fakeident, hi->fakehost);
1494 else if (hi->fakeident)
1495 reply("NSMSG_HANDLEINFO_FAKEIDENT", hi->fakeident);
1496 else if (hi->fakehost)
1497 reply("NSMSG_HANDLEINFO_FAKEHOST", hi->fakehost);
1499 if (hi->last_quit_host[0])
1500 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1502 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1504 if (nickserv_conf.disable_nicks) {
1505 /* nicks disabled; don't show anything about registered nicks */
1506 } else if (hi->nicks) {
1507 struct nick_info *ni, *next_ni;
1508 for (ni = hi->nicks; ni; ni = next_ni) {
1509 herelen = strlen(ni->nick);
1510 if (pos + herelen + 1 > ArrayLength(buff)) {
1512 goto print_nicks_buff;
1516 memcpy(buff+pos, ni->nick, herelen);
1517 pos += herelen; buff[pos++] = ' ';
1521 reply("NSMSG_HANDLEINFO_NICKS", buff);
1526 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1529 if (hi->masks->used) {
1530 for (i=0; i < hi->masks->used; i++) {
1531 herelen = strlen(hi->masks->list[i]);
1532 if (pos + herelen + 1 > ArrayLength(buff)) {
1534 goto print_mask_buff;
1536 memcpy(buff+pos, hi->masks->list[i], herelen);
1537 pos += herelen; buff[pos++] = ' ';
1538 if (i+1 == hi->masks->used) {
1541 reply("NSMSG_HANDLEINFO_MASKS", buff);
1546 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1550 struct userData *chan, *next;
1553 for (chan = hi->channels; chan; chan = next) {
1554 next = chan->u_next;
1555 name = chan->channel->channel->name;
1556 herelen = strlen(name);
1557 if (pos + herelen + 7 > ArrayLength(buff)) {
1559 goto print_chans_buff;
1561 if (IsUserSuspended(chan))
1563 pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
1567 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1572 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1575 for (target = hi->users; target; target = next_un) {
1576 herelen = strlen(target->nick);
1577 if (pos + herelen + 1 > ArrayLength(buff)) {
1579 goto print_cnick_buff;
1581 next_un = target->next_authed;
1583 memcpy(buff+pos, target->nick, herelen);
1584 pos += herelen; buff[pos++] = ' ';
1588 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1593 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1596 static NICKSERV_FUNC(cmd_userinfo)
1598 struct userNode *target;
1600 NICKSERV_MIN_PARMS(2);
1601 if (!(target = GetUserH(argv[1]))) {
1602 reply("MSG_NICK_UNKNOWN", argv[1]);
1605 if (target->handle_info)
1606 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1608 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1612 static NICKSERV_FUNC(cmd_nickinfo)
1614 struct nick_info *ni;
1616 NICKSERV_MIN_PARMS(2);
1617 if (!(ni = get_nick_info(argv[1]))) {
1618 reply("MSG_NICK_UNKNOWN", argv[1]);
1621 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1625 static NICKSERV_FUNC(cmd_notes)
1627 struct handle_info *hi;
1628 struct handle_note *prev, *note;
1631 NICKSERV_MIN_PARMS(2);
1632 if (!(hi = get_victim_oper(user, argv[1])))
1635 WALK_NOTES(hi, prev, note) {
1636 char set_time[INTERVALLEN];
1637 intervalString(set_time, now - note->set, user->handle_info);
1638 if (note->expires) {
1639 char exp_time[INTERVALLEN];
1640 intervalString(exp_time, note->expires - now, user->handle_info);
1641 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1643 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1647 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1651 static NICKSERV_FUNC(cmd_rename_handle)
1653 struct handle_info *hi;
1654 char msgbuf[MAXLEN], *old_handle;
1657 NICKSERV_MIN_PARMS(3);
1658 if (!(hi = get_victim_oper(user, argv[1])))
1660 if (!is_valid_handle(argv[2])) {
1661 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1664 if (get_handle_info(argv[2])) {
1665 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1668 if (hi->fakehost && hi->fakehost[0] == '.' &&
1669 (strlen(argv[2]) + strlen(hi->fakehost+1) +
1670 strlen(titlehost_suffix) + 2) > HOSTLEN) {
1671 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED_RENAME");
1675 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1676 hi->handle = strdup(argv[2]);
1677 dict_insert(nickserv_handle_dict, hi->handle, hi);
1678 for (nn=0; nn<rf_list_used; nn++)
1679 rf_list[nn](hi, old_handle);
1680 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1681 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1682 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1684 apply_fakehost(hi, NULL);
1688 static failpw_func_t *failpw_func_list;
1689 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1692 reg_failpw_func(failpw_func_t func)
1694 if (failpw_func_used == failpw_func_size) {
1695 if (failpw_func_size) {
1696 failpw_func_size <<= 1;
1697 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1699 failpw_func_size = 8;
1700 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1703 failpw_func_list[failpw_func_used++] = func;
1706 static NICKSERV_FUNC(cmd_auth)
1708 int pw_arg, used, maxlogins;
1709 struct handle_info *hi;
1711 struct userNode *other;
1713 if (user->handle_info) {
1714 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1717 if (IsStamped(user)) {
1718 /* Unauthenticated users might still have been stamped
1719 previously and could therefore have a hidden host;
1720 do not allow them to authenticate. */
1721 reply("NSMSG_STAMPED_AUTH");
1725 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1727 } else if (argc == 2) {
1728 if (nickserv_conf.disable_nicks) {
1729 if (!(hi = get_handle_info(user->nick))) {
1730 reply("NSMSG_HANDLE_NOT_FOUND");
1734 /* try to look up their handle from their nick */
1735 struct nick_info *ni;
1736 ni = get_nick_info(user->nick);
1738 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1745 reply("MSG_MISSING_PARAMS", argv[0]);
1746 svccmd_send_help(user, nickserv, cmd);
1750 reply("NSMSG_HANDLE_NOT_FOUND");
1753 /* Responses from here on look up the language used by the handle they asked about. */
1754 passwd = argv[pw_arg];
1755 if (!valid_user_for(user, hi)) {
1756 if (hi->email_addr && nickserv_conf.email_enabled)
1757 send_message_type(4, user, cmd->parent->bot,
1758 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1761 send_message_type(4, user, cmd->parent->bot,
1762 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1764 argv[pw_arg] = "BADMASK";
1767 if (!checkpass(passwd, hi->passwd)) {
1769 send_message_type(4, user, cmd->parent->bot,
1770 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1771 argv[pw_arg] = "BADPASS";
1772 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1773 if (nickserv_conf.autogag_enabled) {
1774 if (!user->auth_policer.params) {
1775 user->auth_policer.last_req = now;
1776 user->auth_policer.params = nickserv_conf.auth_policer_params;
1778 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1780 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1781 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1782 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1784 argv[pw_arg] = "GAGGED";
1789 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1790 send_message_type(4, user, cmd->parent->bot,
1791 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1792 argv[pw_arg] = "SUSPENDED";
1795 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1796 for (used = 0, other = hi->users; other; other = other->next_authed) {
1797 if (++used >= maxlogins) {
1798 send_message_type(4, user, cmd->parent->bot,
1799 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1801 argv[pw_arg] = "MAXLOGINS";
1805 if (HANDLE_FLAGGED(hi, AUTOHIDE)) {
1806 //ok we have a fakehost set... but we need to set mode +x
1807 irc_svsmode(nickserv,user,"+x");
1810 set_user_handle_info(user, hi, 1);
1811 if (nickserv_conf.email_required && !hi->email_addr)
1812 reply("NSMSG_PLEASE_SET_EMAIL");
1813 if (!is_secure_password(hi->handle, passwd, NULL))
1814 reply("NSMSG_WEAK_PASSWORD");
1815 if (hi->passwd[0] != '$')
1816 cryptpass(passwd, hi->passwd);
1817 if (!hi->masks->used) {
1819 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1820 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1821 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1823 argv[pw_arg] = "****";
1824 reply("NSMSG_AUTH_SUCCESS");
1828 struct handle_info *checklogin(const char *user, const char *pass, const char *numeric, const char *hostmask)
1830 struct handle_info *hi;
1831 hi = dict_find(nickserv_handle_dict, user, NULL);
1834 if(!checkpass(pass, hi->passwd))
1836 if (HANDLE_FLAGGED(hi, SUSPENDED))
1838 /** following in one of the next commits
1839 struct last_login *login,*clogin,*old;
1840 unsigned int ii = 0;
1841 login = calloc(1, sizeof(*login));
1842 login->last_login = hi->last_login;
1843 login->hostmask = strdup(hostmask);
1844 login->authtime = now;
1845 login->quittime = 0;
1848 login->loc_pending = strdup(numeric);
1849 for (clogin = hi->last_login; clogin != NULL && ii < 9; clogin = clogin->last_login) {
1850 if(ii == 8 && clogin->last_login) {
1851 old = clogin->last_login;
1852 clogin->last_login = NULL;
1853 free(old->hostmask);
1856 if(old->loc_pending)
1857 free(old->loc_pending);
1861 hi->last_login = login;
1866 char *getfakehost(const char *user)
1868 struct handle_info *hi;
1869 hi = dict_find(nickserv_handle_dict, user, NULL);
1872 return generate_fakehost(hi);
1875 static allowauth_func_t *allowauth_func_list;
1876 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1879 reg_allowauth_func(allowauth_func_t func)
1881 if (allowauth_func_used == allowauth_func_size) {
1882 if (allowauth_func_size) {
1883 allowauth_func_size <<= 1;
1884 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1886 allowauth_func_size = 8;
1887 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1890 allowauth_func_list[allowauth_func_used++] = func;
1893 static NICKSERV_FUNC(cmd_allowauth)
1895 struct userNode *target;
1896 struct handle_info *hi;
1899 NICKSERV_MIN_PARMS(2);
1900 if (!(target = GetUserH(argv[1]))) {
1901 reply("MSG_NICK_UNKNOWN", argv[1]);
1904 if (target->handle_info) {
1905 reply("NSMSG_USER_PREV_AUTH", target->nick);
1908 if (IsStamped(target)) {
1909 /* Unauthenticated users might still have been stamped
1910 previously and could therefore have a hidden host;
1911 do not allow them to authenticate to an account. */
1912 reply("NSMSG_USER_PREV_STAMP", target->nick);
1917 else if (!(hi = get_handle_info(argv[2]))) {
1918 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1922 if (hi->opserv_level > user->handle_info->opserv_level) {
1923 reply("MSG_USER_OUTRANKED", hi->handle);
1926 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1927 || (hi->opserv_level > 0))
1928 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1929 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1932 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1933 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1934 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1935 if (nickserv_conf.email_enabled)
1936 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1938 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1939 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1941 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1943 for (n=0; n<allowauth_func_used; n++)
1944 allowauth_func_list[n](user, target, hi);
1948 static NICKSERV_FUNC(cmd_authcookie)
1950 struct handle_info *hi;
1952 NICKSERV_MIN_PARMS(2);
1953 if (user->handle_info) {
1954 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1957 if (IsStamped(user)) {
1958 /* Unauthenticated users might still have been stamped
1959 previously and could therefore have a hidden host;
1960 do not allow them to authenticate to an account. */
1961 reply("NSMSG_STAMPED_AUTHCOOKIE");
1964 if (!(hi = get_handle_info(argv[1]))) {
1965 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1968 if (!hi->email_addr) {
1969 reply("MSG_SET_EMAIL_ADDR");
1972 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1976 static NICKSERV_FUNC(cmd_delcookie)
1978 struct handle_info *hi;
1980 hi = user->handle_info;
1982 reply("NSMSG_NO_COOKIE");
1985 switch (hi->cookie->type) {
1988 reply("NSMSG_MUST_TIME_OUT");
1991 nickserv_eat_cookie(hi->cookie);
1992 reply("NSMSG_ATE_COOKIE");
1998 static NICKSERV_FUNC(cmd_odelcookie)
2000 struct handle_info *hi;
2002 NICKSERV_MIN_PARMS(2);
2004 if (!(hi = get_victim_oper(user, argv[1])))
2008 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
2012 nickserv_eat_cookie(hi->cookie);
2013 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
2018 static NICKSERV_FUNC(cmd_resetpass)
2020 struct handle_info *hi;
2021 char crypted[MD5_CRYPT_LENGTH];
2023 NICKSERV_MIN_PARMS(3);
2024 if (user->handle_info) {
2025 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2028 if (IsStamped(user)) {
2029 /* Unauthenticated users might still have been stamped
2030 previously and could therefore have a hidden host;
2031 do not allow them to activate an account. */
2032 reply("NSMSG_STAMPED_RESETPASS");
2035 if (!(hi = get_handle_info(argv[1]))) {
2036 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2039 if (!hi->email_addr) {
2040 reply("MSG_SET_EMAIL_ADDR");
2043 cryptpass(argv[2], crypted);
2045 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
2049 static NICKSERV_FUNC(cmd_cookie)
2051 struct handle_info *hi;
2054 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
2057 NICKSERV_MIN_PARMS(3);
2058 if (!(hi = get_handle_info(argv[1]))) {
2059 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2065 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2066 reply("NSMSG_HANDLE_SUSPENDED");
2071 reply("NSMSG_NO_COOKIE");
2075 /* Check validity of operation before comparing cookie to
2076 * prohibit guessing by authed users. */
2077 if (user->handle_info
2078 && (hi->cookie->type != EMAIL_CHANGE)
2079 && (hi->cookie->type != PASSWORD_CHANGE)) {
2080 reply("NSMSG_CANNOT_COOKIE");
2084 if (strcmp(cookie, hi->cookie->cookie)) {
2085 reply("NSMSG_BAD_COOKIE");
2089 switch (hi->cookie->type) {
2091 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2092 set_user_handle_info(user, hi, 1);
2093 reply("NSMSG_HANDLE_ACTIVATED");
2095 case PASSWORD_CHANGE:
2096 set_user_handle_info(user, hi, 1);
2097 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2098 reply("NSMSG_PASSWORD_CHANGED");
2101 nickserv_set_email_addr(hi, hi->cookie->data);
2102 reply("NSMSG_EMAIL_CHANGED");
2105 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2106 set_user_handle_info(user, hi, 1);
2107 nickserv_addmask(user, hi, mask);
2108 reply("NSMSG_AUTH_SUCCESS");
2113 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2114 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2118 nickserv_eat_cookie(hi->cookie);
2123 static NICKSERV_FUNC(cmd_oregnick) {
2125 struct handle_info *target;
2126 struct nick_info *ni;
2128 NICKSERV_MIN_PARMS(3);
2129 if (!(target = modcmd_get_handle_info(user, argv[1])))
2132 if (!is_registerable_nick(nick)) {
2133 reply("NSMSG_BAD_NICK", nick);
2136 ni = dict_find(nickserv_nick_dict, nick, NULL);
2138 reply("NSMSG_NICK_EXISTS", nick);
2141 register_nick(nick, target);
2142 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2146 static NICKSERV_FUNC(cmd_regnick) {
2148 struct nick_info *ni;
2150 if (!is_registerable_nick(user->nick)) {
2151 reply("NSMSG_BAD_NICK", user->nick);
2154 /* count their nicks, see if it's too many */
2155 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2156 if (n >= nickserv_conf.nicks_per_handle) {
2157 reply("NSMSG_TOO_MANY_NICKS");
2160 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2162 reply("NSMSG_NICK_EXISTS", user->nick);
2165 register_nick(user->nick, user->handle_info);
2166 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2170 static NICKSERV_FUNC(cmd_pass)
2172 struct handle_info *hi;
2173 const char *old_pass, *new_pass;
2175 NICKSERV_MIN_PARMS(3);
2176 hi = user->handle_info;
2180 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2181 if (!checkpass(old_pass, hi->passwd)) {
2182 argv[1] = "BADPASS";
2183 reply("NSMSG_PASSWORD_INVALID");
2186 cryptpass(new_pass, hi->passwd);
2188 reply("NSMSG_PASS_SUCCESS");
2193 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2196 char *new_mask = canonicalize_hostmask(strdup(mask));
2197 for (i=0; i<hi->masks->used; i++) {
2198 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2199 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2204 string_list_append(hi->masks, new_mask);
2205 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2209 static NICKSERV_FUNC(cmd_addmask)
2212 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2213 int res = nickserv_addmask(user, user->handle_info, mask);
2217 if (!is_gline(argv[1])) {
2218 reply("NSMSG_MASK_INVALID", argv[1]);
2221 return nickserv_addmask(user, user->handle_info, argv[1]);
2225 static NICKSERV_FUNC(cmd_oaddmask)
2227 struct handle_info *hi;
2229 NICKSERV_MIN_PARMS(3);
2230 if (!(hi = get_victim_oper(user, argv[1])))
2232 return nickserv_addmask(user, hi, argv[2]);
2236 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2239 for (i=0; i<hi->masks->used; i++) {
2240 if (!strcmp(del_mask, hi->masks->list[i])) {
2241 char *old_mask = hi->masks->list[i];
2242 if (hi->masks->used == 1 && !force) {
2243 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2246 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2247 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2252 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2256 static NICKSERV_FUNC(cmd_delmask)
2258 NICKSERV_MIN_PARMS(2);
2259 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2262 static NICKSERV_FUNC(cmd_odelmask)
2264 struct handle_info *hi;
2265 NICKSERV_MIN_PARMS(3);
2266 if (!(hi = get_victim_oper(user, argv[1])))
2268 return nickserv_delmask(user, hi, argv[2], 1);
2272 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2273 unsigned int nn, add = 1, pos;
2274 unsigned long added, removed, flag;
2276 for (added=removed=nn=0; str[nn]; nn++) {
2278 case '+': add = 1; break;
2279 case '-': add = 0; break;
2281 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2282 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2285 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2286 /* cheesy avoidance of looking up the flag name.. */
2287 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2290 flag = 1 << (pos - 1);
2292 added |= flag, removed &= ~flag;
2294 removed |= flag, added &= ~flag;
2299 *premoved = removed;
2304 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2306 unsigned long before, after, added, removed;
2307 struct userNode *uNode;
2309 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2310 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2312 hi->flags = (hi->flags | added) & ~removed;
2313 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2315 /* Strip helping flag if they're only a support helper and not
2316 * currently in #support. */
2317 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2318 struct channelList *schannels;
2320 schannels = chanserv_support_channels();
2321 for (ii = 0; ii < schannels->used; ++ii)
2322 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2324 if (ii == schannels->used)
2325 HANDLE_CLEAR_FLAG(hi, HELPING);
2328 if (after && !before) {
2329 /* Add user to current helper list. */
2330 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2331 userList_append(&curr_helpers, uNode);
2332 } else if (!after && before) {
2333 /* Remove user from current helper list. */
2334 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2335 userList_remove(&curr_helpers, uNode);
2342 set_list(struct userNode *user, struct handle_info *hi, int override)
2346 char *set_display[] = {
2347 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2348 "EMAIL", "MAXLOGINS", "LANGUAGE", "DEVNULL"
2351 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2353 /* Do this so options are presented in a consistent order. */
2354 for (i = 0; i < ArrayLength(set_display); ++i)
2355 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2356 opt(user, hi, override, 0, NULL);
2359 static NICKSERV_FUNC(cmd_set)
2361 struct handle_info *hi;
2364 hi = user->handle_info;
2366 set_list(user, hi, 0);
2369 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2370 reply("NSMSG_INVALID_OPTION", argv[1]);
2373 return opt(user, hi, 0, argc-1, argv+1);
2376 static NICKSERV_FUNC(cmd_oset)
2378 struct handle_info *hi;
2379 struct svccmd *subcmd;
2381 char cmdname[MAXLEN];
2383 NICKSERV_MIN_PARMS(2);
2385 if (!(hi = get_victim_oper(user, argv[1])))
2389 set_list(user, hi, 0);
2393 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2394 reply("NSMSG_INVALID_OPTION", argv[2]);
2398 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2399 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2400 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2403 return opt(user, hi, 1, argc-2, argv+2);
2406 static OPTION_FUNC(opt_info)
2410 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2412 hi->infoline = NULL;
2414 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2418 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2419 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2423 static OPTION_FUNC(opt_devnull)
2425 const char *devnull;
2429 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2432 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2436 devnull = unsplit_string(argv+1, argc-1, NULL);
2437 if(devnull_check(devnull) == 1) {
2438 hi->devnull = strdup(devnull);
2443 devnull = hi->devnull ? hi->devnull : user_find_message(user, "MSG_NONE");
2444 send_message(user, nickserv, "NSMSG_SET_DEVNULL", devnull);
2448 void nickserv_devnull_delete(char *name) {
2450 struct handle_info *hi;
2452 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2454 if (hi->devnull && !irccasecmp(name, hi->devnull)) {
2461 void nickserv_devnull_rename(char *oldname, char *newname) {
2463 struct handle_info *hi;
2465 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2467 if (hi->devnull && !irccasecmp(oldname, hi->devnull)) {
2468 hi->devnull = strdup(newname);
2473 static OPTION_FUNC(opt_website)
2475 const char *website;
2478 if (!HANDLE_FLAGGED(user->handle_info, BOT)) {
2479 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2482 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2486 website = unsplit_string(argv+1, argc-1, NULL);
2487 hi->website = strdup(website);
2490 if (HANDLE_FLAGGED(user->handle_info, BOT)) {
2491 website = hi->website ? hi->website : user_find_message(user, "MSG_NONE");
2492 send_message(user, nickserv, "NSMSG_SET_WEBSITE", website);
2497 static OPTION_FUNC(opt_width)
2500 hi->screen_width = strtoul(argv[1], NULL, 0);
2502 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2503 hi->screen_width = MIN_LINE_SIZE;
2504 else if (hi->screen_width > MAX_LINE_SIZE)
2505 hi->screen_width = MAX_LINE_SIZE;
2507 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2511 static OPTION_FUNC(opt_tablewidth)
2514 hi->table_width = strtoul(argv[1], NULL, 0);
2516 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2517 hi->table_width = MIN_LINE_SIZE;
2518 else if (hi->screen_width > MAX_LINE_SIZE)
2519 hi->table_width = MAX_LINE_SIZE;
2521 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2525 static OPTION_FUNC(opt_color)
2528 if (enabled_string(argv[1]))
2529 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2530 else if (disabled_string(argv[1]))
2531 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2533 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2538 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2542 static OPTION_FUNC(opt_privmsg)
2545 if (enabled_string(argv[1]))
2546 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2547 else if (disabled_string(argv[1]))
2548 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2550 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2555 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2559 static OPTION_FUNC(opt_autohide)
2562 if (enabled_string(argv[1]))
2563 HANDLE_SET_FLAG(hi, AUTOHIDE);
2564 else if (disabled_string(argv[1]))
2565 HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
2567 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2572 send_message(user, nickserv, "NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
2576 static OPTION_FUNC(opt_style)
2581 if (!irccasecmp(argv[1], "Zoot"))
2582 hi->userlist_style = HI_STYLE_ZOOT;
2583 else if (!irccasecmp(argv[1], "def"))
2584 hi->userlist_style = HI_STYLE_DEF;
2587 switch (hi->userlist_style) {
2596 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2600 static OPTION_FUNC(opt_password)
2603 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2608 cryptpass(argv[1], hi->passwd);
2610 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2614 static OPTION_FUNC(opt_flags)
2617 unsigned int ii, flen;
2620 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2625 nickserv_apply_flags(user, hi, argv[1]);
2627 for (ii = flen = 0; handle_flags[ii]; ii++)
2628 if (hi->flags & (1 << ii))
2629 flags[flen++] = handle_flags[ii];
2632 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2634 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2638 static OPTION_FUNC(opt_email)
2642 if (!is_valid_email_addr(argv[1])) {
2643 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2646 if ((str = mail_prohibited_address(argv[1]))) {
2647 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2650 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2651 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2653 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2655 nickserv_set_email_addr(hi, argv[1]);
2657 nickserv_eat_cookie(hi->cookie);
2658 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2661 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2665 static OPTION_FUNC(opt_maxlogins)
2667 unsigned char maxlogins;
2669 maxlogins = strtoul(argv[1], NULL, 0);
2670 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2671 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2674 hi->maxlogins = maxlogins;
2676 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2677 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2681 static OPTION_FUNC(opt_language)
2683 struct language *lang;
2685 lang = language_find(argv[1]);
2686 if (irccasecmp(lang->name, argv[1]))
2687 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2688 hi->language = lang;
2690 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2694 static OPTION_FUNC(opt_karma)
2697 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2702 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2703 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2704 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2705 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2707 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2711 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2716 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2717 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2719 if ((user->handle_info->opserv_level < target->opserv_level)
2720 || ((user->handle_info->opserv_level == target->opserv_level)
2721 && (user->handle_info->opserv_level < 1000))) {
2722 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2725 if ((user->handle_info->opserv_level < new_level)
2726 || ((user->handle_info->opserv_level == new_level)
2727 && (user->handle_info->opserv_level < 1000))) {
2728 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2731 if (user->handle_info == target) {
2732 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2735 if (target->opserv_level == new_level)
2737 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2738 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2739 target->opserv_level = new_level;
2743 static OPTION_FUNC(opt_level)
2748 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2752 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2753 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2757 static OPTION_FUNC(opt_epithet)
2760 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2764 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2765 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2768 if ((epithet[0] == '*') && !epithet[1])
2771 hi->epithet = strdup(epithet);
2775 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2777 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2781 static OPTION_FUNC(opt_title)
2786 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2790 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2792 if (strchr(title, '.')) {
2793 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2796 if ((strlen(user->handle_info->handle) + strlen(title) +
2797 strlen(titlehost_suffix) + 2) > HOSTLEN) {
2798 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2803 if (!strcmp(title, "*")) {
2804 hi->fakehost = NULL;
2806 hi->fakehost = malloc(strlen(title)+2);
2807 hi->fakehost[0] = '.';
2808 strcpy(hi->fakehost+1, title);
2810 apply_fakehost(hi, NULL);
2811 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2812 title = hi->fakehost + 1;
2816 title = user_find_message(user, "MSG_NONE");
2817 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2821 static OPTION_FUNC(opt_fakehost)
2823 char mask[USERLEN + HOSTLEN + 2];
2827 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2831 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2832 if(strlen(argv[1]) >= sizeof(mask)) {
2833 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
2837 safestrncpy(mask, argv[1], sizeof(mask));
2839 if ((host = strrchr(mask, '@')) && host != mask) {
2840 /* If ident@host was used and the user doesn't have access to set idents, do not change anything. */
2841 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2853 if (ident && strlen(ident) > USERLEN) {
2854 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2858 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
2859 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2863 if (host && host[0]) {
2865 if (!strcmp(host, "*"))
2866 hi->fakehost = NULL;
2868 hi->fakehost = strdup(host);
2869 host = hi->fakehost;
2872 host = generate_fakehost(hi);
2875 free(hi->fakeident);
2876 if (!strcmp(ident, "*"))
2877 hi->fakeident = NULL;
2879 hi->fakeident = strdup(ident);
2880 ident = hi->fakeident;
2883 ident = generate_fakeident(hi, NULL);
2885 apply_fakehost(hi, NULL);
2887 host = generate_fakehost(hi);
2888 ident = generate_fakeident(hi, NULL);
2891 host = (char *) user_find_message(user, "MSG_NONE");
2893 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
2895 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
2899 static OPTION_FUNC(opt_fakeident)
2904 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2908 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2910 if (strlen(ident) > USERLEN) {
2911 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2914 free(hi->fakeident);
2915 if (!strcmp(ident, "*"))
2916 hi->fakeident = NULL;
2918 hi->fakeident = strdup(ident);
2919 ident = hi->fakeident;
2920 apply_fakehost(hi, NULL);
2922 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
2924 ident = user_find_message(user, "MSG_NONE");
2925 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
2929 static NICKSERV_FUNC(cmd_reclaim)
2931 struct handle_info *hi;
2932 struct nick_info *ni;
2933 struct userNode *victim;
2935 NICKSERV_MIN_PARMS(2);
2936 hi = user->handle_info;
2937 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2939 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2942 if (ni->owner != user->handle_info) {
2943 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2946 victim = GetUserH(ni->nick);
2948 reply("MSG_NICK_UNKNOWN", ni->nick);
2951 if (victim == user) {
2952 reply("NSMSG_NICK_USER_YOU");
2955 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2956 switch (nickserv_conf.reclaim_action) {
2957 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2958 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2959 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2960 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2965 static NICKSERV_FUNC(cmd_unregnick)
2968 struct handle_info *hi;
2969 struct nick_info *ni;
2971 hi = user->handle_info;
2972 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2973 ni = dict_find(nickserv_nick_dict, nick, NULL);
2975 reply("NSMSG_UNKNOWN_NICK", nick);
2978 if (hi != ni->owner) {
2979 reply("NSMSG_NOT_YOUR_NICK", nick);
2982 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2987 static NICKSERV_FUNC(cmd_ounregnick)
2989 struct nick_info *ni;
2991 NICKSERV_MIN_PARMS(2);
2992 if (!(ni = get_nick_info(argv[1]))) {
2993 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2996 if (!oper_outranks(user, ni->owner))
2998 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3003 static NICKSERV_FUNC(cmd_unregister)
3005 struct handle_info *hi;
3008 NICKSERV_MIN_PARMS(2);
3009 hi = user->handle_info;
3012 if (checkpass(passwd, hi->passwd)) {
3013 nickserv_unregister_handle(hi, user);
3016 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3017 reply("NSMSG_PASSWORD_INVALID");
3022 static NICKSERV_FUNC(cmd_ounregister)
3024 struct handle_info *hi;
3025 char reason[MAXLEN];
3028 NICKSERV_MIN_PARMS(2);
3029 if (!(hi = get_victim_oper(user, argv[1])))
3032 if (HANDLE_FLAGGED(hi, NODELETE)) {
3033 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
3037 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
3039 ((hi->flags & nickserv_conf.ounregister_flags)
3041 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
3042 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
3046 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
3047 global_message(MESSAGE_RECIPIENT_STAFF, reason);
3048 nickserv_unregister_handle(hi, user);
3052 static NICKSERV_FUNC(cmd_status)
3054 if (nickserv_conf.disable_nicks) {
3055 reply("NSMSG_GLOBAL_STATS_NONICK",
3056 dict_size(nickserv_handle_dict));
3058 if (user->handle_info) {
3060 struct nick_info *ni;
3061 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
3062 reply("NSMSG_HANDLE_STATS", cnt);
3064 reply("NSMSG_HANDLE_NONE");
3066 reply("NSMSG_GLOBAL_STATS",
3067 dict_size(nickserv_handle_dict),
3068 dict_size(nickserv_nick_dict));
3073 static NICKSERV_FUNC(cmd_ghost)
3075 struct userNode *target;
3076 char reason[MAXLEN];
3078 NICKSERV_MIN_PARMS(2);
3079 if (!(target = GetUserH(argv[1]))) {
3080 reply("MSG_NICK_UNKNOWN", argv[1]);
3083 if (target == user) {
3084 reply("NSMSG_CANNOT_GHOST_SELF");
3087 if (!target->handle_info || (target->handle_info != user->handle_info)) {
3088 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
3091 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
3092 DelUser(target, nickserv, 1, reason);
3093 reply("NSMSG_GHOST_KILLED", argv[1]);
3097 static NICKSERV_FUNC(cmd_vacation)
3099 HANDLE_SET_FLAG(user->handle_info, FROZEN);
3100 reply("NSMSG_ON_VACATION");
3104 static NICKSERV_FUNC(cmd_addnote)
3106 struct handle_info *hi;
3107 unsigned long duration;
3110 struct handle_note *prev;
3111 struct handle_note *note;
3113 /* Parse parameters and figure out values for note's fields. */
3114 NICKSERV_MIN_PARMS(4);
3115 hi = get_victim_oper(user, argv[1]);
3118 if(!strcmp(argv[2], "0"))
3120 else if(!(duration = ParseInterval(argv[2])))
3122 reply("MSG_INVALID_DURATION", argv[2]);
3125 if (duration > 2*365*86400) {
3126 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
3129 unsplit_string(argv + 3, argc - 3, text);
3130 WALK_NOTES(hi, prev, note) {}
3131 id = prev ? (prev->id + 1) : 1;
3133 /* Create the new note structure. */
3134 note = calloc(1, sizeof(*note) + strlen(text));
3136 note->expires = duration ? (now + duration) : 0;
3139 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
3140 strcpy(note->note, text);
3145 reply("NSMSG_NOTE_ADDED", id, hi->handle);
3149 static NICKSERV_FUNC(cmd_delnote)
3151 struct handle_info *hi;
3152 struct handle_note *prev;
3153 struct handle_note *note;
3156 NICKSERV_MIN_PARMS(3);
3157 hi = get_victim_oper(user, argv[1]);
3160 id = strtoul(argv[2], NULL, 10);
3161 WALK_NOTES(hi, prev, note) {
3162 if (id == note->id) {
3164 prev->next = note->next;
3166 hi->notes = note->next;
3168 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3172 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3177 nickserv_saxdb_write(struct saxdb_context *ctx) {
3179 struct handle_info *hi;
3182 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3184 assert(hi->id != 0);
3185 saxdb_start_record(ctx, iter_key(it), 0);
3187 struct handle_cookie *cookie = hi->cookie;
3190 switch (cookie->type) {
3191 case ACTIVATION: type = KEY_ACTIVATION; break;
3192 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3193 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3194 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3195 default: type = NULL; break;
3198 saxdb_start_record(ctx, KEY_COOKIE, 0);
3199 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3200 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3202 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3203 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3204 saxdb_end_record(ctx);
3208 struct handle_note *prev, *note;
3209 saxdb_start_record(ctx, KEY_NOTES, 0);
3210 WALK_NOTES(hi, prev, note) {
3211 snprintf(flags, sizeof(flags), "%d", note->id);
3212 saxdb_start_record(ctx, flags, 0);
3214 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3215 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3216 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3217 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3218 saxdb_end_record(ctx);
3220 saxdb_end_record(ctx);
3223 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3225 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3227 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3229 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3233 for (ii=flen=0; handle_flags[ii]; ++ii)
3234 if (hi->flags & (1 << ii))
3235 flags[flen++] = handle_flags[ii];
3237 saxdb_write_string(ctx, KEY_FLAGS, flags);
3239 saxdb_write_int(ctx, KEY_ID, hi->id);
3241 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3243 saxdb_write_string(ctx, KEY_DEVNULL, hi->devnull);
3245 saxdb_write_string(ctx, KEY_WEBSITE, hi->website);
3246 if (hi->last_quit_host[0])
3247 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3248 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3250 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3251 if (hi->masks->used)
3252 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3254 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3256 struct string_list *slist;
3257 struct nick_info *ni;
3259 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3260 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3261 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3265 if (hi->opserv_level)
3266 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3267 if (hi->language != lang_C)
3268 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3269 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3270 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3271 if (hi->screen_width)
3272 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3273 if (hi->table_width)
3274 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3275 flags[0] = hi->userlist_style;
3277 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3278 saxdb_end_record(ctx);
3283 static handle_merge_func_t *handle_merge_func_list;
3284 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3287 reg_handle_merge_func(handle_merge_func_t func)
3289 if (handle_merge_func_used == handle_merge_func_size) {
3290 if (handle_merge_func_size) {
3291 handle_merge_func_size <<= 1;
3292 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3294 handle_merge_func_size = 8;
3295 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3298 handle_merge_func_list[handle_merge_func_used++] = func;
3301 static NICKSERV_FUNC(cmd_merge)
3303 struct handle_info *hi_from, *hi_to;
3304 struct userNode *last_user;
3305 struct userData *cList, *cListNext;
3306 unsigned int ii, jj, n;
3307 char buffer[MAXLEN];
3309 NICKSERV_MIN_PARMS(3);
3311 if (!(hi_from = get_victim_oper(user, argv[1])))
3313 if (!(hi_to = get_victim_oper(user, argv[2])))
3315 if (hi_to == hi_from) {
3316 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3320 for (n=0; n<handle_merge_func_used; n++)
3321 handle_merge_func_list[n](user, hi_to, hi_from);
3323 /* Append "from" handle's nicks to "to" handle's nick list. */
3325 struct nick_info *last_ni;
3326 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3327 last_ni->next = hi_from->nicks;
3329 while (hi_from->nicks) {
3330 hi_from->nicks->owner = hi_to;
3331 hi_from->nicks = hi_from->nicks->next;
3334 /* Merge the hostmasks. */
3335 for (ii=0; ii<hi_from->masks->used; ii++) {
3336 char *mask = hi_from->masks->list[ii];
3337 for (jj=0; jj<hi_to->masks->used; jj++)
3338 if (match_ircglobs(hi_to->masks->list[jj], mask))
3340 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3341 string_list_append(hi_to->masks, strdup(mask));
3344 /* Merge the lists of authed users. */
3346 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3347 last_user->next_authed = hi_from->users;
3349 hi_to->users = hi_from->users;
3351 /* Repoint the old "from" handle's users. */
3352 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3353 last_user->handle_info = hi_to;
3355 hi_from->users = NULL;
3357 /* Merge channel userlists. */
3358 for (cList=hi_from->channels; cList; cList=cListNext) {
3359 struct userData *cList2;
3360 cListNext = cList->u_next;
3361 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3362 if (cList->channel == cList2->channel)
3364 if (cList2 && (cList2->access >= cList->access)) {
3365 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);
3366 /* keep cList2 in hi_to; remove cList from hi_from */
3367 del_channel_user(cList, 1);
3370 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);
3371 /* remove the lower-ranking cList2 from hi_to */
3372 del_channel_user(cList2, 1);
3374 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3376 /* cList needs to be moved from hi_from to hi_to */
3377 cList->handle = hi_to;
3378 /* Remove from linked list for hi_from */
3379 assert(!cList->u_prev);
3380 hi_from->channels = cList->u_next;
3382 cList->u_next->u_prev = cList->u_prev;
3383 /* Add to linked list for hi_to */
3384 cList->u_prev = NULL;
3385 cList->u_next = hi_to->channels;
3386 if (hi_to->channels)
3387 hi_to->channels->u_prev = cList;
3388 hi_to->channels = cList;
3392 /* Do they get an OpServ level promotion? */
3393 if (hi_from->opserv_level > hi_to->opserv_level)
3394 hi_to->opserv_level = hi_from->opserv_level;
3396 /* What about last seen time? */
3397 if (hi_from->lastseen > hi_to->lastseen)
3398 hi_to->lastseen = hi_from->lastseen;
3400 /* New karma is the sum of the two original karmas. */
3401 hi_to->karma += hi_from->karma;
3403 /* Does a fakehost carry over? (This intentionally doesn't set it
3404 * for users previously attached to hi_to. They'll just have to
3407 if (hi_from->fakehost && !hi_to->fakehost)
3408 hi_to->fakehost = strdup(hi_from->fakehost);
3409 if (hi_from->fakeident && !hi_to->fakeident)
3410 hi_to->fakeident = strdup(hi_from->fakeident);
3412 /* Notify of success. */
3413 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3414 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3415 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3417 /* Unregister the "from" handle. */
3418 nickserv_unregister_handle(hi_from, NULL);
3423 struct nickserv_discrim {
3424 unsigned long flags_on, flags_off;
3425 unsigned long min_registered, max_registered;
3426 unsigned long lastseen;
3428 int min_level, max_level;
3429 int min_karma, max_karma;
3430 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3431 const char *nickmask;
3432 const char *hostmask;
3433 const char *fakehostmask;
3434 const char *fakeidentmask;
3435 const char *website;
3436 const char *devnullclass;
3437 const char *handlemask;
3438 const char *emailmask;
3441 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3443 struct discrim_apply_info {
3444 struct nickserv_discrim *discrim;
3445 discrim_search_func func;
3446 struct userNode *source;
3447 unsigned int matched;
3450 static struct nickserv_discrim *
3451 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3454 struct nickserv_discrim *discrim;
3456 discrim = malloc(sizeof(*discrim));
3457 memset(discrim, 0, sizeof(*discrim));
3458 discrim->min_level = 0;
3459 discrim->max_level = INT_MAX;
3460 discrim->limit = 50;
3461 discrim->min_registered = 0;
3462 discrim->max_registered = ULONG_MAX;
3463 discrim->lastseen = ULONG_MAX;
3464 discrim->min_karma = INT_MIN;
3465 discrim->max_karma = INT_MAX;
3467 for (i=0; i<argc; i++) {
3468 if (i == argc - 1) {
3469 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3472 if (!irccasecmp(argv[i], "limit")) {
3473 discrim->limit = strtoul(argv[++i], NULL, 0);
3474 } else if (!irccasecmp(argv[i], "flags")) {
3475 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3476 } else if (!irccasecmp(argv[i], "registered")) {
3477 const char *cmp = argv[++i];
3478 if (cmp[0] == '<') {
3479 if (cmp[1] == '=') {
3480 discrim->min_registered = now - ParseInterval(cmp+2);
3482 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3484 } else if (cmp[0] == '=') {
3485 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3486 } else if (cmp[0] == '>') {
3487 if (cmp[1] == '=') {
3488 discrim->max_registered = now - ParseInterval(cmp+2);
3490 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3493 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3495 } else if (!irccasecmp(argv[i], "seen")) {
3496 discrim->lastseen = now - ParseInterval(argv[++i]);
3497 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3498 discrim->nickmask = argv[++i];
3499 } else if (!irccasecmp(argv[i], "hostmask")) {
3501 if (!irccasecmp(argv[i], "exact")) {
3502 if (i == argc - 1) {
3503 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3506 discrim->hostmask_type = EXACT;
3507 } else if (!irccasecmp(argv[i], "subset")) {
3508 if (i == argc - 1) {
3509 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3512 discrim->hostmask_type = SUBSET;
3513 } else if (!irccasecmp(argv[i], "superset")) {
3514 if (i == argc - 1) {
3515 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3518 discrim->hostmask_type = SUPERSET;
3519 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3520 if (i == argc - 1) {
3521 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3524 discrim->hostmask_type = LASTQUIT;
3527 discrim->hostmask_type = SUPERSET;
3529 discrim->hostmask = argv[++i];
3530 } else if (!irccasecmp(argv[i], "fakehost")) {
3531 if (!irccasecmp(argv[++i], "*")) {
3532 discrim->fakehostmask = 0;
3534 discrim->fakehostmask = argv[i];
3536 } else if (!irccasecmp(argv[i], "fakeident")) {
3537 if (!irccasecmp(argv[++i], "*")) {
3538 discrim->fakeidentmask = 0;
3540 discrim->fakeidentmask = argv[i];
3542 } else if (!irccasecmp(argv[i], "website")) {
3543 if (!irccasecmp(argv[++i], "*")) {
3544 discrim->website = 0;
3546 discrim->website = argv[i];
3548 } else if (!irccasecmp(argv[i], "devnull")) {
3549 if (!irccasecmp(argv[++i], "*")) {
3550 discrim->devnullclass = 0;
3552 discrim->devnullclass = argv[i];
3554 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3555 if (!irccasecmp(argv[++i], "*")) {
3556 discrim->handlemask = 0;
3558 discrim->handlemask = argv[i];
3560 } else if (!irccasecmp(argv[i], "email")) {
3561 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3562 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3564 } else if (!irccasecmp(argv[++i], "*")) {
3565 discrim->emailmask = 0;
3567 discrim->emailmask = argv[i];
3569 } else if (!irccasecmp(argv[i], "access")) {
3570 const char *cmp = argv[++i];
3571 if (cmp[0] == '<') {
3572 if (discrim->min_level == 0) discrim->min_level = 1;
3573 if (cmp[1] == '=') {
3574 discrim->max_level = strtoul(cmp+2, NULL, 0);
3576 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3578 } else if (cmp[0] == '=') {
3579 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3580 } else if (cmp[0] == '>') {
3581 if (cmp[1] == '=') {
3582 discrim->min_level = strtoul(cmp+2, NULL, 0);
3584 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3587 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3589 } else if (!irccasecmp(argv[i], "karma")) {
3590 const char *cmp = argv[++i];
3591 if (cmp[0] == '<') {
3592 if (cmp[1] == '=') {
3593 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3595 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3597 } else if (cmp[0] == '=') {
3598 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3599 } else if (cmp[0] == '>') {
3600 if (cmp[1] == '=') {
3601 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3603 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3606 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3609 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3620 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3622 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3623 || (discrim->flags_off & hi->flags)
3624 || (discrim->min_registered > hi->registered)
3625 || (discrim->max_registered < hi->registered)
3626 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3627 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3628 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3629 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3630 || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website)))
3631 || (discrim->devnullclass && (!hi->devnull || !match_ircglob(hi->devnull, discrim->devnullclass)))
3632 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3633 || (discrim->min_level > hi->opserv_level)
3634 || (discrim->max_level < hi->opserv_level)
3635 || (discrim->min_karma > hi->karma)
3636 || (discrim->max_karma < hi->karma)
3640 if (discrim->hostmask) {
3642 for (i=0; i<hi->masks->used; i++) {
3643 const char *mask = hi->masks->list[i];
3644 if ((discrim->hostmask_type == SUBSET)
3645 && (match_ircglobs(discrim->hostmask, mask))) break;
3646 else if ((discrim->hostmask_type == EXACT)
3647 && !irccasecmp(discrim->hostmask, mask)) break;
3648 else if ((discrim->hostmask_type == SUPERSET)
3649 && (match_ircglobs(mask, discrim->hostmask))) break;
3650 else if ((discrim->hostmask_type == LASTQUIT)
3651 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3653 if (i==hi->masks->used) return 0;
3655 if (discrim->nickmask) {
3656 struct nick_info *nick = hi->nicks;
3658 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3661 if (!nick) return 0;
3667 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3669 dict_iterator_t it, next;
3670 unsigned int matched;
3672 for (it = dict_first(nickserv_handle_dict), matched = 0;
3673 it && (matched < discrim->limit);
3675 next = iter_next(it);
3676 if (nickserv_discrim_match(discrim, iter_data(it))) {
3677 dsf(source, iter_data(it));
3685 search_print_func(struct userNode *source, struct handle_info *match)
3687 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3691 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3696 search_unregister_func (struct userNode *source, struct handle_info *match)
3698 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3699 nickserv_unregister_handle(match, source);
3703 nickserv_sort_accounts_by_access(const void *a, const void *b)
3705 const struct handle_info *hi_a = *(const struct handle_info**)a;
3706 const struct handle_info *hi_b = *(const struct handle_info**)b;
3707 if (hi_a->opserv_level != hi_b->opserv_level)
3708 return hi_b->opserv_level - hi_a->opserv_level;
3709 return irccasecmp(hi_a->handle, hi_b->handle);
3713 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3715 struct handle_info_list hil;
3716 struct helpfile_table tbl;
3721 memset(&hil, 0, sizeof(hil));
3722 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3723 struct handle_info *hi = iter_data(it);
3724 if (hi->opserv_level)
3725 handle_info_list_append(&hil, hi);
3727 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3728 tbl.length = hil.used + 1;
3730 tbl.flags = TABLE_NO_FREE;
3731 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3732 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3735 for (ii = 0; ii < hil.used; ) {
3736 ary = malloc(tbl.width * sizeof(ary[0]));
3737 ary[0] = hil.list[ii]->handle;
3738 ary[1] = strtab(hil.list[ii]->opserv_level);
3739 tbl.contents[++ii] = ary;
3741 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3742 reply("MSG_MATCH_COUNT", hil.used);
3743 for (ii = 0; ii < hil.used; ii++)
3744 free(tbl.contents[ii]);
3749 static NICKSERV_FUNC(cmd_search)
3751 struct nickserv_discrim *discrim;
3752 discrim_search_func action;
3753 struct svccmd *subcmd;
3754 unsigned int matches;
3757 NICKSERV_MIN_PARMS(3);
3758 sprintf(buf, "search %s", argv[1]);
3759 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3760 if (!irccasecmp(argv[1], "print"))
3761 action = search_print_func;
3762 else if (!irccasecmp(argv[1], "count"))
3763 action = search_count_func;
3764 else if (!irccasecmp(argv[1], "unregister"))
3765 action = search_unregister_func;
3767 reply("NSMSG_INVALID_ACTION", argv[1]);
3771 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3774 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3778 if (action == search_print_func)
3779 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3780 else if (action == search_count_func)
3781 discrim->limit = INT_MAX;
3783 matches = nickserv_discrim_search(discrim, action, user);
3786 reply("MSG_MATCH_COUNT", matches);
3788 reply("MSG_NO_MATCHES");
3794 static MODCMD_FUNC(cmd_checkpass)
3796 struct handle_info *hi;
3798 NICKSERV_MIN_PARMS(3);
3799 if (!(hi = get_handle_info(argv[1]))) {
3800 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3803 if (checkpass(argv[2], hi->passwd))
3804 reply("CHECKPASS_YES");
3806 reply("CHECKPASS_NO");
3811 static MODCMD_FUNC(cmd_checkemail)
3813 struct handle_info *hi;
3815 NICKSERV_MIN_PARMS(3);
3816 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3819 if (!hi->email_addr)
3820 reply("CHECKEMAIL_NOT_SET");
3821 else if (!irccasecmp(argv[2], hi->email_addr))
3822 reply("CHECKEMAIL_YES");
3824 reply("CHECKEMAIL_NO");
3830 nickserv_db_read_handle(const char *handle, dict_t obj)
3833 struct string_list *masks, *slist;
3834 struct handle_info *hi;
3835 struct userNode *authed_users;
3836 struct userData *channel_list;
3841 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3842 id = str ? strtoul(str, NULL, 0) : 0;
3843 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3845 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3848 if ((hi = get_handle_info(handle))) {
3849 authed_users = hi->users;
3850 channel_list = hi->channels;
3852 hi->channels = NULL;
3853 dict_remove(nickserv_handle_dict, hi->handle);
3855 authed_users = NULL;
3856 channel_list = NULL;
3858 hi = register_handle(handle, str, id);
3860 hi->users = authed_users;
3861 while (authed_users) {
3862 authed_users->handle_info = hi;
3863 authed_users = authed_users->next_authed;
3866 hi->channels = channel_list;
3867 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3868 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3869 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3870 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3871 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3872 hi->language = language_find(str ? str : "C");
3873 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3874 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3875 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3877 hi->infoline = strdup(str);
3878 str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING);
3880 hi->website = strdup(str);
3881 str = database_get_data(obj, KEY_DEVNULL, RECDB_QSTRING);
3883 hi->devnull = strdup(str);
3884 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3885 hi->registered = str ? strtoul(str, NULL, 0) : now;
3886 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3887 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3888 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3889 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3890 /* We want to read the nicks even if disable_nicks is set. This is so
3891 * that we don't lose the nick data entirely. */
3892 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3894 for (ii=0; ii<slist->used; ii++)
3895 register_nick(slist->list[ii], hi);
3897 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3899 for (ii=0; str[ii]; ii++)
3900 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3902 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3903 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3904 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3905 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3906 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3907 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3908 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3910 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3912 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3913 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3915 nickserv_set_email_addr(hi, str);
3916 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3918 hi->epithet = strdup(str);
3919 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3921 hi->fakehost = strdup(str);
3922 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
3924 hi->fakeident = strdup(str);
3925 /* Read the "cookie" sub-database (if it exists). */
3926 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3928 const char *data, *type, *expires, *cookie_str;
3929 struct handle_cookie *cookie;
3931 cookie = calloc(1, sizeof(*cookie));
3932 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3933 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3934 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3935 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3936 if (!type || !expires || !cookie_str) {
3937 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3940 if (!irccasecmp(type, KEY_ACTIVATION))
3941 cookie->type = ACTIVATION;
3942 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3943 cookie->type = PASSWORD_CHANGE;
3944 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3945 cookie->type = EMAIL_CHANGE;
3946 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3947 cookie->type = ALLOWAUTH;
3949 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3952 cookie->expires = strtoul(expires, NULL, 0);
3953 if (cookie->expires < now)
3956 cookie->data = strdup(data);
3957 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3961 nickserv_bake_cookie(cookie);
3963 nickserv_free_cookie(cookie);
3965 /* Read the "notes" sub-database (if it exists). */
3966 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3969 struct handle_note *last_note;
3970 struct handle_note *note;
3973 for (it = dict_first(subdb); it; it = iter_next(it)) {
3974 const char *expires;
3978 const char *note_id;
3981 note_id = iter_key(it);
3982 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3984 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
3987 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3988 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3989 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3990 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3991 if (!setter || !text || !set) {
3992 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
3995 note = calloc(1, sizeof(*note) + strlen(text));
3997 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3998 note->set = strtoul(set, NULL, 10);
3999 note->id = strtoul(note_id, NULL, 10);
4000 safestrncpy(note->setter, setter, sizeof(note->setter));
4001 strcpy(note->note, text);
4003 last_note->next = note;
4012 nickserv_saxdb_read(dict_t db) {
4014 struct record_data *rd;
4016 for (it=dict_first(db); it; it=iter_next(it)) {
4018 nickserv_db_read_handle(iter_key(it), rd->d.object);
4023 static NICKSERV_FUNC(cmd_mergedb)
4025 struct timeval start, stop;
4028 NICKSERV_MIN_PARMS(2);
4029 gettimeofday(&start, NULL);
4030 if (!(db = parse_database(argv[1]))) {
4031 reply("NSMSG_DB_UNREADABLE", argv[1]);
4034 nickserv_saxdb_read(db);
4036 gettimeofday(&stop, NULL);
4037 stop.tv_sec -= start.tv_sec;
4038 stop.tv_usec -= start.tv_usec;
4039 if (stop.tv_usec < 0) {
4041 stop.tv_usec += 1000000;
4043 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
4048 expire_handles(UNUSED_ARG(void *data))
4050 dict_iterator_t it, next;
4051 unsigned long expiry;
4052 struct handle_info *hi;
4054 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4055 next = iter_next(it);
4057 if ((hi->opserv_level > 0)
4059 || HANDLE_FLAGGED(hi, FROZEN)
4060 || HANDLE_FLAGGED(hi, NODELETE)) {
4063 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4064 if ((now - hi->lastseen) > expiry) {
4065 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
4066 nickserv_unregister_handle(hi, NULL);
4070 if (nickserv_conf.handle_expire_frequency)
4071 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4075 nickserv_load_dict(const char *fname)
4079 if (!(file = fopen(fname, "r"))) {
4080 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4083 while (fgets(line, sizeof(line), file)) {
4086 if (line[strlen(line)-1] == '\n')
4087 line[strlen(line)-1] = 0;
4088 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4091 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4094 static enum reclaim_action
4095 reclaim_action_from_string(const char *str) {
4097 return RECLAIM_NONE;
4098 else if (!irccasecmp(str, "warn"))
4099 return RECLAIM_WARN;
4100 else if (!irccasecmp(str, "svsnick"))
4101 return RECLAIM_SVSNICK;
4102 else if (!irccasecmp(str, "kill"))
4103 return RECLAIM_KILL;
4105 return RECLAIM_NONE;
4109 nickserv_conf_read(void)
4111 dict_t conf_node, child;
4115 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4116 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4119 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4121 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4122 if (nickserv_conf.valid_handle_regex_set)
4123 regfree(&nickserv_conf.valid_handle_regex);
4125 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4126 nickserv_conf.valid_handle_regex_set = !err;
4127 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4129 nickserv_conf.valid_handle_regex_set = 0;
4131 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4132 if (nickserv_conf.valid_nick_regex_set)
4133 regfree(&nickserv_conf.valid_nick_regex);
4135 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4136 nickserv_conf.valid_nick_regex_set = !err;
4137 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4139 nickserv_conf.valid_nick_regex_set = 0;
4141 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4143 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4144 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4145 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4146 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4147 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4148 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4149 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4150 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4151 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4152 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4153 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4154 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4155 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4156 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4157 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4158 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4159 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4160 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4161 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4162 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4163 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4164 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4165 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4166 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4167 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
4168 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
4169 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4171 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4172 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4173 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4175 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4176 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4177 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4179 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4180 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4181 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4182 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4183 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4184 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4185 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4186 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4187 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4188 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4189 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4192 nickserv_conf.ounregister_flags = 0;
4194 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4197 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4199 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4201 nickserv_conf.handle_ts_mode = TS_IGNORE;
4202 else if (!irccasecmp(str, "ircu"))
4203 nickserv_conf.handle_ts_mode = TS_IRCU;
4205 nickserv_conf.handle_ts_mode = TS_IGNORE;
4206 if (!nickserv_conf.disable_nicks) {
4207 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4208 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4209 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4210 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4211 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4212 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4213 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4214 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4216 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4217 for (it=dict_first(child); it; it=iter_next(it)) {
4218 const char *key = iter_key(it), *value;
4222 if (!strncasecmp(key, "uc_", 3))
4223 flag = toupper(key[3]);
4224 else if (!strncasecmp(key, "lc_", 3))
4225 flag = tolower(key[3]);
4229 if ((pos = handle_inverse_flags[flag])) {
4230 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4231 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4234 if (nickserv_conf.weak_password_dict)
4235 dict_delete(nickserv_conf.weak_password_dict);
4236 nickserv_conf.weak_password_dict = dict_new();
4237 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4238 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4239 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4240 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4242 nickserv_load_dict(str);
4243 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4244 if (nickserv && str)
4245 NickChange(nickserv, str, 0);
4246 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4247 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4248 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4249 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4250 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4251 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4252 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4253 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4254 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4255 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4256 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4257 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4258 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4259 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4260 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4261 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4262 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4263 titlehost_suffix = str ? str : "example.net";
4264 str = conf_get_data("server/network", RECDB_QSTRING);
4265 nickserv_conf.network_name = str ? str : "some IRC network";
4266 if (!nickserv_conf.auth_policer_params) {
4267 nickserv_conf.auth_policer_params = policer_params_new();
4268 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4269 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4271 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4272 for (it=dict_first(child); it; it=iter_next(it))
4273 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4277 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4279 char newnick[NICKLEN+1];
4288 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4290 case RECLAIM_SVSNICK:
4292 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4293 } while (GetUserH(newnick));
4294 irc_svsnick(nickserv, user, newnick);
4297 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4298 DelUser(user, nickserv, 1, msg);
4304 nickserv_reclaim_p(void *data) {
4305 struct userNode *user = data;
4306 struct nick_info *ni = get_nick_info(user->nick);
4308 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4312 check_user_nick(struct userNode *user) {
4313 struct nick_info *ni;
4314 user->modes &= ~FLAGS_REGNICK;
4315 if (!(ni = get_nick_info(user->nick)))
4317 if (user->handle_info == ni->owner) {
4318 user->modes |= FLAGS_REGNICK;
4322 if (nickserv_conf.warn_nick_owned)
4323 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4324 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4326 if (nickserv_conf.auto_reclaim_delay)
4327 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4329 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4333 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4335 struct handle_info *hi = NULL;
4338 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4339 if ((hi == NULL) && (serial != 0)) {
4341 inttobase64(id, serial, IDLEN);
4342 hi = dict_find(nickserv_id_dict, id, NULL);
4346 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4347 && (timestamp != hi->registered)) {
4350 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4353 set_user_handle_info(user, hi, 0);
4355 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4360 handle_nick_change(struct userNode *user, const char *old_nick)
4362 struct handle_info *hi;
4364 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4365 dict_remove(nickserv_allow_auth_dict, old_nick);
4366 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4368 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4369 check_user_nick(user);
4373 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4375 dict_remove(nickserv_allow_auth_dict, user->nick);
4376 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4377 set_user_handle_info(user, NULL, 0);
4380 static struct modcmd *
4381 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4383 if (min_level > 0) {
4385 sprintf(buf, "%u", min_level);
4386 if (must_be_qualified) {
4387 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4389 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4391 } else if (min_level == 0) {
4392 if (must_be_qualified) {
4393 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4395 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4398 if (must_be_qualified) {
4399 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4401 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4407 nickserv_db_cleanup(void)
4409 unreg_del_user_func(nickserv_remove_user);
4410 userList_clean(&curr_helpers);
4411 policer_params_delete(nickserv_conf.auth_policer_params);
4412 dict_delete(nickserv_handle_dict);
4413 dict_delete(nickserv_nick_dict);
4414 dict_delete(nickserv_opt_dict);
4415 dict_delete(nickserv_allow_auth_dict);
4416 dict_delete(nickserv_email_dict);
4417 dict_delete(nickserv_id_dict);
4418 dict_delete(nickserv_conf.weak_password_dict);
4419 free(auth_func_list);
4420 free(unreg_func_list);
4422 free(allowauth_func_list);
4423 free(handle_merge_func_list);
4424 free(failpw_func_list);
4425 if (nickserv_conf.valid_handle_regex_set)
4426 regfree(&nickserv_conf.valid_handle_regex);
4427 if (nickserv_conf.valid_nick_regex_set)
4428 regfree(&nickserv_conf.valid_nick_regex);
4432 init_nickserv(const char *nick)
4435 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4436 reg_new_user_func(check_user_nick);
4437 reg_nick_change_func(handle_nick_change);
4438 reg_del_user_func(nickserv_remove_user);
4439 reg_account_func(handle_account);
4441 /* set up handle_inverse_flags */
4442 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4443 for (i=0; handle_flags[i]; i++) {
4444 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4445 flag_access_levels[i] = 0;
4448 conf_register_reload(nickserv_conf_read);
4449 nickserv_opt_dict = dict_new();
4450 nickserv_email_dict = dict_new();
4451 dict_set_free_keys(nickserv_email_dict, free);
4452 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4454 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4455 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4456 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4457 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4458 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4459 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4460 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4461 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4462 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4463 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4464 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4465 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4466 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4467 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4468 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4469 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4470 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4471 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4472 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4473 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4474 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4475 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4476 if (!nickserv_conf.disable_nicks) {
4477 /* nick management commands */
4478 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4479 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4480 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4481 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4482 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4483 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4485 if (nickserv_conf.email_enabled) {
4486 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4487 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4488 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4489 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4490 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4491 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4493 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4494 /* miscellaneous commands */
4495 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4496 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4497 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4498 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4499 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4500 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4502 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4503 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4504 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4505 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4506 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4507 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4508 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
4509 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4510 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4511 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4512 dict_insert(nickserv_opt_dict, "WEBSITE", opt_website);
4513 dict_insert(nickserv_opt_dict, "DEVNULL", opt_devnull);
4514 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4515 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4516 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4517 if (titlehost_suffix) {
4518 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4519 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4520 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4522 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4523 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4524 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4525 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4527 nickserv_handle_dict = dict_new();
4528 dict_set_free_keys(nickserv_handle_dict, free);
4529 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4531 nickserv_id_dict = dict_new();
4532 dict_set_free_keys(nickserv_id_dict, free);
4534 nickserv_nick_dict = dict_new();
4535 dict_set_free_data(nickserv_nick_dict, free);
4537 nickserv_allow_auth_dict = dict_new();
4539 userList_init(&curr_helpers);
4542 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4543 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4544 nickserv_service = service_register(nickserv);
4546 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4547 reg_exit_func(nickserv_db_cleanup);
4548 if(nickserv_conf.handle_expire_frequency)
4549 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4550 message_register_table(msgtab);