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_TITLEHOST_SUFFIX "titlehost_suffix"
55 #define KEY_FLAG_LEVELS "flag_levels"
56 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
57 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
58 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
59 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
60 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
61 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
62 #define KEY_DICT_FILE "dict_file"
63 #define KEY_NICK "nick"
64 #define KEY_LANGUAGE "language"
65 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
66 #define KEY_AUTOGAG_DURATION "autogag_duration"
67 #define KEY_AUTH_POLICER "auth_policer"
68 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
69 #define KEY_EMAIL_ENABLED "email_enabled"
70 #define KEY_EMAIL_REQUIRED "email_required"
71 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
72 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
73 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
74 #define KEY_OUNREGISTER_INACTIVE "ounregister_inactive"
75 #define KEY_OUNREGISTER_FLAGS "ounregister_flags"
76 #define KEY_HANDLE_TS_MODE "account_timestamp_mode"
79 #define KEY_PASSWD "passwd"
80 #define KEY_NICKS "nicks"
81 #define KEY_MASKS "masks"
82 #define KEY_OPSERV_LEVEL "opserv_level"
83 #define KEY_FLAGS "flags"
84 #define KEY_REGISTER_ON "register"
85 #define KEY_LAST_SEEN "lastseen"
86 #define KEY_INFO "info"
87 #define KEY_USERLIST_STYLE "user_style"
88 #define KEY_SCREEN_WIDTH "screen_width"
89 #define KEY_LAST_AUTHED_HOST "last_authed_host"
90 #define KEY_LAST_QUIT_HOST "last_quit_host"
91 #define KEY_EMAIL_ADDR "email_addr"
92 #define KEY_COOKIE "cookie"
93 #define KEY_COOKIE_DATA "data"
94 #define KEY_COOKIE_TYPE "type"
95 #define KEY_COOKIE_EXPIRES "expires"
96 #define KEY_ACTIVATION "activation"
97 #define KEY_PASSWORD_CHANGE "password change"
98 #define KEY_EMAIL_CHANGE "email change"
99 #define KEY_ALLOWAUTH "allowauth"
100 #define KEY_EPITHET "epithet"
101 #define KEY_TABLE_WIDTH "table_width"
102 #define KEY_MAXLOGINS "maxlogins"
103 #define KEY_FAKEHOST "fakehost"
104 #define KEY_NOTES "notes"
105 #define KEY_NOTE_EXPIRES "expires"
106 #define KEY_NOTE_SET "set"
107 #define KEY_NOTE_SETTER "setter"
108 #define KEY_NOTE_NOTE "note"
109 #define KEY_KARMA "karma"
111 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
113 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
114 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
115 typedef OPTION_FUNC(option_func_t);
117 DEFINE_LIST(handle_info_list, struct handle_info*)
119 #define NICKSERV_MIN_PARMS(N) do { \
121 reply("MSG_MISSING_PARAMS", argv[0]); \
122 svccmd_send_help(user, nickserv, cmd); \
126 struct userNode *nickserv;
127 struct userList curr_helpers;
128 const char *handle_flags = HANDLE_FLAGS;
130 static struct module *nickserv_module;
131 static struct service *nickserv_service;
132 static struct log_type *NS_LOG;
133 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
134 static dict_t nickserv_id_dict; /* contains struct handle_info* */
135 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
136 static dict_t nickserv_opt_dict; /* contains option_func_t* */
137 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
138 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
139 static char handle_inverse_flags[256];
140 static unsigned int flag_access_levels[32];
141 static const struct message_entry msgtab[] = {
142 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
143 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
144 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
145 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
146 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
147 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
148 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
149 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
150 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
151 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
152 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
153 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
154 { "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." },
155 { "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." },
156 { "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." },
157 { "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." },
158 { "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." },
159 { "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." },
160 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
161 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
162 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
163 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
164 { "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." },
165 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
166 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
167 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
168 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
169 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
170 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
171 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
172 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
173 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
174 { "NSMSG_ATE_COOKIE_FOREIGN", "I ate the cookie for account $b%s$b. It may now have another." },
175 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
176 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
177 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
178 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
179 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
180 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
181 { "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)" },
182 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
183 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
184 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
185 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
186 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
187 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
188 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
189 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
190 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
191 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
192 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
193 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
194 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
195 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
196 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
197 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
198 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
199 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
200 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
201 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
202 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
203 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
204 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
205 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
206 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
207 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
208 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
209 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
210 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
211 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
212 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
213 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
214 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
215 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
216 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
217 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
218 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
219 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
220 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
221 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
222 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
223 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
224 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
225 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
226 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
227 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
228 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
229 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
230 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
231 { "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)." },
232 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
233 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
234 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
235 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
236 { "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." },
237 { "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." },
238 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
239 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
240 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
241 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
242 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
243 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
244 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
245 { "NSMSG_PASS_SUCCESS", "Password changed." },
246 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
247 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
248 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
249 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
250 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
251 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
252 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
253 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
254 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
255 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
256 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
257 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
258 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
259 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
260 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
261 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
262 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
263 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
264 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
265 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
266 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
267 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
268 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
269 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
270 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
271 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
272 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
273 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
274 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
275 { "NSMSG_NO_ACCESS", "Access denied." },
276 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
277 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
278 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
279 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
280 { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
281 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
282 { "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." },
283 { "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." },
284 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
285 { "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." },
286 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
287 { "NSMSG_SEARCH_MATCH", "Match: %s" },
288 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
289 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
290 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
291 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
292 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
293 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
294 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
295 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
296 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
297 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
298 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
299 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
300 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
301 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
302 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
303 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
304 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
305 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
306 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
307 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
308 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
309 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
310 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
311 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
312 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
313 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
314 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
315 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
316 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
317 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
318 { "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." },
319 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
320 { "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." },
321 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
322 { "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." },
323 { "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." },
324 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
325 { "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." },
326 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
327 { "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." },
328 { "CHECKPASS_YES", "Yes." },
329 { "CHECKPASS_NO", "No." },
330 { "CHECKEMAIL_NOT_SET", "No email set." },
331 { "CHECKEMAIL_YES", "Yes." },
332 { "CHECKEMAIL_NO", "No." },
336 enum reclaim_action {
342 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
343 static void nickserv_reclaim_p(void *data);
344 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
346 enum handle_ts_mode {
352 unsigned int disable_nicks : 1;
353 unsigned int valid_handle_regex_set : 1;
354 unsigned int valid_nick_regex_set : 1;
355 unsigned int autogag_enabled : 1;
356 unsigned int email_enabled : 1;
357 unsigned int email_required : 1;
358 unsigned int default_hostmask : 1;
359 unsigned int warn_nick_owned : 1;
360 unsigned int warn_clone_auth : 1;
361 unsigned long nicks_per_handle;
362 unsigned long password_min_length;
363 unsigned long password_min_digits;
364 unsigned long password_min_upper;
365 unsigned long password_min_lower;
366 unsigned long db_backup_frequency;
367 unsigned long handle_expire_frequency;
368 unsigned long autogag_duration;
369 unsigned long email_visible_level;
370 unsigned long cookie_timeout;
371 unsigned long handle_expire_delay;
372 unsigned long nochan_handle_expire_delay;
373 unsigned long modoper_level;
374 unsigned long set_epithet_level;
375 unsigned long set_title_level;
376 unsigned long set_fakehost_level;
377 unsigned long handles_per_email;
378 unsigned long email_search_level;
379 const char *network_name;
380 const char *titlehost_suffix;
381 regex_t valid_handle_regex;
382 regex_t valid_nick_regex;
383 dict_t weak_password_dict;
384 struct policer_params *auth_policer_params;
385 enum reclaim_action reclaim_action;
386 enum reclaim_action auto_reclaim_action;
387 enum handle_ts_mode handle_ts_mode;
388 unsigned long auto_reclaim_delay;
389 unsigned char default_maxlogins;
390 unsigned char hard_maxlogins;
391 unsigned long ounregister_inactive;
392 unsigned long ounregister_flags;
395 /* We have 2^32 unique account IDs to use. */
396 unsigned long int highest_id = 0;
398 #define WALK_NOTES(HANDLE, PREV, NOTE) \
399 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
400 if (NOTE->expires && NOTE->expires < now) { \
401 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
403 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
407 canonicalize_hostmask(char *mask)
409 char *out = mask, *temp;
410 if ((temp = strchr(mask, '!'))) {
412 while (*temp) *out++ = *temp++;
418 static struct handle_info *
419 register_handle(const char *handle, const char *passwd, unsigned long id)
421 struct handle_info *hi;
423 char id_base64[IDLEN + 1];
426 /* Assign a unique account ID to the account; note that 0 is
427 an invalid account ID. 1 is therefore the first account ID. */
429 id = 1 + highest_id++;
431 /* Note: highest_id is and must always be the highest ID. */
432 if (id > highest_id) {
436 inttobase64(id_base64, id, IDLEN);
438 /* Make sure an account with the same ID doesn't exist. If a
439 duplicate is found, log some details and assign a new one.
440 This should be impossible, but it never hurts to expect it. */
441 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
442 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
447 hi = calloc(1, sizeof(*hi));
448 hi->userlist_style = HI_DEFAULT_STYLE;
449 hi->handle = strdup(handle);
450 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
452 dict_insert(nickserv_handle_dict, hi->handle, hi);
455 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
461 register_nick(const char *nick, struct handle_info *owner)
463 struct nick_info *ni;
464 ni = malloc(sizeof(struct nick_info));
465 safestrncpy(ni->nick, nick, sizeof(ni->nick));
467 ni->next = owner->nicks;
469 dict_insert(nickserv_nick_dict, ni->nick, ni);
473 delete_nick(struct nick_info *ni)
475 struct nick_info *last, *next;
476 struct userNode *user;
477 /* Check to see if we should mark a user as unregistered. */
478 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
479 user->modes &= ~FLAGS_REGNICK;
482 /* Remove ni from the nick_info linked list. */
483 if (ni == ni->owner->nicks) {
484 ni->owner->nicks = ni->next;
486 last = ni->owner->nicks;
492 last->next = next->next;
494 dict_remove(nickserv_nick_dict, ni->nick);
497 static unreg_func_t *unreg_func_list;
498 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
501 reg_unreg_func(unreg_func_t func)
503 if (unreg_func_used == unreg_func_size) {
504 if (unreg_func_size) {
505 unreg_func_size <<= 1;
506 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
509 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
512 unreg_func_list[unreg_func_used++] = func;
516 nickserv_free_cookie(void *data)
518 struct handle_cookie *cookie = data;
519 if (cookie->hi) cookie->hi->cookie = NULL;
520 if (cookie->data) free(cookie->data);
525 free_handle_info(void *vhi)
527 struct handle_info *hi = vhi;
530 inttobase64(id, hi->id, IDLEN);
531 dict_remove(nickserv_id_dict, id);
533 free_string_list(hi->masks);
537 delete_nick(hi->nicks);
542 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
543 nickserv_free_cookie(hi->cookie);
546 struct handle_note *note = hi->notes;
547 hi->notes = note->next;
550 if (hi->email_addr) {
551 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
552 handle_info_list_remove(hil, hi);
554 dict_remove(nickserv_email_dict, hi->email_addr);
559 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
562 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
566 for (n=0; n<unreg_func_used; n++)
567 unreg_func_list[n](notify, hi);
569 set_user_handle_info(hi->users, NULL, 0);
571 if (nickserv_conf.disable_nicks)
572 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
574 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
576 dict_remove(nickserv_handle_dict, hi->handle);
580 get_handle_info(const char *handle)
582 return dict_find(nickserv_handle_dict, handle, 0);
586 get_nick_info(const char *nick)
588 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
592 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
597 for (nn=0; nn<channel->members.used; ++nn) {
598 mn = channel->members.list[nn];
599 if ((mn->user != except) && (mn->user->handle_info == handle))
606 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
607 if (!user->handle_info) {
609 send_message(user, bot, "MSG_AUTHENTICATE");
613 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
615 send_message(user, bot, "NSMSG_NO_ACCESS");
619 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
621 send_message(user, bot, "MSG_OPER_SUSPENDED");
625 if (user->handle_info->opserv_level < min_level) {
627 send_message(user, bot, "NSMSG_NO_ACCESS");
635 is_valid_handle(const char *handle)
637 struct userNode *user;
638 /* cant register a juped nick/service nick as handle, to prevent confusion */
639 user = GetUserH(handle);
640 if (user && IsLocal(user))
642 /* check against maximum length */
643 if (strlen(handle) > NICKSERV_HANDLE_LEN)
645 /* for consistency, only allow account names that could be nicks */
646 if (!is_valid_nick(handle))
648 /* disallow account names that look like bad words */
649 if (opserv_bad_channel(handle))
651 /* test either regex or containing all valid chars */
652 if (nickserv_conf.valid_handle_regex_set) {
653 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
656 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
657 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
661 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
666 is_registerable_nick(const char *nick)
668 /* make sure it could be used as an account name */
669 if (!is_valid_handle(nick))
672 if (strlen(nick) > NICKLEN)
674 /* test either regex or as valid handle */
675 if (nickserv_conf.valid_nick_regex_set) {
676 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
679 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
680 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
688 is_valid_email_addr(const char *email)
690 return strchr(email, '@') != NULL;
694 visible_email_addr(struct userNode *user, struct handle_info *hi)
696 if (hi->email_addr) {
697 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
698 return hi->email_addr;
708 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
710 struct handle_info *hi;
711 struct userNode *target;
715 if (!(hi = get_handle_info(++name))) {
716 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
721 if (!(target = GetUserH(name))) {
722 send_message(user, service, "MSG_NICK_UNKNOWN", name);
725 if (IsLocal(target)) {
726 if (IsService(target))
727 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
729 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
732 if (!(hi = target->handle_info)) {
733 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
741 oper_outranks(struct userNode *user, struct handle_info *hi) {
742 if (user->handle_info->opserv_level > hi->opserv_level)
744 if (user->handle_info->opserv_level == hi->opserv_level) {
745 if ((user->handle_info->opserv_level == 1000)
746 || (user->handle_info == hi)
747 || ((user->handle_info->opserv_level == 0)
748 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
749 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
753 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
757 static struct handle_info *
758 get_victim_oper(struct userNode *user, const char *target)
760 struct handle_info *hi;
761 if (!(hi = smart_get_handle_info(nickserv, user, target)))
763 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
764 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
767 return oper_outranks(user, hi) ? hi : NULL;
771 valid_user_for(struct userNode *user, struct handle_info *hi)
775 /* If no hostmasks on the account, allow it. */
776 if (!hi->masks->used)
778 /* If any hostmask matches, allow it. */
779 for (ii=0; ii<hi->masks->used; ii++)
780 if (user_matches_glob(user, hi->masks->list[ii], 0))
782 /* If they are allowauthed to this account, allow it (removing the aa). */
783 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
784 dict_remove(nickserv_allow_auth_dict, user->nick);
787 /* The user is not allowed to use this account. */
792 is_secure_password(const char *handle, const char *pass, struct userNode *user)
795 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
799 if (len < nickserv_conf.password_min_length) {
801 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
804 if (!irccasecmp(pass, handle)) {
806 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
809 dict_find(nickserv_conf.weak_password_dict, pass, &p);
812 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
815 for (i=0; i<len; i++) {
816 if (isdigit(pass[i]))
818 if (isupper(pass[i]))
820 if (islower(pass[i]))
823 if ((cnt_lower < nickserv_conf.password_min_lower)
824 || (cnt_upper < nickserv_conf.password_min_upper)
825 || (cnt_digits < nickserv_conf.password_min_digits)) {
827 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
833 static auth_func_t *auth_func_list;
834 static unsigned int auth_func_size = 0, auth_func_used = 0;
837 reg_auth_func(auth_func_t func)
839 if (auth_func_used == auth_func_size) {
840 if (auth_func_size) {
841 auth_func_size <<= 1;
842 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
845 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
848 auth_func_list[auth_func_used++] = func;
851 static handle_rename_func_t *rf_list;
852 static unsigned int rf_list_size, rf_list_used;
855 reg_handle_rename_func(handle_rename_func_t func)
857 if (rf_list_used == rf_list_size) {
860 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
863 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
866 rf_list[rf_list_used++] = func;
870 generate_fakehost(struct handle_info *handle)
872 extern const char *hidden_host_suffix;
873 static char buffer[HOSTLEN+1];
875 if (!handle->fakehost) {
876 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
878 } else if (handle->fakehost[0] == '.') {
879 /* A leading dot indicates the stored value is actually a title. */
880 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
883 return handle->fakehost;
887 apply_fakehost(struct handle_info *handle)
889 struct userNode *target;
894 fake = generate_fakehost(handle);
895 for (target = handle->users; target; target = target->next_authed)
896 assign_fakehost(target, fake, 1);
900 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
903 struct handle_info *old_info;
905 /* This can happen if somebody uses COOKIE while authed, or if
906 * they re-auth to their current handle (which is silly, but users
908 if (user->handle_info == hi)
911 if (user->handle_info) {
912 struct userNode *other;
915 userList_remove(&curr_helpers, user);
917 /* remove from next_authed linked list */
918 if (user->handle_info->users == user) {
919 user->handle_info->users = user->next_authed;
921 for (other = user->handle_info->users;
922 other->next_authed != user;
923 other = other->next_authed) ;
924 other->next_authed = user->next_authed;
926 /* if nobody left on old handle, and they're not an oper, remove !god */
927 if (!user->handle_info->users && !user->handle_info->opserv_level)
928 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
929 /* record them as being last seen at this time */
930 user->handle_info->lastseen = now;
931 /* and record their hostmask */
932 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
934 old_info = user->handle_info;
935 user->handle_info = hi;
936 if (hi && !hi->users && !hi->opserv_level)
937 HANDLE_CLEAR_FLAG(hi, HELPING);
938 for (n=0; n<auth_func_used; n++)
939 auth_func_list[n](user, old_info);
941 struct nick_info *ni;
943 HANDLE_CLEAR_FLAG(hi, FROZEN);
944 if (nickserv_conf.warn_clone_auth) {
945 struct userNode *other;
946 for (other = hi->users; other; other = other->next_authed)
947 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
949 user->next_authed = hi->users;
952 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
953 userList_append(&curr_helpers, user);
955 if (hi->fakehost || old_info)
959 if (!nickserv_conf.disable_nicks) {
960 struct nick_info *ni;
961 for (ni = hi->nicks; ni; ni = ni->next) {
962 if (!irccasecmp(user->nick, ni->nick)) {
963 user->modes |= FLAGS_REGNICK;
968 StampUser(user, hi->handle, hi->registered, hi->id);
971 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
972 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
974 /* We cannot clear the user's account ID, unfortunately. */
975 user->next_authed = NULL;
979 static struct handle_info*
980 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
982 struct handle_info *hi;
983 struct nick_info *ni;
984 char crypted[MD5_CRYPT_LENGTH];
986 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
987 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
991 if (!is_secure_password(handle, passwd, user))
994 cryptpass(passwd, crypted);
995 hi = register_handle(handle, crypted, 0);
996 hi->masks = alloc_string_list(1);
998 hi->language = lang_C;
999 hi->registered = now;
1001 hi->flags = HI_DEFAULT_FLAGS;
1002 if (settee && !no_auth)
1003 set_user_handle_info(settee, hi, 1);
1006 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1007 else if (nickserv_conf.disable_nicks)
1008 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1009 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1010 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1012 register_nick(user->nick, hi);
1013 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1015 if (settee && (user != settee))
1016 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1021 nickserv_bake_cookie(struct handle_cookie *cookie)
1023 cookie->hi->cookie = cookie;
1024 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1028 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1030 struct handle_cookie *cookie;
1031 char subject[128], body[4096], *misc;
1032 const char *netname, *fmt;
1036 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1040 cookie = calloc(1, sizeof(*cookie));
1042 cookie->type = type;
1043 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1044 cookie->expires = now + nickserv_conf.cookie_timeout;
1045 inttobase64(cookie->cookie, rand(), 5);
1046 inttobase64(cookie->cookie+5, rand(), 5);
1048 netname = nickserv_conf.network_name;
1051 switch (cookie->type) {
1053 hi->passwd[0] = 0; /* invalidate password */
1054 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1055 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1056 snprintf(subject, sizeof(subject), fmt, netname);
1057 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1058 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1061 case PASSWORD_CHANGE:
1062 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1063 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1064 snprintf(subject, sizeof(subject), fmt, netname);
1065 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1066 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1069 misc = hi->email_addr;
1070 hi->email_addr = cookie->data;
1072 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1073 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1074 snprintf(subject, sizeof(subject), fmt, netname);
1075 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1076 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1077 mail_send(nickserv, hi, subject, body, 1);
1078 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1079 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1081 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1082 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1083 snprintf(subject, sizeof(subject), fmt, netname);
1084 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1085 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1086 mail_send(nickserv, hi, subject, body, 1);
1089 hi->email_addr = misc;
1092 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1093 snprintf(subject, sizeof(subject), fmt, netname);
1094 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1095 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1096 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1099 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1103 mail_send(nickserv, hi, subject, body, first_time);
1104 nickserv_bake_cookie(cookie);
1108 nickserv_eat_cookie(struct handle_cookie *cookie)
1110 cookie->hi->cookie = NULL;
1111 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1112 nickserv_free_cookie(cookie);
1116 nickserv_free_email_addr(void *data)
1118 handle_info_list_clean(data);
1123 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1125 struct handle_info_list *hil;
1126 /* Remove from old handle_info_list ... */
1127 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1128 handle_info_list_remove(hil, hi);
1129 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1130 hi->email_addr = NULL;
1132 /* Add to the new list.. */
1133 if (new_email_addr) {
1134 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1135 hil = calloc(1, sizeof(*hil));
1136 hil->tag = strdup(new_email_addr);
1137 handle_info_list_init(hil);
1138 dict_insert(nickserv_email_dict, hil->tag, hil);
1140 handle_info_list_append(hil, hi);
1141 hi->email_addr = hil->tag;
1145 static NICKSERV_FUNC(cmd_register)
1148 struct handle_info *hi;
1149 const char *email_addr, *password;
1152 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1153 /* Require the first handle registered to belong to someone +o. */
1154 reply("NSMSG_REQUIRE_OPER");
1158 if (user->handle_info) {
1159 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1163 if (IsRegistering(user)) {
1164 reply("NSMSG_ALREADY_REGISTERING");
1168 if (IsStamped(user)) {
1169 /* Unauthenticated users might still have been stamped
1170 previously and could therefore have a hidden host;
1171 do not allow them to register a new account. */
1172 reply("NSMSG_STAMPED_REGISTER");
1176 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1178 if (!is_valid_handle(argv[1])) {
1179 reply("NSMSG_BAD_HANDLE", argv[1]);
1183 if ((argc >= 4) && nickserv_conf.email_enabled) {
1184 struct handle_info_list *hil;
1187 /* Remember email address. */
1188 email_addr = argv[3];
1190 /* Check that the email address looks valid.. */
1191 if (!is_valid_email_addr(email_addr)) {
1192 reply("NSMSG_BAD_EMAIL_ADDR");
1196 /* .. and that we are allowed to send to it. */
1197 if ((str = mail_prohibited_address(email_addr))) {
1198 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1202 /* If we do email verify, make sure we don't spam the address. */
1203 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1205 for (nn=0; nn<hil->used; nn++) {
1206 if (hil->list[nn]->cookie) {
1207 reply("NSMSG_EMAIL_UNACTIVATED");
1211 if (hil->used >= nickserv_conf.handles_per_email) {
1212 reply("NSMSG_EMAIL_OVERUSED");
1225 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1227 /* Add any masks they should get. */
1228 if (nickserv_conf.default_hostmask) {
1229 string_list_append(hi->masks, strdup("*@*"));
1231 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1232 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1233 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1236 /* If they're the first to register, give them level 1000. */
1237 if (dict_size(nickserv_handle_dict) == 1) {
1238 hi->opserv_level = 1000;
1239 reply("NSMSG_ROOT_HANDLE", argv[1]);
1242 /* Set their email address. */
1244 nickserv_set_email_addr(hi, email_addr);
1246 /* If they need to do email verification, tell them. */
1248 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1250 /* Set registering flag.. */
1251 user->modes |= FLAGS_REGISTERING;
1256 static NICKSERV_FUNC(cmd_oregister)
1259 struct userNode *settee;
1260 struct handle_info *hi;
1262 NICKSERV_MIN_PARMS(3);
1264 if (!is_valid_handle(argv[1])) {
1265 reply("NSMSG_BAD_HANDLE", argv[1]);
1272 } else if (strchr(argv[3], '@')) {
1273 mask = canonicalize_hostmask(strdup(argv[3]));
1275 settee = GetUserH(argv[4]);
1277 reply("MSG_NICK_UNKNOWN", argv[4]);
1284 } else if ((settee = GetUserH(argv[3]))) {
1285 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1287 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1290 if (settee && settee->handle_info) {
1291 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1295 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1300 string_list_append(hi->masks, mask);
1304 static NICKSERV_FUNC(cmd_handleinfo)
1307 unsigned int i, pos=0, herelen;
1308 struct userNode *target, *next_un;
1309 struct handle_info *hi;
1310 const char *nsmsg_none;
1314 if (!(hi = user->handle_info)) {
1315 reply("NSMSG_MUST_AUTH");
1318 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1322 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1323 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1324 feh = hi->registered;
1325 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1328 intervalString(buff, now - hi->lastseen, user->handle_info);
1329 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1331 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1334 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1335 if (HANDLE_FLAGGED(hi, FROZEN))
1336 reply("NSMSG_HANDLEINFO_VACATION");
1338 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1339 struct do_not_register *dnr;
1340 if ((dnr = chanserv_is_dnr(NULL, hi)))
1341 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1342 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1344 } else if (hi != user->handle_info)
1348 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1350 if (nickserv_conf.email_enabled)
1351 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1355 switch (hi->cookie->type) {
1356 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1357 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1358 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1359 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1360 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1365 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1366 reply("NSMSG_HANDLEINFO_ID", hi->id);
1368 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1370 reply("NSMSG_HANDLEINFO_NO_NOTES");
1372 struct handle_note *prev, *note;
1374 WALK_NOTES(hi, prev, note) {
1375 char set_time[INTERVALLEN];
1376 intervalString(set_time, now - note->set, user->handle_info);
1377 if (note->expires) {
1378 char exp_time[INTERVALLEN];
1379 intervalString(exp_time, note->expires - now, user->handle_info);
1380 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1382 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1389 unsigned long flen = 1;
1390 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1392 for (i=0, flen=1; handle_flags[i]; i++)
1393 if (hi->flags & 1 << i)
1394 flags[flen++] = handle_flags[i];
1396 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1398 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1401 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1402 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1403 || (hi->opserv_level > 0)) {
1404 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1408 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1410 if (hi->last_quit_host[0])
1411 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1413 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1415 if (nickserv_conf.disable_nicks) {
1416 /* nicks disabled; don't show anything about registered nicks */
1417 } else if (hi->nicks) {
1418 struct nick_info *ni, *next_ni;
1419 for (ni = hi->nicks; ni; ni = next_ni) {
1420 herelen = strlen(ni->nick);
1421 if (pos + herelen + 1 > ArrayLength(buff)) {
1423 goto print_nicks_buff;
1427 memcpy(buff+pos, ni->nick, herelen);
1428 pos += herelen; buff[pos++] = ' ';
1432 reply("NSMSG_HANDLEINFO_NICKS", buff);
1437 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1440 if (hi->masks->used) {
1441 for (i=0; i < hi->masks->used; i++) {
1442 herelen = strlen(hi->masks->list[i]);
1443 if (pos + herelen + 1 > ArrayLength(buff)) {
1445 goto print_mask_buff;
1447 memcpy(buff+pos, hi->masks->list[i], herelen);
1448 pos += herelen; buff[pos++] = ' ';
1449 if (i+1 == hi->masks->used) {
1452 reply("NSMSG_HANDLEINFO_MASKS", buff);
1457 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1461 struct userData *channel, *next;
1464 for (channel = hi->channels; channel; channel = next) {
1465 next = channel->u_next;
1466 name = channel->channel->channel->name;
1467 herelen = strlen(name);
1468 if (pos + herelen + 7 > ArrayLength(buff)) {
1470 goto print_chans_buff;
1472 if (IsUserSuspended(channel))
1474 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1478 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1483 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1486 for (target = hi->users; target; target = next_un) {
1487 herelen = strlen(target->nick);
1488 if (pos + herelen + 1 > ArrayLength(buff)) {
1490 goto print_cnick_buff;
1492 next_un = target->next_authed;
1494 memcpy(buff+pos, target->nick, herelen);
1495 pos += herelen; buff[pos++] = ' ';
1499 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1504 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1507 static NICKSERV_FUNC(cmd_userinfo)
1509 struct userNode *target;
1511 NICKSERV_MIN_PARMS(2);
1512 if (!(target = GetUserH(argv[1]))) {
1513 reply("MSG_NICK_UNKNOWN", argv[1]);
1516 if (target->handle_info)
1517 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1519 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1523 static NICKSERV_FUNC(cmd_nickinfo)
1525 struct nick_info *ni;
1527 NICKSERV_MIN_PARMS(2);
1528 if (!(ni = get_nick_info(argv[1]))) {
1529 reply("MSG_NICK_UNKNOWN", argv[1]);
1532 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1536 static NICKSERV_FUNC(cmd_notes)
1538 struct handle_info *hi;
1539 struct handle_note *prev, *note;
1542 NICKSERV_MIN_PARMS(2);
1543 if (!(hi = get_victim_oper(user, argv[1])))
1546 WALK_NOTES(hi, prev, note) {
1547 char set_time[INTERVALLEN];
1548 intervalString(set_time, now - note->set, user->handle_info);
1549 if (note->expires) {
1550 char exp_time[INTERVALLEN];
1551 intervalString(exp_time, note->expires - now, user->handle_info);
1552 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1554 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1558 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1562 static NICKSERV_FUNC(cmd_rename_handle)
1564 struct handle_info *hi;
1565 char msgbuf[MAXLEN], *old_handle;
1568 NICKSERV_MIN_PARMS(3);
1569 if (!(hi = get_victim_oper(user, argv[1])))
1571 if (!is_valid_handle(argv[2])) {
1572 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1575 if (get_handle_info(argv[2])) {
1576 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1580 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1581 hi->handle = strdup(argv[2]);
1582 dict_insert(nickserv_handle_dict, hi->handle, hi);
1583 for (nn=0; nn<rf_list_used; nn++)
1584 rf_list[nn](hi, old_handle);
1585 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1586 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1587 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1592 static failpw_func_t *failpw_func_list;
1593 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1596 reg_failpw_func(failpw_func_t func)
1598 if (failpw_func_used == failpw_func_size) {
1599 if (failpw_func_size) {
1600 failpw_func_size <<= 1;
1601 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1603 failpw_func_size = 8;
1604 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1607 failpw_func_list[failpw_func_used++] = func;
1610 static NICKSERV_FUNC(cmd_auth)
1612 int pw_arg, used, maxlogins;
1613 struct handle_info *hi;
1615 struct userNode *other;
1617 if (user->handle_info) {
1618 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1621 if (IsStamped(user)) {
1622 /* Unauthenticated users might still have been stamped
1623 previously and could therefore have a hidden host;
1624 do not allow them to authenticate. */
1625 reply("NSMSG_STAMPED_AUTH");
1629 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1631 } else if (argc == 2) {
1632 if (nickserv_conf.disable_nicks) {
1633 if (!(hi = get_handle_info(user->nick))) {
1634 reply("NSMSG_HANDLE_NOT_FOUND");
1638 /* try to look up their handle from their nick */
1639 struct nick_info *ni;
1640 ni = get_nick_info(user->nick);
1642 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1649 reply("MSG_MISSING_PARAMS", argv[0]);
1650 svccmd_send_help(user, nickserv, cmd);
1654 reply("NSMSG_HANDLE_NOT_FOUND");
1657 /* Responses from here on look up the language used by the handle they asked about. */
1658 passwd = argv[pw_arg];
1659 if (!valid_user_for(user, hi)) {
1660 if (hi->email_addr && nickserv_conf.email_enabled)
1661 send_message_type(4, user, cmd->parent->bot,
1662 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1665 send_message_type(4, user, cmd->parent->bot,
1666 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1668 argv[pw_arg] = "BADMASK";
1671 if (!checkpass(passwd, hi->passwd)) {
1673 send_message_type(4, user, cmd->parent->bot,
1674 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1675 argv[pw_arg] = "BADPASS";
1676 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1677 if (nickserv_conf.autogag_enabled) {
1678 if (!user->auth_policer.params) {
1679 user->auth_policer.last_req = now;
1680 user->auth_policer.params = nickserv_conf.auth_policer_params;
1682 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1684 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1685 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1686 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1688 argv[pw_arg] = "GAGGED";
1693 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1694 send_message_type(4, user, cmd->parent->bot,
1695 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1696 argv[pw_arg] = "SUSPENDED";
1699 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1700 for (used = 0, other = hi->users; other; other = other->next_authed) {
1701 if (++used >= maxlogins) {
1702 send_message_type(4, user, cmd->parent->bot,
1703 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1705 argv[pw_arg] = "MAXLOGINS";
1710 set_user_handle_info(user, hi, 1);
1711 if (nickserv_conf.email_required && !hi->email_addr)
1712 reply("NSMSG_PLEASE_SET_EMAIL");
1713 if (!is_secure_password(hi->handle, passwd, NULL))
1714 reply("NSMSG_WEAK_PASSWORD");
1715 if (hi->passwd[0] != '$')
1716 cryptpass(passwd, hi->passwd);
1717 if (!hi->masks->used) {
1719 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1720 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1721 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1723 argv[pw_arg] = "****";
1724 reply("NSMSG_AUTH_SUCCESS");
1728 static allowauth_func_t *allowauth_func_list;
1729 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1732 reg_allowauth_func(allowauth_func_t func)
1734 if (allowauth_func_used == allowauth_func_size) {
1735 if (allowauth_func_size) {
1736 allowauth_func_size <<= 1;
1737 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1739 allowauth_func_size = 8;
1740 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1743 allowauth_func_list[allowauth_func_used++] = func;
1746 static NICKSERV_FUNC(cmd_allowauth)
1748 struct userNode *target;
1749 struct handle_info *hi;
1752 NICKSERV_MIN_PARMS(2);
1753 if (!(target = GetUserH(argv[1]))) {
1754 reply("MSG_NICK_UNKNOWN", argv[1]);
1757 if (target->handle_info) {
1758 reply("NSMSG_USER_PREV_AUTH", target->nick);
1761 if (IsStamped(target)) {
1762 /* Unauthenticated users might still have been stamped
1763 previously and could therefore have a hidden host;
1764 do not allow them to authenticate to an account. */
1765 reply("NSMSG_USER_PREV_STAMP", target->nick);
1770 else if (!(hi = get_handle_info(argv[2]))) {
1771 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1775 if (hi->opserv_level > user->handle_info->opserv_level) {
1776 reply("MSG_USER_OUTRANKED", hi->handle);
1779 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1780 || (hi->opserv_level > 0))
1781 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1782 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1785 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1786 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1787 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1788 if (nickserv_conf.email_enabled)
1789 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1791 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1792 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1794 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1796 for (n=0; n<allowauth_func_used; n++)
1797 allowauth_func_list[n](user, target, hi);
1801 static NICKSERV_FUNC(cmd_authcookie)
1803 struct handle_info *hi;
1805 NICKSERV_MIN_PARMS(2);
1806 if (user->handle_info) {
1807 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1810 if (IsStamped(user)) {
1811 /* Unauthenticated users might still have been stamped
1812 previously and could therefore have a hidden host;
1813 do not allow them to authenticate to an account. */
1814 reply("NSMSG_STAMPED_AUTHCOOKIE");
1817 if (!(hi = get_handle_info(argv[1]))) {
1818 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1821 if (!hi->email_addr) {
1822 reply("MSG_SET_EMAIL_ADDR");
1825 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1829 static NICKSERV_FUNC(cmd_delcookie)
1831 struct handle_info *hi;
1833 hi = user->handle_info;
1835 reply("NSMSG_NO_COOKIE");
1838 switch (hi->cookie->type) {
1841 reply("NSMSG_MUST_TIME_OUT");
1844 nickserv_eat_cookie(hi->cookie);
1845 reply("NSMSG_ATE_COOKIE");
1851 static NICKSERV_FUNC(cmd_odelcookie)
1853 struct handle_info *hi;
1855 NICKSERV_MIN_PARMS(2);
1857 if (!(hi = get_victim_oper(user, argv[1])))
1861 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
1865 nickserv_eat_cookie(hi->cookie);
1866 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
1871 static NICKSERV_FUNC(cmd_resetpass)
1873 struct handle_info *hi;
1874 char crypted[MD5_CRYPT_LENGTH];
1876 NICKSERV_MIN_PARMS(3);
1877 if (user->handle_info) {
1878 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1881 if (IsStamped(user)) {
1882 /* Unauthenticated users might still have been stamped
1883 previously and could therefore have a hidden host;
1884 do not allow them to activate an account. */
1885 reply("NSMSG_STAMPED_RESETPASS");
1888 if (!(hi = get_handle_info(argv[1]))) {
1889 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1892 if (!hi->email_addr) {
1893 reply("MSG_SET_EMAIL_ADDR");
1896 cryptpass(argv[2], crypted);
1898 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1902 static NICKSERV_FUNC(cmd_cookie)
1904 struct handle_info *hi;
1907 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1910 NICKSERV_MIN_PARMS(3);
1911 if (!(hi = get_handle_info(argv[1]))) {
1912 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1918 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1919 reply("NSMSG_HANDLE_SUSPENDED");
1924 reply("NSMSG_NO_COOKIE");
1928 /* Check validity of operation before comparing cookie to
1929 * prohibit guessing by authed users. */
1930 if (user->handle_info
1931 && (hi->cookie->type != EMAIL_CHANGE)
1932 && (hi->cookie->type != PASSWORD_CHANGE)) {
1933 reply("NSMSG_CANNOT_COOKIE");
1937 if (strcmp(cookie, hi->cookie->cookie)) {
1938 reply("NSMSG_BAD_COOKIE");
1942 switch (hi->cookie->type) {
1944 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1945 set_user_handle_info(user, hi, 1);
1946 reply("NSMSG_HANDLE_ACTIVATED");
1948 case PASSWORD_CHANGE:
1949 set_user_handle_info(user, hi, 1);
1950 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1951 reply("NSMSG_PASSWORD_CHANGED");
1954 nickserv_set_email_addr(hi, hi->cookie->data);
1955 reply("NSMSG_EMAIL_CHANGED");
1958 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1959 set_user_handle_info(user, hi, 1);
1960 nickserv_addmask(user, hi, mask);
1961 reply("NSMSG_AUTH_SUCCESS");
1966 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1967 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1971 nickserv_eat_cookie(hi->cookie);
1976 static NICKSERV_FUNC(cmd_oregnick) {
1978 struct handle_info *target;
1979 struct nick_info *ni;
1981 NICKSERV_MIN_PARMS(3);
1982 if (!(target = modcmd_get_handle_info(user, argv[1])))
1985 if (!is_registerable_nick(nick)) {
1986 reply("NSMSG_BAD_NICK", nick);
1989 ni = dict_find(nickserv_nick_dict, nick, NULL);
1991 reply("NSMSG_NICK_EXISTS", nick);
1994 register_nick(nick, target);
1995 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1999 static NICKSERV_FUNC(cmd_regnick) {
2001 struct nick_info *ni;
2003 if (!is_registerable_nick(user->nick)) {
2004 reply("NSMSG_BAD_NICK", user->nick);
2007 /* count their nicks, see if it's too many */
2008 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2009 if (n >= nickserv_conf.nicks_per_handle) {
2010 reply("NSMSG_TOO_MANY_NICKS");
2013 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2015 reply("NSMSG_NICK_EXISTS", user->nick);
2018 register_nick(user->nick, user->handle_info);
2019 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2023 static NICKSERV_FUNC(cmd_pass)
2025 struct handle_info *hi;
2026 const char *old_pass, *new_pass;
2028 NICKSERV_MIN_PARMS(3);
2029 hi = user->handle_info;
2033 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2034 if (!checkpass(old_pass, hi->passwd)) {
2035 argv[1] = "BADPASS";
2036 reply("NSMSG_PASSWORD_INVALID");
2039 cryptpass(new_pass, hi->passwd);
2041 reply("NSMSG_PASS_SUCCESS");
2046 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2049 char *new_mask = canonicalize_hostmask(strdup(mask));
2050 for (i=0; i<hi->masks->used; i++) {
2051 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2052 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2057 string_list_append(hi->masks, new_mask);
2058 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2062 static NICKSERV_FUNC(cmd_addmask)
2065 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2066 int res = nickserv_addmask(user, user->handle_info, mask);
2070 if (!is_gline(argv[1])) {
2071 reply("NSMSG_MASK_INVALID", argv[1]);
2074 return nickserv_addmask(user, user->handle_info, argv[1]);
2078 static NICKSERV_FUNC(cmd_oaddmask)
2080 struct handle_info *hi;
2082 NICKSERV_MIN_PARMS(3);
2083 if (!(hi = get_victim_oper(user, argv[1])))
2085 return nickserv_addmask(user, hi, argv[2]);
2089 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2092 for (i=0; i<hi->masks->used; i++) {
2093 if (!strcmp(del_mask, hi->masks->list[i])) {
2094 char *old_mask = hi->masks->list[i];
2095 if (hi->masks->used == 1 && !force) {
2096 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2099 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2100 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2105 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2109 static NICKSERV_FUNC(cmd_delmask)
2111 NICKSERV_MIN_PARMS(2);
2112 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2115 static NICKSERV_FUNC(cmd_odelmask)
2117 struct handle_info *hi;
2118 NICKSERV_MIN_PARMS(3);
2119 if (!(hi = get_victim_oper(user, argv[1])))
2121 return nickserv_delmask(user, hi, argv[2], 1);
2125 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2126 unsigned int nn, add = 1, pos;
2127 unsigned long added, removed, flag;
2129 for (added=removed=nn=0; str[nn]; nn++) {
2131 case '+': add = 1; break;
2132 case '-': add = 0; break;
2134 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2135 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2138 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2139 /* cheesy avoidance of looking up the flag name.. */
2140 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2143 flag = 1 << (pos - 1);
2145 added |= flag, removed &= ~flag;
2147 removed |= flag, added &= ~flag;
2152 *premoved = removed;
2157 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2159 unsigned long before, after, added, removed;
2160 struct userNode *uNode;
2162 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2163 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2165 hi->flags = (hi->flags | added) & ~removed;
2166 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2168 /* Strip helping flag if they're only a support helper and not
2169 * currently in #support. */
2170 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2171 struct channelList *schannels;
2173 schannels = chanserv_support_channels();
2174 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2175 for (ii = 0; ii < schannels->used; ++ii)
2176 if (GetUserMode(schannels->list[ii], uNode))
2178 if (ii < schannels->used)
2182 HANDLE_CLEAR_FLAG(hi, HELPING);
2185 if (after && !before) {
2186 /* Add user to current helper list. */
2187 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2188 userList_append(&curr_helpers, uNode);
2189 } else if (!after && before) {
2190 /* Remove user from current helper list. */
2191 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2192 userList_remove(&curr_helpers, uNode);
2199 set_list(struct userNode *user, struct handle_info *hi, int override)
2203 char *set_display[] = {
2204 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2205 "EMAIL", "MAXLOGINS", "LANGUAGE"
2208 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2210 /* Do this so options are presented in a consistent order. */
2211 for (i = 0; i < ArrayLength(set_display); ++i)
2212 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2213 opt(user, hi, override, 0, NULL);
2216 static NICKSERV_FUNC(cmd_set)
2218 struct handle_info *hi;
2221 hi = user->handle_info;
2223 set_list(user, hi, 0);
2226 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2227 reply("NSMSG_INVALID_OPTION", argv[1]);
2230 return opt(user, hi, 0, argc-1, argv+1);
2233 static NICKSERV_FUNC(cmd_oset)
2235 struct handle_info *hi;
2236 struct svccmd *subcmd;
2238 char cmdname[MAXLEN];
2240 NICKSERV_MIN_PARMS(2);
2242 if (!(hi = get_victim_oper(user, argv[1])))
2246 set_list(user, hi, 0);
2250 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2251 reply("NSMSG_INVALID_OPTION", argv[2]);
2255 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2256 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2257 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2260 return opt(user, hi, 1, argc-2, argv+2);
2263 static OPTION_FUNC(opt_info)
2267 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2269 hi->infoline = NULL;
2271 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2275 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2276 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2280 static OPTION_FUNC(opt_width)
2283 hi->screen_width = strtoul(argv[1], NULL, 0);
2285 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2286 hi->screen_width = MIN_LINE_SIZE;
2287 else if (hi->screen_width > MAX_LINE_SIZE)
2288 hi->screen_width = MAX_LINE_SIZE;
2290 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2294 static OPTION_FUNC(opt_tablewidth)
2297 hi->table_width = strtoul(argv[1], NULL, 0);
2299 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2300 hi->table_width = MIN_LINE_SIZE;
2301 else if (hi->screen_width > MAX_LINE_SIZE)
2302 hi->table_width = MAX_LINE_SIZE;
2304 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2308 static OPTION_FUNC(opt_color)
2311 if (enabled_string(argv[1]))
2312 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2313 else if (disabled_string(argv[1]))
2314 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2316 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2321 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2325 static OPTION_FUNC(opt_privmsg)
2328 if (enabled_string(argv[1]))
2329 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2330 else if (disabled_string(argv[1]))
2331 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2333 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2338 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2342 static OPTION_FUNC(opt_style)
2347 if (!irccasecmp(argv[1], "Zoot"))
2348 hi->userlist_style = HI_STYLE_ZOOT;
2349 else if (!irccasecmp(argv[1], "def"))
2350 hi->userlist_style = HI_STYLE_DEF;
2353 switch (hi->userlist_style) {
2362 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2366 static OPTION_FUNC(opt_password)
2369 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2374 cryptpass(argv[1], hi->passwd);
2376 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2380 static OPTION_FUNC(opt_flags)
2383 unsigned int ii, flen;
2386 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2391 nickserv_apply_flags(user, hi, argv[1]);
2393 for (ii = flen = 0; handle_flags[ii]; ii++)
2394 if (hi->flags & (1 << ii))
2395 flags[flen++] = handle_flags[ii];
2398 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2400 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2404 static OPTION_FUNC(opt_email)
2408 if (!is_valid_email_addr(argv[1])) {
2409 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2412 if ((str = mail_prohibited_address(argv[1]))) {
2413 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2416 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2417 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2419 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2421 nickserv_set_email_addr(hi, argv[1]);
2423 nickserv_eat_cookie(hi->cookie);
2424 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2427 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2431 static OPTION_FUNC(opt_maxlogins)
2433 unsigned char maxlogins;
2435 maxlogins = strtoul(argv[1], NULL, 0);
2436 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2437 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2440 hi->maxlogins = maxlogins;
2442 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2443 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2447 static OPTION_FUNC(opt_language)
2449 struct language *lang;
2451 lang = language_find(argv[1]);
2452 if (irccasecmp(lang->name, argv[1]))
2453 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2454 hi->language = lang;
2456 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2460 static OPTION_FUNC(opt_karma)
2463 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2468 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2469 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2470 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2471 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2473 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2477 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2482 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2483 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2485 if ((user->handle_info->opserv_level < target->opserv_level)
2486 || ((user->handle_info->opserv_level == target->opserv_level)
2487 && (user->handle_info->opserv_level < 1000))) {
2488 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2491 if ((user->handle_info->opserv_level < new_level)
2492 || ((user->handle_info->opserv_level == new_level)
2493 && (user->handle_info->opserv_level < 1000))) {
2494 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2497 if (user->handle_info == target) {
2498 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2501 if (target->opserv_level == new_level)
2503 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2504 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2505 target->opserv_level = new_level;
2509 static OPTION_FUNC(opt_level)
2514 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2518 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2519 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2523 static OPTION_FUNC(opt_epithet)
2526 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2530 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2531 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2534 if ((epithet[0] == '*') && !epithet[1])
2537 hi->epithet = strdup(epithet);
2541 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2543 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2547 static OPTION_FUNC(opt_title)
2552 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2556 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2558 if (strchr(title, '.')) {
2559 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2562 if ((strlen(user->handle_info->handle) + strlen(title) +
2563 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2564 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2569 if (!strcmp(title, "*")) {
2570 hi->fakehost = NULL;
2572 hi->fakehost = malloc(strlen(title)+2);
2573 hi->fakehost[0] = '.';
2574 strcpy(hi->fakehost+1, title);
2577 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2578 title = hi->fakehost + 1;
2582 title = user_find_message(user, "MSG_NONE");
2583 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2587 static OPTION_FUNC(opt_fakehost)
2592 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2596 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2598 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2599 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2603 if (!strcmp(fake, "*"))
2604 hi->fakehost = NULL;
2606 hi->fakehost = strdup(fake);
2607 fake = hi->fakehost;
2610 fake = generate_fakehost(hi);
2612 fake = user_find_message(user, "MSG_NONE");
2613 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2617 static NICKSERV_FUNC(cmd_reclaim)
2619 struct handle_info *hi;
2620 struct nick_info *ni;
2621 struct userNode *victim;
2623 NICKSERV_MIN_PARMS(2);
2624 hi = user->handle_info;
2625 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2627 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2630 if (ni->owner != user->handle_info) {
2631 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2634 victim = GetUserH(ni->nick);
2636 reply("MSG_NICK_UNKNOWN", ni->nick);
2639 if (victim == user) {
2640 reply("NSMSG_NICK_USER_YOU");
2643 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2644 switch (nickserv_conf.reclaim_action) {
2645 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2646 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2647 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2648 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2653 static NICKSERV_FUNC(cmd_unregnick)
2656 struct handle_info *hi;
2657 struct nick_info *ni;
2659 hi = user->handle_info;
2660 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2661 ni = dict_find(nickserv_nick_dict, nick, NULL);
2663 reply("NSMSG_UNKNOWN_NICK", nick);
2666 if (hi != ni->owner) {
2667 reply("NSMSG_NOT_YOUR_NICK", nick);
2670 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2675 static NICKSERV_FUNC(cmd_ounregnick)
2677 struct nick_info *ni;
2679 NICKSERV_MIN_PARMS(2);
2680 if (!(ni = get_nick_info(argv[1]))) {
2681 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2684 if (!oper_outranks(user, ni->owner))
2686 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2691 static NICKSERV_FUNC(cmd_unregister)
2693 struct handle_info *hi;
2696 NICKSERV_MIN_PARMS(2);
2697 hi = user->handle_info;
2700 if (checkpass(passwd, hi->passwd)) {
2701 nickserv_unregister_handle(hi, user);
2704 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2705 reply("NSMSG_PASSWORD_INVALID");
2710 static NICKSERV_FUNC(cmd_ounregister)
2712 struct handle_info *hi;
2713 char reason[MAXLEN];
2716 NICKSERV_MIN_PARMS(2);
2717 if (!(hi = get_victim_oper(user, argv[1])))
2720 if (HANDLE_FLAGGED(hi, NODELETE)) {
2721 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
2725 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
2727 ((hi->flags & nickserv_conf.ounregister_flags)
2729 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
2730 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
2734 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2735 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2736 nickserv_unregister_handle(hi, user);
2740 static NICKSERV_FUNC(cmd_status)
2742 if (nickserv_conf.disable_nicks) {
2743 reply("NSMSG_GLOBAL_STATS_NONICK",
2744 dict_size(nickserv_handle_dict));
2746 if (user->handle_info) {
2748 struct nick_info *ni;
2749 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2750 reply("NSMSG_HANDLE_STATS", cnt);
2752 reply("NSMSG_HANDLE_NONE");
2754 reply("NSMSG_GLOBAL_STATS",
2755 dict_size(nickserv_handle_dict),
2756 dict_size(nickserv_nick_dict));
2761 static NICKSERV_FUNC(cmd_ghost)
2763 struct userNode *target;
2764 char reason[MAXLEN];
2766 NICKSERV_MIN_PARMS(2);
2767 if (!(target = GetUserH(argv[1]))) {
2768 reply("MSG_NICK_UNKNOWN", argv[1]);
2771 if (target == user) {
2772 reply("NSMSG_CANNOT_GHOST_SELF");
2775 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2776 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2779 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2780 DelUser(target, nickserv, 1, reason);
2781 reply("NSMSG_GHOST_KILLED", argv[1]);
2785 static NICKSERV_FUNC(cmd_vacation)
2787 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2788 reply("NSMSG_ON_VACATION");
2792 static NICKSERV_FUNC(cmd_addnote)
2794 struct handle_info *hi;
2795 unsigned long duration;
2798 struct handle_note *prev;
2799 struct handle_note *note;
2801 /* Parse parameters and figure out values for note's fields. */
2802 NICKSERV_MIN_PARMS(4);
2803 hi = get_victim_oper(user, argv[1]);
2806 if(!strcmp(argv[2], "0"))
2808 else if(!(duration = ParseInterval(argv[2])))
2810 reply("MSG_INVALID_DURATION", argv[2]);
2813 if (duration > 2*365*86400) {
2814 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2817 unsplit_string(argv + 3, argc - 3, text);
2818 WALK_NOTES(hi, prev, note) {}
2819 id = prev ? (prev->id + 1) : 1;
2821 /* Create the new note structure. */
2822 note = calloc(1, sizeof(*note) + strlen(text));
2824 note->expires = duration ? (now + duration) : 0;
2827 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2828 strcpy(note->note, text);
2833 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2837 static NICKSERV_FUNC(cmd_delnote)
2839 struct handle_info *hi;
2840 struct handle_note *prev;
2841 struct handle_note *note;
2844 NICKSERV_MIN_PARMS(3);
2845 hi = get_victim_oper(user, argv[1]);
2848 id = strtoul(argv[2], NULL, 10);
2849 WALK_NOTES(hi, prev, note) {
2850 if (id == note->id) {
2852 prev->next = note->next;
2854 hi->notes = note->next;
2856 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2860 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2865 nickserv_saxdb_write(struct saxdb_context *ctx) {
2867 struct handle_info *hi;
2870 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2872 assert(hi->id != 0);
2873 saxdb_start_record(ctx, iter_key(it), 0);
2875 struct handle_cookie *cookie = hi->cookie;
2878 switch (cookie->type) {
2879 case ACTIVATION: type = KEY_ACTIVATION; break;
2880 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2881 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2882 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2883 default: type = NULL; break;
2886 saxdb_start_record(ctx, KEY_COOKIE, 0);
2887 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2888 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2890 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2891 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2892 saxdb_end_record(ctx);
2896 struct handle_note *prev, *note;
2897 saxdb_start_record(ctx, KEY_NOTES, 0);
2898 WALK_NOTES(hi, prev, note) {
2899 snprintf(flags, sizeof(flags), "%d", note->id);
2900 saxdb_start_record(ctx, flags, 0);
2902 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
2903 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
2904 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
2905 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
2906 saxdb_end_record(ctx);
2908 saxdb_end_record(ctx);
2911 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2913 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2915 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2919 for (ii=flen=0; handle_flags[ii]; ++ii)
2920 if (hi->flags & (1 << ii))
2921 flags[flen++] = handle_flags[ii];
2923 saxdb_write_string(ctx, KEY_FLAGS, flags);
2925 saxdb_write_int(ctx, KEY_ID, hi->id);
2927 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2928 if (hi->last_quit_host[0])
2929 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2930 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2932 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
2933 if (hi->masks->used)
2934 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2936 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2938 struct string_list *slist;
2939 struct nick_info *ni;
2941 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2942 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2943 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2947 if (hi->opserv_level)
2948 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2949 if (hi->language != lang_C)
2950 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2951 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2952 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2953 if (hi->screen_width)
2954 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2955 if (hi->table_width)
2956 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2957 flags[0] = hi->userlist_style;
2959 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2960 saxdb_end_record(ctx);
2965 static handle_merge_func_t *handle_merge_func_list;
2966 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2969 reg_handle_merge_func(handle_merge_func_t func)
2971 if (handle_merge_func_used == handle_merge_func_size) {
2972 if (handle_merge_func_size) {
2973 handle_merge_func_size <<= 1;
2974 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2976 handle_merge_func_size = 8;
2977 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2980 handle_merge_func_list[handle_merge_func_used++] = func;
2983 static NICKSERV_FUNC(cmd_merge)
2985 struct handle_info *hi_from, *hi_to;
2986 struct userNode *last_user;
2987 struct userData *cList, *cListNext;
2988 unsigned int ii, jj, n;
2989 char buffer[MAXLEN];
2991 NICKSERV_MIN_PARMS(3);
2993 if (!(hi_from = get_victim_oper(user, argv[1])))
2995 if (!(hi_to = get_victim_oper(user, argv[2])))
2997 if (hi_to == hi_from) {
2998 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3002 for (n=0; n<handle_merge_func_used; n++)
3003 handle_merge_func_list[n](user, hi_to, hi_from);
3005 /* Append "from" handle's nicks to "to" handle's nick list. */
3007 struct nick_info *last_ni;
3008 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3009 last_ni->next = hi_from->nicks;
3011 while (hi_from->nicks) {
3012 hi_from->nicks->owner = hi_to;
3013 hi_from->nicks = hi_from->nicks->next;
3016 /* Merge the hostmasks. */
3017 for (ii=0; ii<hi_from->masks->used; ii++) {
3018 char *mask = hi_from->masks->list[ii];
3019 for (jj=0; jj<hi_to->masks->used; jj++)
3020 if (match_ircglobs(hi_to->masks->list[jj], mask))
3022 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3023 string_list_append(hi_to->masks, strdup(mask));
3026 /* Merge the lists of authed users. */
3028 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3029 last_user->next_authed = hi_from->users;
3031 hi_to->users = hi_from->users;
3033 /* Repoint the old "from" handle's users. */
3034 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3035 last_user->handle_info = hi_to;
3037 hi_from->users = NULL;
3039 /* Merge channel userlists. */
3040 for (cList=hi_from->channels; cList; cList=cListNext) {
3041 struct userData *cList2;
3042 cListNext = cList->u_next;
3043 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3044 if (cList->channel == cList2->channel)
3046 if (cList2 && (cList2->access >= cList->access)) {
3047 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);
3048 /* keep cList2 in hi_to; remove cList from hi_from */
3049 del_channel_user(cList, 1);
3052 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);
3053 /* remove the lower-ranking cList2 from hi_to */
3054 del_channel_user(cList2, 1);
3056 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3058 /* cList needs to be moved from hi_from to hi_to */
3059 cList->handle = hi_to;
3060 /* Remove from linked list for hi_from */
3061 assert(!cList->u_prev);
3062 hi_from->channels = cList->u_next;
3064 cList->u_next->u_prev = cList->u_prev;
3065 /* Add to linked list for hi_to */
3066 cList->u_prev = NULL;
3067 cList->u_next = hi_to->channels;
3068 if (hi_to->channels)
3069 hi_to->channels->u_prev = cList;
3070 hi_to->channels = cList;
3074 /* Do they get an OpServ level promotion? */
3075 if (hi_from->opserv_level > hi_to->opserv_level)
3076 hi_to->opserv_level = hi_from->opserv_level;
3078 /* What about last seen time? */
3079 if (hi_from->lastseen > hi_to->lastseen)
3080 hi_to->lastseen = hi_from->lastseen;
3082 /* New karma is the sum of the two original karmas. */
3083 hi_to->karma += hi_from->karma;
3085 /* Does a fakehost carry over? (This intentionally doesn't set it
3086 * for users previously attached to hi_to. They'll just have to
3089 if (hi_from->fakehost && !hi_to->fakehost)
3090 hi_to->fakehost = strdup(hi_from->fakehost);
3092 /* Notify of success. */
3093 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3094 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3095 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3097 /* Unregister the "from" handle. */
3098 nickserv_unregister_handle(hi_from, NULL);
3103 struct nickserv_discrim {
3104 unsigned long flags_on, flags_off;
3105 unsigned long min_registered, max_registered;
3106 unsigned long lastseen;
3108 int min_level, max_level;
3109 int min_karma, max_karma;
3110 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3111 const char *nickmask;
3112 const char *hostmask;
3113 const char *fakehostmask;
3114 const char *handlemask;
3115 const char *emailmask;
3118 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3120 struct discrim_apply_info {
3121 struct nickserv_discrim *discrim;
3122 discrim_search_func func;
3123 struct userNode *source;
3124 unsigned int matched;
3127 static struct nickserv_discrim *
3128 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3131 struct nickserv_discrim *discrim;
3133 discrim = malloc(sizeof(*discrim));
3134 memset(discrim, 0, sizeof(*discrim));
3135 discrim->min_level = 0;
3136 discrim->max_level = INT_MAX;
3137 discrim->limit = 50;
3138 discrim->min_registered = 0;
3139 discrim->max_registered = ULONG_MAX;
3140 discrim->lastseen = ULONG_MAX;
3141 discrim->min_karma = INT_MIN;
3142 discrim->max_karma = INT_MAX;
3144 for (i=0; i<argc; i++) {
3145 if (i == argc - 1) {
3146 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3149 if (!irccasecmp(argv[i], "limit")) {
3150 discrim->limit = strtoul(argv[++i], NULL, 0);
3151 } else if (!irccasecmp(argv[i], "flags")) {
3152 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3153 } else if (!irccasecmp(argv[i], "registered")) {
3154 const char *cmp = argv[++i];
3155 if (cmp[0] == '<') {
3156 if (cmp[1] == '=') {
3157 discrim->min_registered = now - ParseInterval(cmp+2);
3159 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3161 } else if (cmp[0] == '=') {
3162 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3163 } else if (cmp[0] == '>') {
3164 if (cmp[1] == '=') {
3165 discrim->max_registered = now - ParseInterval(cmp+2);
3167 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3170 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3172 } else if (!irccasecmp(argv[i], "seen")) {
3173 discrim->lastseen = now - ParseInterval(argv[++i]);
3174 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3175 discrim->nickmask = argv[++i];
3176 } else if (!irccasecmp(argv[i], "hostmask")) {
3178 if (!irccasecmp(argv[i], "exact")) {
3179 if (i == argc - 1) {
3180 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3183 discrim->hostmask_type = EXACT;
3184 } else if (!irccasecmp(argv[i], "subset")) {
3185 if (i == argc - 1) {
3186 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3189 discrim->hostmask_type = SUBSET;
3190 } else if (!irccasecmp(argv[i], "superset")) {
3191 if (i == argc - 1) {
3192 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3195 discrim->hostmask_type = SUPERSET;
3196 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3197 if (i == argc - 1) {
3198 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3201 discrim->hostmask_type = LASTQUIT;
3204 discrim->hostmask_type = SUPERSET;
3206 discrim->hostmask = argv[++i];
3207 } else if (!irccasecmp(argv[i], "fakehost")) {
3208 if (!irccasecmp(argv[++i], "*")) {
3209 discrim->fakehostmask = 0;
3211 discrim->fakehostmask = argv[i];
3213 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3214 if (!irccasecmp(argv[++i], "*")) {
3215 discrim->handlemask = 0;
3217 discrim->handlemask = argv[i];
3219 } else if (!irccasecmp(argv[i], "email")) {
3220 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3221 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3223 } else if (!irccasecmp(argv[++i], "*")) {
3224 discrim->emailmask = 0;
3226 discrim->emailmask = argv[i];
3228 } else if (!irccasecmp(argv[i], "access")) {
3229 const char *cmp = argv[++i];
3230 if (cmp[0] == '<') {
3231 if (discrim->min_level == 0) discrim->min_level = 1;
3232 if (cmp[1] == '=') {
3233 discrim->max_level = strtoul(cmp+2, NULL, 0);
3235 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3237 } else if (cmp[0] == '=') {
3238 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3239 } else if (cmp[0] == '>') {
3240 if (cmp[1] == '=') {
3241 discrim->min_level = strtoul(cmp+2, NULL, 0);
3243 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3246 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3248 } else if (!irccasecmp(argv[i], "karma")) {
3249 const char *cmp = argv[++i];
3250 if (cmp[0] == '<') {
3251 if (cmp[1] == '=') {
3252 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3254 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3256 } else if (cmp[0] == '=') {
3257 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3258 } else if (cmp[0] == '>') {
3259 if (cmp[1] == '=') {
3260 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3262 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3265 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3268 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3279 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3281 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3282 || (discrim->flags_off & hi->flags)
3283 || (discrim->min_registered > hi->registered)
3284 || (discrim->max_registered < hi->registered)
3285 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3286 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3287 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3288 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3289 || (discrim->min_level > hi->opserv_level)
3290 || (discrim->max_level < hi->opserv_level)
3291 || (discrim->min_karma > hi->karma)
3292 || (discrim->max_karma < hi->karma)
3296 if (discrim->hostmask) {
3298 for (i=0; i<hi->masks->used; i++) {
3299 const char *mask = hi->masks->list[i];
3300 if ((discrim->hostmask_type == SUBSET)
3301 && (match_ircglobs(discrim->hostmask, mask))) break;
3302 else if ((discrim->hostmask_type == EXACT)
3303 && !irccasecmp(discrim->hostmask, mask)) break;
3304 else if ((discrim->hostmask_type == SUPERSET)
3305 && (match_ircglobs(mask, discrim->hostmask))) break;
3306 else if ((discrim->hostmask_type == LASTQUIT)
3307 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3309 if (i==hi->masks->used) return 0;
3311 if (discrim->nickmask) {
3312 struct nick_info *nick = hi->nicks;
3314 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3317 if (!nick) return 0;
3323 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3325 dict_iterator_t it, next;
3326 unsigned int matched;
3328 for (it = dict_first(nickserv_handle_dict), matched = 0;
3329 it && (matched < discrim->limit);
3331 next = iter_next(it);
3332 if (nickserv_discrim_match(discrim, iter_data(it))) {
3333 dsf(source, iter_data(it));
3341 search_print_func(struct userNode *source, struct handle_info *match)
3343 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3347 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3352 search_unregister_func (struct userNode *source, struct handle_info *match)
3354 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3355 nickserv_unregister_handle(match, source);
3359 nickserv_sort_accounts_by_access(const void *a, const void *b)
3361 const struct handle_info *hi_a = *(const struct handle_info**)a;
3362 const struct handle_info *hi_b = *(const struct handle_info**)b;
3363 if (hi_a->opserv_level != hi_b->opserv_level)
3364 return hi_b->opserv_level - hi_a->opserv_level;
3365 return irccasecmp(hi_a->handle, hi_b->handle);
3369 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3371 struct handle_info_list hil;
3372 struct helpfile_table tbl;
3377 memset(&hil, 0, sizeof(hil));
3378 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3379 struct handle_info *hi = iter_data(it);
3380 if (hi->opserv_level)
3381 handle_info_list_append(&hil, hi);
3383 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3384 tbl.length = hil.used + 1;
3386 tbl.flags = TABLE_NO_FREE;
3387 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3388 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3391 for (ii = 0; ii < hil.used; ) {
3392 ary = malloc(tbl.width * sizeof(ary[0]));
3393 ary[0] = hil.list[ii]->handle;
3394 ary[1] = strtab(hil.list[ii]->opserv_level);
3395 tbl.contents[++ii] = ary;
3397 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3398 reply("MSG_MATCH_COUNT", hil.used);
3399 for (ii = 0; ii < hil.used; ii++)
3400 free(tbl.contents[ii]);
3405 static NICKSERV_FUNC(cmd_search)
3407 struct nickserv_discrim *discrim;
3408 discrim_search_func action;
3409 struct svccmd *subcmd;
3410 unsigned int matches;
3413 NICKSERV_MIN_PARMS(3);
3414 sprintf(buf, "search %s", argv[1]);
3415 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3416 if (!irccasecmp(argv[1], "print"))
3417 action = search_print_func;
3418 else if (!irccasecmp(argv[1], "count"))
3419 action = search_count_func;
3420 else if (!irccasecmp(argv[1], "unregister"))
3421 action = search_unregister_func;
3423 reply("NSMSG_INVALID_ACTION", argv[1]);
3427 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3430 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3434 if (action == search_print_func)
3435 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3436 else if (action == search_count_func)
3437 discrim->limit = INT_MAX;
3439 matches = nickserv_discrim_search(discrim, action, user);
3442 reply("MSG_MATCH_COUNT", matches);
3444 reply("MSG_NO_MATCHES");
3450 static MODCMD_FUNC(cmd_checkpass)
3452 struct handle_info *hi;
3454 NICKSERV_MIN_PARMS(3);
3455 if (!(hi = get_handle_info(argv[1]))) {
3456 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3459 if (checkpass(argv[2], hi->passwd))
3460 reply("CHECKPASS_YES");
3462 reply("CHECKPASS_NO");
3467 static MODCMD_FUNC(cmd_checkemail)
3469 struct handle_info *hi;
3471 NICKSERV_MIN_PARMS(3);
3472 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3473 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3476 if (!hi->email_addr)
3477 reply("CHECKEMAIL_NOT_SET");
3478 else if (!irccasecmp(argv[2], hi->email_addr))
3479 reply("CHECKEMAIL_YES");
3481 reply("CHECKEMAIL_NO");
3487 nickserv_db_read_handle(const char *handle, dict_t obj)
3490 struct string_list *masks, *slist;
3491 struct handle_info *hi;
3492 struct userNode *authed_users;
3493 struct userData *channels;
3498 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3499 id = str ? strtoul(str, NULL, 0) : 0;
3500 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3502 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3505 if ((hi = get_handle_info(handle))) {
3506 authed_users = hi->users;
3507 channels = hi->channels;
3509 hi->channels = NULL;
3510 dict_remove(nickserv_handle_dict, hi->handle);
3512 authed_users = NULL;
3515 hi = register_handle(handle, str, id);
3517 hi->users = authed_users;
3518 while (authed_users) {
3519 authed_users->handle_info = hi;
3520 authed_users = authed_users->next_authed;
3523 hi->channels = channels;
3524 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3525 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3526 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3527 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3528 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3529 hi->language = language_find(str ? str : "C");
3530 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3531 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3532 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3534 hi->infoline = strdup(str);
3535 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3536 hi->registered = str ? strtoul(str, NULL, 0) : now;
3537 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3538 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3539 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3540 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3541 /* We want to read the nicks even if disable_nicks is set. This is so
3542 * that we don't lose the nick data entirely. */
3543 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3545 for (ii=0; ii<slist->used; ii++)
3546 register_nick(slist->list[ii], hi);
3548 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3550 for (ii=0; str[ii]; ii++)
3551 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3553 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3554 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3555 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3556 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3557 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3558 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3559 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3561 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3563 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3564 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3566 nickserv_set_email_addr(hi, str);
3567 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3569 hi->epithet = strdup(str);
3570 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3572 hi->fakehost = strdup(str);
3573 /* Read the "cookie" sub-database (if it exists). */
3574 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3576 const char *data, *type, *expires, *cookie_str;
3577 struct handle_cookie *cookie;
3579 cookie = calloc(1, sizeof(*cookie));
3580 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3581 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3582 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3583 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3584 if (!type || !expires || !cookie_str) {
3585 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3588 if (!irccasecmp(type, KEY_ACTIVATION))
3589 cookie->type = ACTIVATION;
3590 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3591 cookie->type = PASSWORD_CHANGE;
3592 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3593 cookie->type = EMAIL_CHANGE;
3594 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3595 cookie->type = ALLOWAUTH;
3597 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3600 cookie->expires = strtoul(expires, NULL, 0);
3601 if (cookie->expires < now)
3604 cookie->data = strdup(data);
3605 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3609 nickserv_bake_cookie(cookie);
3611 nickserv_free_cookie(cookie);
3613 /* Read the "notes" sub-database (if it exists). */
3614 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3617 struct handle_note *last_note;
3618 struct handle_note *note;
3621 for (it = dict_first(subdb); it; it = iter_next(it)) {
3622 const char *expires;
3630 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3632 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", id, hi->handle);
3635 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3636 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3637 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3638 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3639 if (!setter || !text || !set) {
3640 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", id, hi->handle);
3643 note = calloc(1, sizeof(*note) + strlen(text));
3645 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3646 note->set = strtoul(set, NULL, 10);
3647 note->id = strtoul(id, NULL, 10);
3648 safestrncpy(note->setter, setter, sizeof(note->setter));
3649 strcpy(note->note, text);
3651 last_note->next = note;
3660 nickserv_saxdb_read(dict_t db) {
3662 struct record_data *rd;
3664 for (it=dict_first(db); it; it=iter_next(it)) {
3666 nickserv_db_read_handle(iter_key(it), rd->d.object);
3671 static NICKSERV_FUNC(cmd_mergedb)
3673 struct timeval start, stop;
3676 NICKSERV_MIN_PARMS(2);
3677 gettimeofday(&start, NULL);
3678 if (!(db = parse_database(argv[1]))) {
3679 reply("NSMSG_DB_UNREADABLE", argv[1]);
3682 nickserv_saxdb_read(db);
3684 gettimeofday(&stop, NULL);
3685 stop.tv_sec -= start.tv_sec;
3686 stop.tv_usec -= start.tv_usec;
3687 if (stop.tv_usec < 0) {
3689 stop.tv_usec += 1000000;
3691 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
3696 expire_handles(UNUSED_ARG(void *data))
3698 dict_iterator_t it, next;
3699 unsigned long expiry;
3700 struct handle_info *hi;
3702 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3703 next = iter_next(it);
3705 if ((hi->opserv_level > 0)
3707 || HANDLE_FLAGGED(hi, FROZEN)
3708 || HANDLE_FLAGGED(hi, NODELETE)) {
3711 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3712 if ((now - hi->lastseen) > expiry) {
3713 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3714 nickserv_unregister_handle(hi, NULL);
3718 if (nickserv_conf.handle_expire_frequency)
3719 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3723 nickserv_load_dict(const char *fname)
3727 if (!(file = fopen(fname, "r"))) {
3728 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3731 while (!feof(file)) {
3732 fgets(line, sizeof(line), file);
3735 if (line[strlen(line)-1] == '\n')
3736 line[strlen(line)-1] = 0;
3737 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3740 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3743 static enum reclaim_action
3744 reclaim_action_from_string(const char *str) {
3746 return RECLAIM_NONE;
3747 else if (!irccasecmp(str, "warn"))
3748 return RECLAIM_WARN;
3749 else if (!irccasecmp(str, "svsnick"))
3750 return RECLAIM_SVSNICK;
3751 else if (!irccasecmp(str, "kill"))
3752 return RECLAIM_KILL;
3754 return RECLAIM_NONE;
3758 nickserv_conf_read(void)
3760 dict_t conf_node, child;
3764 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3765 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3768 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3770 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3771 if (nickserv_conf.valid_handle_regex_set)
3772 regfree(&nickserv_conf.valid_handle_regex);
3774 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3775 nickserv_conf.valid_handle_regex_set = !err;
3776 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3778 nickserv_conf.valid_handle_regex_set = 0;
3780 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3781 if (nickserv_conf.valid_nick_regex_set)
3782 regfree(&nickserv_conf.valid_nick_regex);
3784 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3785 nickserv_conf.valid_nick_regex_set = !err;
3786 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3788 nickserv_conf.valid_nick_regex_set = 0;
3790 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3792 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3793 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3794 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3795 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3796 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3797 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3798 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3799 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3800 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3801 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3802 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3803 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3804 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3805 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3806 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3807 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3808 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3809 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3810 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3811 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3812 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3813 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3814 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3815 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3816 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3818 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3819 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3820 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3822 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3823 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3824 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3826 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3827 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3828 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3829 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3830 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3831 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3832 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3833 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3834 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
3835 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
3836 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
3839 nickserv_conf.ounregister_flags = 0;
3841 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
3844 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
3846 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
3848 nickserv_conf.handle_ts_mode = TS_IGNORE;
3849 else if (!irccasecmp(str, "ircu"))
3850 nickserv_conf.handle_ts_mode = TS_IRCU;
3852 nickserv_conf.handle_ts_mode = TS_IGNORE;
3853 if (!nickserv_conf.disable_nicks) {
3854 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3855 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3856 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3857 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3858 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3859 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3860 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3861 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3863 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3864 for (it=dict_first(child); it; it=iter_next(it)) {
3865 const char *key = iter_key(it), *value;
3869 if (!strncasecmp(key, "uc_", 3))
3870 flag = toupper(key[3]);
3871 else if (!strncasecmp(key, "lc_", 3))
3872 flag = tolower(key[3]);
3876 if ((pos = handle_inverse_flags[flag])) {
3877 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3878 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3881 if (nickserv_conf.weak_password_dict)
3882 dict_delete(nickserv_conf.weak_password_dict);
3883 nickserv_conf.weak_password_dict = dict_new();
3884 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3885 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3886 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3887 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3889 nickserv_load_dict(str);
3890 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3891 if (nickserv && str)
3892 NickChange(nickserv, str, 0);
3893 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3894 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3895 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3896 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3897 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3898 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3899 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3900 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3901 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3902 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3903 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3904 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3905 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3906 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3907 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3908 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3909 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3910 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3911 str = conf_get_data("server/network", RECDB_QSTRING);
3912 nickserv_conf.network_name = str ? str : "some IRC network";
3913 if (!nickserv_conf.auth_policer_params) {
3914 nickserv_conf.auth_policer_params = policer_params_new();
3915 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3916 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3918 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3919 for (it=dict_first(child); it; it=iter_next(it))
3920 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3924 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3926 char newnick[NICKLEN+1];
3935 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3937 case RECLAIM_SVSNICK:
3939 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3940 } while (GetUserH(newnick));
3941 irc_svsnick(nickserv, user, newnick);
3944 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3945 DelUser(user, nickserv, 1, msg);
3951 nickserv_reclaim_p(void *data) {
3952 struct userNode *user = data;
3953 struct nick_info *ni = get_nick_info(user->nick);
3955 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3959 check_user_nick(struct userNode *user) {
3960 struct nick_info *ni;
3961 user->modes &= ~FLAGS_REGNICK;
3962 if (!(ni = get_nick_info(user->nick)))
3964 if (user->handle_info == ni->owner) {
3965 user->modes |= FLAGS_REGNICK;
3969 if (nickserv_conf.warn_nick_owned)
3970 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3971 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3973 if (nickserv_conf.auto_reclaim_delay)
3974 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3976 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3981 handle_new_user(struct userNode *user)
3983 return check_user_nick(user);
3987 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
3989 struct handle_info *hi = NULL;
3992 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3993 if ((hi == NULL) && (serial != 0)) {
3995 inttobase64(id, serial, IDLEN);
3996 hi = dict_find(nickserv_id_dict, id, NULL);
4000 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4001 && (timestamp != hi->registered)) {
4004 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4007 set_user_handle_info(user, hi, 0);
4009 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4014 handle_nick_change(struct userNode *user, const char *old_nick)
4016 struct handle_info *hi;
4018 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4019 dict_remove(nickserv_allow_auth_dict, old_nick);
4020 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4022 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4023 check_user_nick(user);
4027 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4029 dict_remove(nickserv_allow_auth_dict, user->nick);
4030 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4031 set_user_handle_info(user, NULL, 0);
4034 static struct modcmd *
4035 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4037 if (min_level > 0) {
4039 sprintf(buf, "%u", min_level);
4040 if (must_be_qualified) {
4041 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4043 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4045 } else if (min_level == 0) {
4046 if (must_be_qualified) {
4047 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4049 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4052 if (must_be_qualified) {
4053 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4055 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4061 nickserv_db_cleanup(void)
4063 unreg_del_user_func(nickserv_remove_user);
4064 userList_clean(&curr_helpers);
4065 policer_params_delete(nickserv_conf.auth_policer_params);
4066 dict_delete(nickserv_handle_dict);
4067 dict_delete(nickserv_nick_dict);
4068 dict_delete(nickserv_opt_dict);
4069 dict_delete(nickserv_allow_auth_dict);
4070 dict_delete(nickserv_email_dict);
4071 dict_delete(nickserv_id_dict);
4072 dict_delete(nickserv_conf.weak_password_dict);
4073 free(auth_func_list);
4074 free(unreg_func_list);
4076 free(allowauth_func_list);
4077 free(handle_merge_func_list);
4078 free(failpw_func_list);
4079 if (nickserv_conf.valid_handle_regex_set)
4080 regfree(&nickserv_conf.valid_handle_regex);
4081 if (nickserv_conf.valid_nick_regex_set)
4082 regfree(&nickserv_conf.valid_nick_regex);
4086 init_nickserv(const char *nick)
4089 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4090 reg_new_user_func(handle_new_user);
4091 reg_nick_change_func(handle_nick_change);
4092 reg_del_user_func(nickserv_remove_user);
4093 reg_account_func(handle_account);
4095 /* set up handle_inverse_flags */
4096 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4097 for (i=0; handle_flags[i]; i++) {
4098 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4099 flag_access_levels[i] = 0;
4102 conf_register_reload(nickserv_conf_read);
4103 nickserv_opt_dict = dict_new();
4104 nickserv_email_dict = dict_new();
4105 dict_set_free_keys(nickserv_email_dict, free);
4106 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4108 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4109 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4110 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4111 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4112 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4113 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4114 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4115 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4116 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4117 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4118 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4119 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4120 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4121 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4122 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4123 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4124 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4125 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4126 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4127 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4128 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4129 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4130 if (!nickserv_conf.disable_nicks) {
4131 /* nick management commands */
4132 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4133 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4134 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4135 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4136 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4137 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4139 if (nickserv_conf.email_enabled) {
4140 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4141 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4142 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4143 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4144 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4145 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4147 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4148 /* miscellaneous commands */
4149 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4150 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4151 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4152 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4153 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4154 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4156 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4157 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4158 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4159 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4160 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4161 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4162 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4163 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4164 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4165 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4166 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4167 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4168 if (nickserv_conf.titlehost_suffix) {
4169 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4170 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4172 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4173 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4174 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4175 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4177 nickserv_handle_dict = dict_new();
4178 dict_set_free_keys(nickserv_handle_dict, free);
4179 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4181 nickserv_id_dict = dict_new();
4182 dict_set_free_keys(nickserv_id_dict, free);
4184 nickserv_nick_dict = dict_new();
4185 dict_set_free_data(nickserv_nick_dict, free);
4187 nickserv_allow_auth_dict = dict_new();
4189 userList_init(&curr_helpers);
4192 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4193 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4194 nickserv_service = service_register(nickserv);
4196 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4197 reg_exit_func(nickserv_db_cleanup);
4198 if(nickserv_conf.handle_expire_frequency)
4199 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4200 message_register_table(msgtab);