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) && !user->dead; 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 (ii = 0; ii < schannels->used; ++ii)
2175 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2177 if (ii == schannels->used)
2178 HANDLE_CLEAR_FLAG(hi, HELPING);
2181 if (after && !before) {
2182 /* Add user to current helper list. */
2183 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2184 userList_append(&curr_helpers, uNode);
2185 } else if (!after && before) {
2186 /* Remove user from current helper list. */
2187 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2188 userList_remove(&curr_helpers, uNode);
2195 set_list(struct userNode *user, struct handle_info *hi, int override)
2199 char *set_display[] = {
2200 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2201 "EMAIL", "MAXLOGINS", "LANGUAGE"
2204 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2206 /* Do this so options are presented in a consistent order. */
2207 for (i = 0; i < ArrayLength(set_display); ++i)
2208 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2209 opt(user, hi, override, 0, NULL);
2212 static NICKSERV_FUNC(cmd_set)
2214 struct handle_info *hi;
2217 hi = user->handle_info;
2219 set_list(user, hi, 0);
2222 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2223 reply("NSMSG_INVALID_OPTION", argv[1]);
2226 return opt(user, hi, 0, argc-1, argv+1);
2229 static NICKSERV_FUNC(cmd_oset)
2231 struct handle_info *hi;
2232 struct svccmd *subcmd;
2234 char cmdname[MAXLEN];
2236 NICKSERV_MIN_PARMS(2);
2238 if (!(hi = get_victim_oper(user, argv[1])))
2242 set_list(user, hi, 0);
2246 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2247 reply("NSMSG_INVALID_OPTION", argv[2]);
2251 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2252 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2253 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2256 return opt(user, hi, 1, argc-2, argv+2);
2259 static OPTION_FUNC(opt_info)
2263 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2265 hi->infoline = NULL;
2267 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2271 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2272 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2276 static OPTION_FUNC(opt_width)
2279 hi->screen_width = strtoul(argv[1], NULL, 0);
2281 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2282 hi->screen_width = MIN_LINE_SIZE;
2283 else if (hi->screen_width > MAX_LINE_SIZE)
2284 hi->screen_width = MAX_LINE_SIZE;
2286 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2290 static OPTION_FUNC(opt_tablewidth)
2293 hi->table_width = strtoul(argv[1], NULL, 0);
2295 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2296 hi->table_width = MIN_LINE_SIZE;
2297 else if (hi->screen_width > MAX_LINE_SIZE)
2298 hi->table_width = MAX_LINE_SIZE;
2300 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2304 static OPTION_FUNC(opt_color)
2307 if (enabled_string(argv[1]))
2308 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2309 else if (disabled_string(argv[1]))
2310 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2312 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2317 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2321 static OPTION_FUNC(opt_privmsg)
2324 if (enabled_string(argv[1]))
2325 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2326 else if (disabled_string(argv[1]))
2327 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2329 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2334 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2338 static OPTION_FUNC(opt_style)
2343 if (!irccasecmp(argv[1], "Zoot"))
2344 hi->userlist_style = HI_STYLE_ZOOT;
2345 else if (!irccasecmp(argv[1], "def"))
2346 hi->userlist_style = HI_STYLE_DEF;
2349 switch (hi->userlist_style) {
2358 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2362 static OPTION_FUNC(opt_password)
2365 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2370 cryptpass(argv[1], hi->passwd);
2372 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2376 static OPTION_FUNC(opt_flags)
2379 unsigned int ii, flen;
2382 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2387 nickserv_apply_flags(user, hi, argv[1]);
2389 for (ii = flen = 0; handle_flags[ii]; ii++)
2390 if (hi->flags & (1 << ii))
2391 flags[flen++] = handle_flags[ii];
2394 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2396 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2400 static OPTION_FUNC(opt_email)
2404 if (!is_valid_email_addr(argv[1])) {
2405 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2408 if ((str = mail_prohibited_address(argv[1]))) {
2409 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2412 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2413 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2415 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2417 nickserv_set_email_addr(hi, argv[1]);
2419 nickserv_eat_cookie(hi->cookie);
2420 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2423 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2427 static OPTION_FUNC(opt_maxlogins)
2429 unsigned char maxlogins;
2431 maxlogins = strtoul(argv[1], NULL, 0);
2432 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2433 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2436 hi->maxlogins = maxlogins;
2438 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2439 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2443 static OPTION_FUNC(opt_language)
2445 struct language *lang;
2447 lang = language_find(argv[1]);
2448 if (irccasecmp(lang->name, argv[1]))
2449 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2450 hi->language = lang;
2452 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2456 static OPTION_FUNC(opt_karma)
2459 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2464 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2465 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2466 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2467 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2469 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2473 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2478 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2479 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2481 if ((user->handle_info->opserv_level < target->opserv_level)
2482 || ((user->handle_info->opserv_level == target->opserv_level)
2483 && (user->handle_info->opserv_level < 1000))) {
2484 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2487 if ((user->handle_info->opserv_level < new_level)
2488 || ((user->handle_info->opserv_level == new_level)
2489 && (user->handle_info->opserv_level < 1000))) {
2490 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2493 if (user->handle_info == target) {
2494 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2497 if (target->opserv_level == new_level)
2499 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2500 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2501 target->opserv_level = new_level;
2505 static OPTION_FUNC(opt_level)
2510 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2514 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2515 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2519 static OPTION_FUNC(opt_epithet)
2522 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2526 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2527 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2530 if ((epithet[0] == '*') && !epithet[1])
2533 hi->epithet = strdup(epithet);
2537 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2539 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2543 static OPTION_FUNC(opt_title)
2548 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2552 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2554 if (strchr(title, '.')) {
2555 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2558 if ((strlen(user->handle_info->handle) + strlen(title) +
2559 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2560 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2565 if (!strcmp(title, "*")) {
2566 hi->fakehost = NULL;
2568 hi->fakehost = malloc(strlen(title)+2);
2569 hi->fakehost[0] = '.';
2570 strcpy(hi->fakehost+1, title);
2573 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2574 title = hi->fakehost + 1;
2578 title = user_find_message(user, "MSG_NONE");
2579 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2583 static OPTION_FUNC(opt_fakehost)
2588 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2592 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2594 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2595 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2599 if (!strcmp(fake, "*"))
2600 hi->fakehost = NULL;
2602 hi->fakehost = strdup(fake);
2603 fake = hi->fakehost;
2606 fake = generate_fakehost(hi);
2608 fake = user_find_message(user, "MSG_NONE");
2609 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2613 static NICKSERV_FUNC(cmd_reclaim)
2615 struct handle_info *hi;
2616 struct nick_info *ni;
2617 struct userNode *victim;
2619 NICKSERV_MIN_PARMS(2);
2620 hi = user->handle_info;
2621 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2623 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2626 if (ni->owner != user->handle_info) {
2627 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2630 victim = GetUserH(ni->nick);
2632 reply("MSG_NICK_UNKNOWN", ni->nick);
2635 if (victim == user) {
2636 reply("NSMSG_NICK_USER_YOU");
2639 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2640 switch (nickserv_conf.reclaim_action) {
2641 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2642 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2643 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2644 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2649 static NICKSERV_FUNC(cmd_unregnick)
2652 struct handle_info *hi;
2653 struct nick_info *ni;
2655 hi = user->handle_info;
2656 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2657 ni = dict_find(nickserv_nick_dict, nick, NULL);
2659 reply("NSMSG_UNKNOWN_NICK", nick);
2662 if (hi != ni->owner) {
2663 reply("NSMSG_NOT_YOUR_NICK", nick);
2666 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2671 static NICKSERV_FUNC(cmd_ounregnick)
2673 struct nick_info *ni;
2675 NICKSERV_MIN_PARMS(2);
2676 if (!(ni = get_nick_info(argv[1]))) {
2677 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2680 if (!oper_outranks(user, ni->owner))
2682 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2687 static NICKSERV_FUNC(cmd_unregister)
2689 struct handle_info *hi;
2692 NICKSERV_MIN_PARMS(2);
2693 hi = user->handle_info;
2696 if (checkpass(passwd, hi->passwd)) {
2697 nickserv_unregister_handle(hi, user);
2700 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2701 reply("NSMSG_PASSWORD_INVALID");
2706 static NICKSERV_FUNC(cmd_ounregister)
2708 struct handle_info *hi;
2709 char reason[MAXLEN];
2712 NICKSERV_MIN_PARMS(2);
2713 if (!(hi = get_victim_oper(user, argv[1])))
2716 if (HANDLE_FLAGGED(hi, NODELETE)) {
2717 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
2721 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
2723 ((hi->flags & nickserv_conf.ounregister_flags)
2725 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
2726 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
2730 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2731 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2732 nickserv_unregister_handle(hi, user);
2736 static NICKSERV_FUNC(cmd_status)
2738 if (nickserv_conf.disable_nicks) {
2739 reply("NSMSG_GLOBAL_STATS_NONICK",
2740 dict_size(nickserv_handle_dict));
2742 if (user->handle_info) {
2744 struct nick_info *ni;
2745 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2746 reply("NSMSG_HANDLE_STATS", cnt);
2748 reply("NSMSG_HANDLE_NONE");
2750 reply("NSMSG_GLOBAL_STATS",
2751 dict_size(nickserv_handle_dict),
2752 dict_size(nickserv_nick_dict));
2757 static NICKSERV_FUNC(cmd_ghost)
2759 struct userNode *target;
2760 char reason[MAXLEN];
2762 NICKSERV_MIN_PARMS(2);
2763 if (!(target = GetUserH(argv[1]))) {
2764 reply("MSG_NICK_UNKNOWN", argv[1]);
2767 if (target == user) {
2768 reply("NSMSG_CANNOT_GHOST_SELF");
2771 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2772 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2775 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2776 DelUser(target, nickserv, 1, reason);
2777 reply("NSMSG_GHOST_KILLED", argv[1]);
2781 static NICKSERV_FUNC(cmd_vacation)
2783 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2784 reply("NSMSG_ON_VACATION");
2788 static NICKSERV_FUNC(cmd_addnote)
2790 struct handle_info *hi;
2791 unsigned long duration;
2794 struct handle_note *prev;
2795 struct handle_note *note;
2797 /* Parse parameters and figure out values for note's fields. */
2798 NICKSERV_MIN_PARMS(4);
2799 hi = get_victim_oper(user, argv[1]);
2802 if(!strcmp(argv[2], "0"))
2804 else if(!(duration = ParseInterval(argv[2])))
2806 reply("MSG_INVALID_DURATION", argv[2]);
2809 if (duration > 2*365*86400) {
2810 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2813 unsplit_string(argv + 3, argc - 3, text);
2814 WALK_NOTES(hi, prev, note) {}
2815 id = prev ? (prev->id + 1) : 1;
2817 /* Create the new note structure. */
2818 note = calloc(1, sizeof(*note) + strlen(text));
2820 note->expires = duration ? (now + duration) : 0;
2823 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2824 strcpy(note->note, text);
2829 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2833 static NICKSERV_FUNC(cmd_delnote)
2835 struct handle_info *hi;
2836 struct handle_note *prev;
2837 struct handle_note *note;
2840 NICKSERV_MIN_PARMS(3);
2841 hi = get_victim_oper(user, argv[1]);
2844 id = strtoul(argv[2], NULL, 10);
2845 WALK_NOTES(hi, prev, note) {
2846 if (id == note->id) {
2848 prev->next = note->next;
2850 hi->notes = note->next;
2852 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2856 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2861 nickserv_saxdb_write(struct saxdb_context *ctx) {
2863 struct handle_info *hi;
2866 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2868 assert(hi->id != 0);
2869 saxdb_start_record(ctx, iter_key(it), 0);
2871 struct handle_cookie *cookie = hi->cookie;
2874 switch (cookie->type) {
2875 case ACTIVATION: type = KEY_ACTIVATION; break;
2876 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2877 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2878 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2879 default: type = NULL; break;
2882 saxdb_start_record(ctx, KEY_COOKIE, 0);
2883 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2884 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2886 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2887 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2888 saxdb_end_record(ctx);
2892 struct handle_note *prev, *note;
2893 saxdb_start_record(ctx, KEY_NOTES, 0);
2894 WALK_NOTES(hi, prev, note) {
2895 snprintf(flags, sizeof(flags), "%d", note->id);
2896 saxdb_start_record(ctx, flags, 0);
2898 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
2899 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
2900 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
2901 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
2902 saxdb_end_record(ctx);
2904 saxdb_end_record(ctx);
2907 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2909 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2911 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2915 for (ii=flen=0; handle_flags[ii]; ++ii)
2916 if (hi->flags & (1 << ii))
2917 flags[flen++] = handle_flags[ii];
2919 saxdb_write_string(ctx, KEY_FLAGS, flags);
2921 saxdb_write_int(ctx, KEY_ID, hi->id);
2923 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2924 if (hi->last_quit_host[0])
2925 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2926 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2928 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
2929 if (hi->masks->used)
2930 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2932 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2934 struct string_list *slist;
2935 struct nick_info *ni;
2937 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2938 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2939 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2943 if (hi->opserv_level)
2944 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2945 if (hi->language != lang_C)
2946 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2947 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2948 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2949 if (hi->screen_width)
2950 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2951 if (hi->table_width)
2952 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2953 flags[0] = hi->userlist_style;
2955 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2956 saxdb_end_record(ctx);
2961 static handle_merge_func_t *handle_merge_func_list;
2962 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2965 reg_handle_merge_func(handle_merge_func_t func)
2967 if (handle_merge_func_used == handle_merge_func_size) {
2968 if (handle_merge_func_size) {
2969 handle_merge_func_size <<= 1;
2970 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2972 handle_merge_func_size = 8;
2973 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2976 handle_merge_func_list[handle_merge_func_used++] = func;
2979 static NICKSERV_FUNC(cmd_merge)
2981 struct handle_info *hi_from, *hi_to;
2982 struct userNode *last_user;
2983 struct userData *cList, *cListNext;
2984 unsigned int ii, jj, n;
2985 char buffer[MAXLEN];
2987 NICKSERV_MIN_PARMS(3);
2989 if (!(hi_from = get_victim_oper(user, argv[1])))
2991 if (!(hi_to = get_victim_oper(user, argv[2])))
2993 if (hi_to == hi_from) {
2994 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2998 for (n=0; n<handle_merge_func_used; n++)
2999 handle_merge_func_list[n](user, hi_to, hi_from);
3001 /* Append "from" handle's nicks to "to" handle's nick list. */
3003 struct nick_info *last_ni;
3004 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3005 last_ni->next = hi_from->nicks;
3007 while (hi_from->nicks) {
3008 hi_from->nicks->owner = hi_to;
3009 hi_from->nicks = hi_from->nicks->next;
3012 /* Merge the hostmasks. */
3013 for (ii=0; ii<hi_from->masks->used; ii++) {
3014 char *mask = hi_from->masks->list[ii];
3015 for (jj=0; jj<hi_to->masks->used; jj++)
3016 if (match_ircglobs(hi_to->masks->list[jj], mask))
3018 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3019 string_list_append(hi_to->masks, strdup(mask));
3022 /* Merge the lists of authed users. */
3024 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3025 last_user->next_authed = hi_from->users;
3027 hi_to->users = hi_from->users;
3029 /* Repoint the old "from" handle's users. */
3030 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3031 last_user->handle_info = hi_to;
3033 hi_from->users = NULL;
3035 /* Merge channel userlists. */
3036 for (cList=hi_from->channels; cList; cList=cListNext) {
3037 struct userData *cList2;
3038 cListNext = cList->u_next;
3039 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3040 if (cList->channel == cList2->channel)
3042 if (cList2 && (cList2->access >= cList->access)) {
3043 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);
3044 /* keep cList2 in hi_to; remove cList from hi_from */
3045 del_channel_user(cList, 1);
3048 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);
3049 /* remove the lower-ranking cList2 from hi_to */
3050 del_channel_user(cList2, 1);
3052 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3054 /* cList needs to be moved from hi_from to hi_to */
3055 cList->handle = hi_to;
3056 /* Remove from linked list for hi_from */
3057 assert(!cList->u_prev);
3058 hi_from->channels = cList->u_next;
3060 cList->u_next->u_prev = cList->u_prev;
3061 /* Add to linked list for hi_to */
3062 cList->u_prev = NULL;
3063 cList->u_next = hi_to->channels;
3064 if (hi_to->channels)
3065 hi_to->channels->u_prev = cList;
3066 hi_to->channels = cList;
3070 /* Do they get an OpServ level promotion? */
3071 if (hi_from->opserv_level > hi_to->opserv_level)
3072 hi_to->opserv_level = hi_from->opserv_level;
3074 /* What about last seen time? */
3075 if (hi_from->lastseen > hi_to->lastseen)
3076 hi_to->lastseen = hi_from->lastseen;
3078 /* New karma is the sum of the two original karmas. */
3079 hi_to->karma += hi_from->karma;
3081 /* Does a fakehost carry over? (This intentionally doesn't set it
3082 * for users previously attached to hi_to. They'll just have to
3085 if (hi_from->fakehost && !hi_to->fakehost)
3086 hi_to->fakehost = strdup(hi_from->fakehost);
3088 /* Notify of success. */
3089 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3090 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3091 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3093 /* Unregister the "from" handle. */
3094 nickserv_unregister_handle(hi_from, NULL);
3099 struct nickserv_discrim {
3100 unsigned long flags_on, flags_off;
3101 unsigned long min_registered, max_registered;
3102 unsigned long lastseen;
3104 int min_level, max_level;
3105 int min_karma, max_karma;
3106 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3107 const char *nickmask;
3108 const char *hostmask;
3109 const char *fakehostmask;
3110 const char *handlemask;
3111 const char *emailmask;
3114 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3116 struct discrim_apply_info {
3117 struct nickserv_discrim *discrim;
3118 discrim_search_func func;
3119 struct userNode *source;
3120 unsigned int matched;
3123 static struct nickserv_discrim *
3124 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3127 struct nickserv_discrim *discrim;
3129 discrim = malloc(sizeof(*discrim));
3130 memset(discrim, 0, sizeof(*discrim));
3131 discrim->min_level = 0;
3132 discrim->max_level = INT_MAX;
3133 discrim->limit = 50;
3134 discrim->min_registered = 0;
3135 discrim->max_registered = ULONG_MAX;
3136 discrim->lastseen = ULONG_MAX;
3137 discrim->min_karma = INT_MIN;
3138 discrim->max_karma = INT_MAX;
3140 for (i=0; i<argc; i++) {
3141 if (i == argc - 1) {
3142 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3145 if (!irccasecmp(argv[i], "limit")) {
3146 discrim->limit = strtoul(argv[++i], NULL, 0);
3147 } else if (!irccasecmp(argv[i], "flags")) {
3148 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3149 } else if (!irccasecmp(argv[i], "registered")) {
3150 const char *cmp = argv[++i];
3151 if (cmp[0] == '<') {
3152 if (cmp[1] == '=') {
3153 discrim->min_registered = now - ParseInterval(cmp+2);
3155 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3157 } else if (cmp[0] == '=') {
3158 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3159 } else if (cmp[0] == '>') {
3160 if (cmp[1] == '=') {
3161 discrim->max_registered = now - ParseInterval(cmp+2);
3163 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3166 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3168 } else if (!irccasecmp(argv[i], "seen")) {
3169 discrim->lastseen = now - ParseInterval(argv[++i]);
3170 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3171 discrim->nickmask = argv[++i];
3172 } else if (!irccasecmp(argv[i], "hostmask")) {
3174 if (!irccasecmp(argv[i], "exact")) {
3175 if (i == argc - 1) {
3176 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3179 discrim->hostmask_type = EXACT;
3180 } else if (!irccasecmp(argv[i], "subset")) {
3181 if (i == argc - 1) {
3182 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3185 discrim->hostmask_type = SUBSET;
3186 } else if (!irccasecmp(argv[i], "superset")) {
3187 if (i == argc - 1) {
3188 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3191 discrim->hostmask_type = SUPERSET;
3192 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3193 if (i == argc - 1) {
3194 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3197 discrim->hostmask_type = LASTQUIT;
3200 discrim->hostmask_type = SUPERSET;
3202 discrim->hostmask = argv[++i];
3203 } else if (!irccasecmp(argv[i], "fakehost")) {
3204 if (!irccasecmp(argv[++i], "*")) {
3205 discrim->fakehostmask = 0;
3207 discrim->fakehostmask = argv[i];
3209 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3210 if (!irccasecmp(argv[++i], "*")) {
3211 discrim->handlemask = 0;
3213 discrim->handlemask = argv[i];
3215 } else if (!irccasecmp(argv[i], "email")) {
3216 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3217 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3219 } else if (!irccasecmp(argv[++i], "*")) {
3220 discrim->emailmask = 0;
3222 discrim->emailmask = argv[i];
3224 } else if (!irccasecmp(argv[i], "access")) {
3225 const char *cmp = argv[++i];
3226 if (cmp[0] == '<') {
3227 if (discrim->min_level == 0) discrim->min_level = 1;
3228 if (cmp[1] == '=') {
3229 discrim->max_level = strtoul(cmp+2, NULL, 0);
3231 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3233 } else if (cmp[0] == '=') {
3234 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3235 } else if (cmp[0] == '>') {
3236 if (cmp[1] == '=') {
3237 discrim->min_level = strtoul(cmp+2, NULL, 0);
3239 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3242 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3244 } else if (!irccasecmp(argv[i], "karma")) {
3245 const char *cmp = argv[++i];
3246 if (cmp[0] == '<') {
3247 if (cmp[1] == '=') {
3248 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3250 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3252 } else if (cmp[0] == '=') {
3253 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3254 } else if (cmp[0] == '>') {
3255 if (cmp[1] == '=') {
3256 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3258 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3261 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3264 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3275 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3277 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3278 || (discrim->flags_off & hi->flags)
3279 || (discrim->min_registered > hi->registered)
3280 || (discrim->max_registered < hi->registered)
3281 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3282 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3283 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3284 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3285 || (discrim->min_level > hi->opserv_level)
3286 || (discrim->max_level < hi->opserv_level)
3287 || (discrim->min_karma > hi->karma)
3288 || (discrim->max_karma < hi->karma)
3292 if (discrim->hostmask) {
3294 for (i=0; i<hi->masks->used; i++) {
3295 const char *mask = hi->masks->list[i];
3296 if ((discrim->hostmask_type == SUBSET)
3297 && (match_ircglobs(discrim->hostmask, mask))) break;
3298 else if ((discrim->hostmask_type == EXACT)
3299 && !irccasecmp(discrim->hostmask, mask)) break;
3300 else if ((discrim->hostmask_type == SUPERSET)
3301 && (match_ircglobs(mask, discrim->hostmask))) break;
3302 else if ((discrim->hostmask_type == LASTQUIT)
3303 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3305 if (i==hi->masks->used) return 0;
3307 if (discrim->nickmask) {
3308 struct nick_info *nick = hi->nicks;
3310 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3313 if (!nick) return 0;
3319 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3321 dict_iterator_t it, next;
3322 unsigned int matched;
3324 for (it = dict_first(nickserv_handle_dict), matched = 0;
3325 it && (matched < discrim->limit);
3327 next = iter_next(it);
3328 if (nickserv_discrim_match(discrim, iter_data(it))) {
3329 dsf(source, iter_data(it));
3337 search_print_func(struct userNode *source, struct handle_info *match)
3339 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3343 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3348 search_unregister_func (struct userNode *source, struct handle_info *match)
3350 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3351 nickserv_unregister_handle(match, source);
3355 nickserv_sort_accounts_by_access(const void *a, const void *b)
3357 const struct handle_info *hi_a = *(const struct handle_info**)a;
3358 const struct handle_info *hi_b = *(const struct handle_info**)b;
3359 if (hi_a->opserv_level != hi_b->opserv_level)
3360 return hi_b->opserv_level - hi_a->opserv_level;
3361 return irccasecmp(hi_a->handle, hi_b->handle);
3365 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3367 struct handle_info_list hil;
3368 struct helpfile_table tbl;
3373 memset(&hil, 0, sizeof(hil));
3374 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3375 struct handle_info *hi = iter_data(it);
3376 if (hi->opserv_level)
3377 handle_info_list_append(&hil, hi);
3379 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3380 tbl.length = hil.used + 1;
3382 tbl.flags = TABLE_NO_FREE;
3383 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3384 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3387 for (ii = 0; ii < hil.used; ) {
3388 ary = malloc(tbl.width * sizeof(ary[0]));
3389 ary[0] = hil.list[ii]->handle;
3390 ary[1] = strtab(hil.list[ii]->opserv_level);
3391 tbl.contents[++ii] = ary;
3393 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3394 reply("MSG_MATCH_COUNT", hil.used);
3395 for (ii = 0; ii < hil.used; ii++)
3396 free(tbl.contents[ii]);
3401 static NICKSERV_FUNC(cmd_search)
3403 struct nickserv_discrim *discrim;
3404 discrim_search_func action;
3405 struct svccmd *subcmd;
3406 unsigned int matches;
3409 NICKSERV_MIN_PARMS(3);
3410 sprintf(buf, "search %s", argv[1]);
3411 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3412 if (!irccasecmp(argv[1], "print"))
3413 action = search_print_func;
3414 else if (!irccasecmp(argv[1], "count"))
3415 action = search_count_func;
3416 else if (!irccasecmp(argv[1], "unregister"))
3417 action = search_unregister_func;
3419 reply("NSMSG_INVALID_ACTION", argv[1]);
3423 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3426 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3430 if (action == search_print_func)
3431 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3432 else if (action == search_count_func)
3433 discrim->limit = INT_MAX;
3435 matches = nickserv_discrim_search(discrim, action, user);
3438 reply("MSG_MATCH_COUNT", matches);
3440 reply("MSG_NO_MATCHES");
3446 static MODCMD_FUNC(cmd_checkpass)
3448 struct handle_info *hi;
3450 NICKSERV_MIN_PARMS(3);
3451 if (!(hi = get_handle_info(argv[1]))) {
3452 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3455 if (checkpass(argv[2], hi->passwd))
3456 reply("CHECKPASS_YES");
3458 reply("CHECKPASS_NO");
3463 static MODCMD_FUNC(cmd_checkemail)
3465 struct handle_info *hi;
3467 NICKSERV_MIN_PARMS(3);
3468 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3469 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3472 if (!hi->email_addr)
3473 reply("CHECKEMAIL_NOT_SET");
3474 else if (!irccasecmp(argv[2], hi->email_addr))
3475 reply("CHECKEMAIL_YES");
3477 reply("CHECKEMAIL_NO");
3483 nickserv_db_read_handle(const char *handle, dict_t obj)
3486 struct string_list *masks, *slist;
3487 struct handle_info *hi;
3488 struct userNode *authed_users;
3489 struct userData *channels;
3494 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3495 id = str ? strtoul(str, NULL, 0) : 0;
3496 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3498 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3501 if ((hi = get_handle_info(handle))) {
3502 authed_users = hi->users;
3503 channels = hi->channels;
3505 hi->channels = NULL;
3506 dict_remove(nickserv_handle_dict, hi->handle);
3508 authed_users = NULL;
3511 hi = register_handle(handle, str, id);
3513 hi->users = authed_users;
3514 while (authed_users) {
3515 authed_users->handle_info = hi;
3516 authed_users = authed_users->next_authed;
3519 hi->channels = channels;
3520 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3521 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3522 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3523 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3524 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3525 hi->language = language_find(str ? str : "C");
3526 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3527 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3528 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3530 hi->infoline = strdup(str);
3531 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3532 hi->registered = str ? strtoul(str, NULL, 0) : now;
3533 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3534 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3535 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3536 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3537 /* We want to read the nicks even if disable_nicks is set. This is so
3538 * that we don't lose the nick data entirely. */
3539 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3541 for (ii=0; ii<slist->used; ii++)
3542 register_nick(slist->list[ii], hi);
3544 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3546 for (ii=0; str[ii]; ii++)
3547 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3549 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3550 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3551 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3552 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3553 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3554 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3555 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3557 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3559 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3560 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3562 nickserv_set_email_addr(hi, str);
3563 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3565 hi->epithet = strdup(str);
3566 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3568 hi->fakehost = strdup(str);
3569 /* Read the "cookie" sub-database (if it exists). */
3570 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3572 const char *data, *type, *expires, *cookie_str;
3573 struct handle_cookie *cookie;
3575 cookie = calloc(1, sizeof(*cookie));
3576 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3577 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3578 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3579 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3580 if (!type || !expires || !cookie_str) {
3581 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3584 if (!irccasecmp(type, KEY_ACTIVATION))
3585 cookie->type = ACTIVATION;
3586 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3587 cookie->type = PASSWORD_CHANGE;
3588 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3589 cookie->type = EMAIL_CHANGE;
3590 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3591 cookie->type = ALLOWAUTH;
3593 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3596 cookie->expires = strtoul(expires, NULL, 0);
3597 if (cookie->expires < now)
3600 cookie->data = strdup(data);
3601 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3605 nickserv_bake_cookie(cookie);
3607 nickserv_free_cookie(cookie);
3609 /* Read the "notes" sub-database (if it exists). */
3610 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3613 struct handle_note *last_note;
3614 struct handle_note *note;
3617 for (it = dict_first(subdb); it; it = iter_next(it)) {
3618 const char *expires;
3626 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3628 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", id, hi->handle);
3631 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3632 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3633 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3634 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3635 if (!setter || !text || !set) {
3636 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", id, hi->handle);
3639 note = calloc(1, sizeof(*note) + strlen(text));
3641 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3642 note->set = strtoul(set, NULL, 10);
3643 note->id = strtoul(id, NULL, 10);
3644 safestrncpy(note->setter, setter, sizeof(note->setter));
3645 strcpy(note->note, text);
3647 last_note->next = note;
3656 nickserv_saxdb_read(dict_t db) {
3658 struct record_data *rd;
3660 for (it=dict_first(db); it; it=iter_next(it)) {
3662 nickserv_db_read_handle(iter_key(it), rd->d.object);
3667 static NICKSERV_FUNC(cmd_mergedb)
3669 struct timeval start, stop;
3672 NICKSERV_MIN_PARMS(2);
3673 gettimeofday(&start, NULL);
3674 if (!(db = parse_database(argv[1]))) {
3675 reply("NSMSG_DB_UNREADABLE", argv[1]);
3678 nickserv_saxdb_read(db);
3680 gettimeofday(&stop, NULL);
3681 stop.tv_sec -= start.tv_sec;
3682 stop.tv_usec -= start.tv_usec;
3683 if (stop.tv_usec < 0) {
3685 stop.tv_usec += 1000000;
3687 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
3692 expire_handles(UNUSED_ARG(void *data))
3694 dict_iterator_t it, next;
3695 unsigned long expiry;
3696 struct handle_info *hi;
3698 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3699 next = iter_next(it);
3701 if ((hi->opserv_level > 0)
3703 || HANDLE_FLAGGED(hi, FROZEN)
3704 || HANDLE_FLAGGED(hi, NODELETE)) {
3707 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3708 if ((now - hi->lastseen) > expiry) {
3709 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3710 nickserv_unregister_handle(hi, NULL);
3714 if (nickserv_conf.handle_expire_frequency)
3715 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3719 nickserv_load_dict(const char *fname)
3723 if (!(file = fopen(fname, "r"))) {
3724 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3727 while (fgets(line, sizeof(line), file)) {
3730 if (line[strlen(line)-1] == '\n')
3731 line[strlen(line)-1] = 0;
3732 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3735 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3738 static enum reclaim_action
3739 reclaim_action_from_string(const char *str) {
3741 return RECLAIM_NONE;
3742 else if (!irccasecmp(str, "warn"))
3743 return RECLAIM_WARN;
3744 else if (!irccasecmp(str, "svsnick"))
3745 return RECLAIM_SVSNICK;
3746 else if (!irccasecmp(str, "kill"))
3747 return RECLAIM_KILL;
3749 return RECLAIM_NONE;
3753 nickserv_conf_read(void)
3755 dict_t conf_node, child;
3759 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3760 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3763 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3765 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3766 if (nickserv_conf.valid_handle_regex_set)
3767 regfree(&nickserv_conf.valid_handle_regex);
3769 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3770 nickserv_conf.valid_handle_regex_set = !err;
3771 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3773 nickserv_conf.valid_handle_regex_set = 0;
3775 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3776 if (nickserv_conf.valid_nick_regex_set)
3777 regfree(&nickserv_conf.valid_nick_regex);
3779 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3780 nickserv_conf.valid_nick_regex_set = !err;
3781 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3783 nickserv_conf.valid_nick_regex_set = 0;
3785 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3787 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3788 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3789 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3790 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3791 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3792 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3793 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3794 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3795 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3796 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3797 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3798 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3799 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3800 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3801 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3802 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3803 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3804 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3805 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3806 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3807 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3808 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3809 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3810 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3811 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3813 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3814 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3815 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3817 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3818 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3819 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3821 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3822 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3823 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3824 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3825 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3826 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3827 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3828 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3829 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
3830 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
3831 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
3834 nickserv_conf.ounregister_flags = 0;
3836 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
3839 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
3841 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
3843 nickserv_conf.handle_ts_mode = TS_IGNORE;
3844 else if (!irccasecmp(str, "ircu"))
3845 nickserv_conf.handle_ts_mode = TS_IRCU;
3847 nickserv_conf.handle_ts_mode = TS_IGNORE;
3848 if (!nickserv_conf.disable_nicks) {
3849 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3850 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3851 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3852 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3853 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3854 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3855 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3856 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3858 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3859 for (it=dict_first(child); it; it=iter_next(it)) {
3860 const char *key = iter_key(it), *value;
3864 if (!strncasecmp(key, "uc_", 3))
3865 flag = toupper(key[3]);
3866 else if (!strncasecmp(key, "lc_", 3))
3867 flag = tolower(key[3]);
3871 if ((pos = handle_inverse_flags[flag])) {
3872 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3873 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3876 if (nickserv_conf.weak_password_dict)
3877 dict_delete(nickserv_conf.weak_password_dict);
3878 nickserv_conf.weak_password_dict = dict_new();
3879 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3880 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3881 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3882 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3884 nickserv_load_dict(str);
3885 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3886 if (nickserv && str)
3887 NickChange(nickserv, str, 0);
3888 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3889 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3890 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3891 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3892 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3893 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3894 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3895 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3896 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3897 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3898 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3899 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3900 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3901 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3902 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3903 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3904 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3905 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3906 str = conf_get_data("server/network", RECDB_QSTRING);
3907 nickserv_conf.network_name = str ? str : "some IRC network";
3908 if (!nickserv_conf.auth_policer_params) {
3909 nickserv_conf.auth_policer_params = policer_params_new();
3910 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3911 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3913 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3914 for (it=dict_first(child); it; it=iter_next(it))
3915 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3919 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3921 char newnick[NICKLEN+1];
3930 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3932 case RECLAIM_SVSNICK:
3934 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3935 } while (GetUserH(newnick));
3936 irc_svsnick(nickserv, user, newnick);
3939 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3940 DelUser(user, nickserv, 1, msg);
3946 nickserv_reclaim_p(void *data) {
3947 struct userNode *user = data;
3948 struct nick_info *ni = get_nick_info(user->nick);
3950 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3954 check_user_nick(struct userNode *user) {
3955 struct nick_info *ni;
3956 user->modes &= ~FLAGS_REGNICK;
3957 if (!(ni = get_nick_info(user->nick)))
3959 if (user->handle_info == ni->owner) {
3960 user->modes |= FLAGS_REGNICK;
3964 if (nickserv_conf.warn_nick_owned)
3965 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3966 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3968 if (nickserv_conf.auto_reclaim_delay)
3969 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3971 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3975 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
3977 struct handle_info *hi = NULL;
3980 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3981 if ((hi == NULL) && (serial != 0)) {
3983 inttobase64(id, serial, IDLEN);
3984 hi = dict_find(nickserv_id_dict, id, NULL);
3988 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
3989 && (timestamp != hi->registered)) {
3992 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3995 set_user_handle_info(user, hi, 0);
3997 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4002 handle_nick_change(struct userNode *user, const char *old_nick)
4004 struct handle_info *hi;
4006 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4007 dict_remove(nickserv_allow_auth_dict, old_nick);
4008 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4010 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4011 check_user_nick(user);
4015 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4017 dict_remove(nickserv_allow_auth_dict, user->nick);
4018 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4019 set_user_handle_info(user, NULL, 0);
4022 static struct modcmd *
4023 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4025 if (min_level > 0) {
4027 sprintf(buf, "%u", min_level);
4028 if (must_be_qualified) {
4029 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4031 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4033 } else if (min_level == 0) {
4034 if (must_be_qualified) {
4035 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4037 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4040 if (must_be_qualified) {
4041 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4043 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4049 nickserv_db_cleanup(void)
4051 unreg_del_user_func(nickserv_remove_user);
4052 userList_clean(&curr_helpers);
4053 policer_params_delete(nickserv_conf.auth_policer_params);
4054 dict_delete(nickserv_handle_dict);
4055 dict_delete(nickserv_nick_dict);
4056 dict_delete(nickserv_opt_dict);
4057 dict_delete(nickserv_allow_auth_dict);
4058 dict_delete(nickserv_email_dict);
4059 dict_delete(nickserv_id_dict);
4060 dict_delete(nickserv_conf.weak_password_dict);
4061 free(auth_func_list);
4062 free(unreg_func_list);
4064 free(allowauth_func_list);
4065 free(handle_merge_func_list);
4066 free(failpw_func_list);
4067 if (nickserv_conf.valid_handle_regex_set)
4068 regfree(&nickserv_conf.valid_handle_regex);
4069 if (nickserv_conf.valid_nick_regex_set)
4070 regfree(&nickserv_conf.valid_nick_regex);
4074 init_nickserv(const char *nick)
4077 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4078 reg_new_user_func(check_user_nick);
4079 reg_nick_change_func(handle_nick_change);
4080 reg_del_user_func(nickserv_remove_user);
4081 reg_account_func(handle_account);
4083 /* set up handle_inverse_flags */
4084 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4085 for (i=0; handle_flags[i]; i++) {
4086 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4087 flag_access_levels[i] = 0;
4090 conf_register_reload(nickserv_conf_read);
4091 nickserv_opt_dict = dict_new();
4092 nickserv_email_dict = dict_new();
4093 dict_set_free_keys(nickserv_email_dict, free);
4094 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4096 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4097 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4098 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4099 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4100 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4101 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4102 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4103 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4104 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4105 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4106 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4107 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4108 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4109 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4110 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4111 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4112 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4113 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4114 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4115 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4116 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4117 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4118 if (!nickserv_conf.disable_nicks) {
4119 /* nick management commands */
4120 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4121 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4122 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4123 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4124 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4125 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4127 if (nickserv_conf.email_enabled) {
4128 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4129 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4130 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4131 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4132 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4133 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4135 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4136 /* miscellaneous commands */
4137 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4138 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4139 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4140 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4141 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4142 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4144 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4145 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4146 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4147 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4148 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4149 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4150 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4151 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4152 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4153 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4154 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4155 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4156 if (nickserv_conf.titlehost_suffix) {
4157 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4158 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4160 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4161 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4162 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4163 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4165 nickserv_handle_dict = dict_new();
4166 dict_set_free_keys(nickserv_handle_dict, free);
4167 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4169 nickserv_id_dict = dict_new();
4170 dict_set_free_keys(nickserv_id_dict, free);
4172 nickserv_nick_dict = dict_new();
4173 dict_set_free_data(nickserv_nick_dict, free);
4175 nickserv_allow_auth_dict = dict_new();
4177 userList_init(&curr_helpers);
4180 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4181 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4182 nickserv_service = service_register(nickserv);
4184 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4185 reg_exit_func(nickserv_db_cleanup);
4186 if(nickserv_conf.handle_expire_frequency)
4187 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4188 message_register_table(msgtab);