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;
920 } else if (user->handle_info->users != NULL) {
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 /* No users authed to the account - can happen if they get
927 * killed for authing. */
929 /* if nobody left on old handle, and they're not an oper, remove !god */
930 if (!user->handle_info->users && !user->handle_info->opserv_level)
931 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
932 /* record them as being last seen at this time */
933 user->handle_info->lastseen = now;
934 /* and record their hostmask */
935 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
937 old_info = user->handle_info;
938 user->handle_info = hi;
939 if (hi && !hi->users && !hi->opserv_level)
940 HANDLE_CLEAR_FLAG(hi, HELPING);
941 for (n=0; n<auth_func_used; n++) {
942 auth_func_list[n](user, old_info);
947 struct nick_info *ni;
949 HANDLE_CLEAR_FLAG(hi, FROZEN);
950 if (nickserv_conf.warn_clone_auth) {
951 struct userNode *other;
952 for (other = hi->users; other; other = other->next_authed)
953 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
955 user->next_authed = hi->users;
958 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
959 userList_append(&curr_helpers, user);
961 if (hi->fakehost || old_info)
965 if (!nickserv_conf.disable_nicks) {
966 struct nick_info *ni2;
967 for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
968 if (!irccasecmp(user->nick, ni2->nick)) {
969 user->modes |= FLAGS_REGNICK;
974 StampUser(user, hi->handle, hi->registered, hi->id);
977 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
978 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
980 /* We cannot clear the user's account ID, unfortunately. */
981 user->next_authed = NULL;
985 static struct handle_info*
986 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
988 struct handle_info *hi;
989 struct nick_info *ni;
990 char crypted[MD5_CRYPT_LENGTH];
992 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
993 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
997 if (!is_secure_password(handle, passwd, user))
1000 cryptpass(passwd, crypted);
1001 hi = register_handle(handle, crypted, 0);
1002 hi->masks = alloc_string_list(1);
1004 hi->language = lang_C;
1005 hi->registered = now;
1007 hi->flags = HI_DEFAULT_FLAGS;
1008 if (settee && !no_auth)
1009 set_user_handle_info(settee, hi, 1);
1012 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1013 else if (nickserv_conf.disable_nicks)
1014 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1015 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1016 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1018 register_nick(user->nick, hi);
1019 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1021 if (settee && (user != settee))
1022 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1027 nickserv_bake_cookie(struct handle_cookie *cookie)
1029 cookie->hi->cookie = cookie;
1030 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1034 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1036 struct handle_cookie *cookie;
1037 char subject[128], body[4096], *misc;
1038 const char *netname, *fmt;
1042 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1046 cookie = calloc(1, sizeof(*cookie));
1048 cookie->type = type;
1049 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1050 cookie->expires = now + nickserv_conf.cookie_timeout;
1051 inttobase64(cookie->cookie, rand(), 5);
1052 inttobase64(cookie->cookie+5, rand(), 5);
1054 netname = nickserv_conf.network_name;
1057 switch (cookie->type) {
1059 hi->passwd[0] = 0; /* invalidate password */
1060 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1061 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1062 snprintf(subject, sizeof(subject), fmt, netname);
1063 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1064 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1067 case PASSWORD_CHANGE:
1068 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1069 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1070 snprintf(subject, sizeof(subject), fmt, netname);
1071 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1072 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1075 misc = hi->email_addr;
1076 hi->email_addr = cookie->data;
1078 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1079 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1080 snprintf(subject, sizeof(subject), fmt, netname);
1081 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1082 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1083 mail_send(nickserv, hi, subject, body, 1);
1084 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1085 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1087 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1088 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1089 snprintf(subject, sizeof(subject), fmt, netname);
1090 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1091 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1092 mail_send(nickserv, hi, subject, body, 1);
1095 hi->email_addr = misc;
1098 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1099 snprintf(subject, sizeof(subject), fmt, netname);
1100 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1101 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1102 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1105 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1109 mail_send(nickserv, hi, subject, body, first_time);
1110 nickserv_bake_cookie(cookie);
1114 nickserv_eat_cookie(struct handle_cookie *cookie)
1116 cookie->hi->cookie = NULL;
1117 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1118 nickserv_free_cookie(cookie);
1122 nickserv_free_email_addr(void *data)
1124 handle_info_list_clean(data);
1129 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1131 struct handle_info_list *hil;
1132 /* Remove from old handle_info_list ... */
1133 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1134 handle_info_list_remove(hil, hi);
1135 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1136 hi->email_addr = NULL;
1138 /* Add to the new list.. */
1139 if (new_email_addr) {
1140 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1141 hil = calloc(1, sizeof(*hil));
1142 hil->tag = strdup(new_email_addr);
1143 handle_info_list_init(hil);
1144 dict_insert(nickserv_email_dict, hil->tag, hil);
1146 handle_info_list_append(hil, hi);
1147 hi->email_addr = hil->tag;
1151 static NICKSERV_FUNC(cmd_register)
1154 struct handle_info *hi;
1155 const char *email_addr, *password;
1158 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1159 /* Require the first handle registered to belong to someone +o. */
1160 reply("NSMSG_REQUIRE_OPER");
1164 if (user->handle_info) {
1165 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1169 if (IsRegistering(user)) {
1170 reply("NSMSG_ALREADY_REGISTERING");
1174 if (IsStamped(user)) {
1175 /* Unauthenticated users might still have been stamped
1176 previously and could therefore have a hidden host;
1177 do not allow them to register a new account. */
1178 reply("NSMSG_STAMPED_REGISTER");
1182 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1184 if (!is_valid_handle(argv[1])) {
1185 reply("NSMSG_BAD_HANDLE", argv[1]);
1189 if ((argc >= 4) && nickserv_conf.email_enabled) {
1190 struct handle_info_list *hil;
1193 /* Remember email address. */
1194 email_addr = argv[3];
1196 /* Check that the email address looks valid.. */
1197 if (!is_valid_email_addr(email_addr)) {
1198 reply("NSMSG_BAD_EMAIL_ADDR");
1202 /* .. and that we are allowed to send to it. */
1203 if ((str = mail_prohibited_address(email_addr))) {
1204 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1208 /* If we do email verify, make sure we don't spam the address. */
1209 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1211 for (nn=0; nn<hil->used; nn++) {
1212 if (hil->list[nn]->cookie) {
1213 reply("NSMSG_EMAIL_UNACTIVATED");
1217 if (hil->used >= nickserv_conf.handles_per_email) {
1218 reply("NSMSG_EMAIL_OVERUSED");
1231 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1233 /* Add any masks they should get. */
1234 if (nickserv_conf.default_hostmask) {
1235 string_list_append(hi->masks, strdup("*@*"));
1237 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1238 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1239 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1242 /* If they're the first to register, give them level 1000. */
1243 if (dict_size(nickserv_handle_dict) == 1) {
1244 hi->opserv_level = 1000;
1245 reply("NSMSG_ROOT_HANDLE", argv[1]);
1248 /* Set their email address. */
1250 nickserv_set_email_addr(hi, email_addr);
1252 /* If they need to do email verification, tell them. */
1254 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1256 /* Set registering flag.. */
1257 user->modes |= FLAGS_REGISTERING;
1262 static NICKSERV_FUNC(cmd_oregister)
1265 struct userNode *settee;
1266 struct handle_info *hi;
1268 NICKSERV_MIN_PARMS(3);
1270 if (!is_valid_handle(argv[1])) {
1271 reply("NSMSG_BAD_HANDLE", argv[1]);
1278 } else if (strchr(argv[3], '@')) {
1279 mask = canonicalize_hostmask(strdup(argv[3]));
1281 settee = GetUserH(argv[4]);
1283 reply("MSG_NICK_UNKNOWN", argv[4]);
1290 } else if ((settee = GetUserH(argv[3]))) {
1291 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1293 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1296 if (settee && settee->handle_info) {
1297 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1301 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1306 string_list_append(hi->masks, mask);
1310 static NICKSERV_FUNC(cmd_handleinfo)
1313 unsigned int i, pos=0, herelen;
1314 struct userNode *target, *next_un;
1315 struct handle_info *hi;
1316 const char *nsmsg_none;
1320 if (!(hi = user->handle_info)) {
1321 reply("NSMSG_MUST_AUTH");
1324 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1328 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1329 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1330 feh = hi->registered;
1331 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1334 intervalString(buff, now - hi->lastseen, user->handle_info);
1335 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1337 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1340 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1341 if (HANDLE_FLAGGED(hi, FROZEN))
1342 reply("NSMSG_HANDLEINFO_VACATION");
1344 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1345 struct do_not_register *dnr;
1346 if ((dnr = chanserv_is_dnr(NULL, hi)))
1347 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1348 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1350 } else if (hi != user->handle_info)
1354 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1356 if (nickserv_conf.email_enabled)
1357 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1361 switch (hi->cookie->type) {
1362 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1363 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1364 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1365 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1366 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1371 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1372 reply("NSMSG_HANDLEINFO_ID", hi->id);
1374 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1376 reply("NSMSG_HANDLEINFO_NO_NOTES");
1378 struct handle_note *prev, *note;
1380 WALK_NOTES(hi, prev, note) {
1381 char set_time[INTERVALLEN];
1382 intervalString(set_time, now - note->set, user->handle_info);
1383 if (note->expires) {
1384 char exp_time[INTERVALLEN];
1385 intervalString(exp_time, note->expires - now, user->handle_info);
1386 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1388 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1395 unsigned long flen = 1;
1396 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1398 for (i=0, flen=1; handle_flags[i]; i++)
1399 if (hi->flags & 1 << i)
1400 flags[flen++] = handle_flags[i];
1402 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1404 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1407 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1408 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1409 || (hi->opserv_level > 0)) {
1410 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1414 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1416 if (hi->last_quit_host[0])
1417 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1419 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1421 if (nickserv_conf.disable_nicks) {
1422 /* nicks disabled; don't show anything about registered nicks */
1423 } else if (hi->nicks) {
1424 struct nick_info *ni, *next_ni;
1425 for (ni = hi->nicks; ni; ni = next_ni) {
1426 herelen = strlen(ni->nick);
1427 if (pos + herelen + 1 > ArrayLength(buff)) {
1429 goto print_nicks_buff;
1433 memcpy(buff+pos, ni->nick, herelen);
1434 pos += herelen; buff[pos++] = ' ';
1438 reply("NSMSG_HANDLEINFO_NICKS", buff);
1443 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1446 if (hi->masks->used) {
1447 for (i=0; i < hi->masks->used; i++) {
1448 herelen = strlen(hi->masks->list[i]);
1449 if (pos + herelen + 1 > ArrayLength(buff)) {
1451 goto print_mask_buff;
1453 memcpy(buff+pos, hi->masks->list[i], herelen);
1454 pos += herelen; buff[pos++] = ' ';
1455 if (i+1 == hi->masks->used) {
1458 reply("NSMSG_HANDLEINFO_MASKS", buff);
1463 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1467 struct userData *chan, *next;
1470 for (chan = hi->channels; chan; chan = next) {
1471 next = chan->u_next;
1472 name = chan->channel->channel->name;
1473 herelen = strlen(name);
1474 if (pos + herelen + 7 > ArrayLength(buff)) {
1476 goto print_chans_buff;
1478 if (IsUserSuspended(chan))
1480 pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
1484 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1489 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1492 for (target = hi->users; target; target = next_un) {
1493 herelen = strlen(target->nick);
1494 if (pos + herelen + 1 > ArrayLength(buff)) {
1496 goto print_cnick_buff;
1498 next_un = target->next_authed;
1500 memcpy(buff+pos, target->nick, herelen);
1501 pos += herelen; buff[pos++] = ' ';
1505 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1510 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1513 static NICKSERV_FUNC(cmd_userinfo)
1515 struct userNode *target;
1517 NICKSERV_MIN_PARMS(2);
1518 if (!(target = GetUserH(argv[1]))) {
1519 reply("MSG_NICK_UNKNOWN", argv[1]);
1522 if (target->handle_info)
1523 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1525 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1529 static NICKSERV_FUNC(cmd_nickinfo)
1531 struct nick_info *ni;
1533 NICKSERV_MIN_PARMS(2);
1534 if (!(ni = get_nick_info(argv[1]))) {
1535 reply("MSG_NICK_UNKNOWN", argv[1]);
1538 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1542 static NICKSERV_FUNC(cmd_notes)
1544 struct handle_info *hi;
1545 struct handle_note *prev, *note;
1548 NICKSERV_MIN_PARMS(2);
1549 if (!(hi = get_victim_oper(user, argv[1])))
1552 WALK_NOTES(hi, prev, note) {
1553 char set_time[INTERVALLEN];
1554 intervalString(set_time, now - note->set, user->handle_info);
1555 if (note->expires) {
1556 char exp_time[INTERVALLEN];
1557 intervalString(exp_time, note->expires - now, user->handle_info);
1558 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1560 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1564 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1568 static NICKSERV_FUNC(cmd_rename_handle)
1570 struct handle_info *hi;
1571 char msgbuf[MAXLEN], *old_handle;
1574 NICKSERV_MIN_PARMS(3);
1575 if (!(hi = get_victim_oper(user, argv[1])))
1577 if (!is_valid_handle(argv[2])) {
1578 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1581 if (get_handle_info(argv[2])) {
1582 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1586 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1587 hi->handle = strdup(argv[2]);
1588 dict_insert(nickserv_handle_dict, hi->handle, hi);
1589 for (nn=0; nn<rf_list_used; nn++)
1590 rf_list[nn](hi, old_handle);
1591 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1592 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1593 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1598 static failpw_func_t *failpw_func_list;
1599 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1602 reg_failpw_func(failpw_func_t func)
1604 if (failpw_func_used == failpw_func_size) {
1605 if (failpw_func_size) {
1606 failpw_func_size <<= 1;
1607 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1609 failpw_func_size = 8;
1610 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1613 failpw_func_list[failpw_func_used++] = func;
1616 static NICKSERV_FUNC(cmd_auth)
1618 int pw_arg, used, maxlogins;
1619 struct handle_info *hi;
1621 struct userNode *other;
1623 if (user->handle_info) {
1624 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1627 if (IsStamped(user)) {
1628 /* Unauthenticated users might still have been stamped
1629 previously and could therefore have a hidden host;
1630 do not allow them to authenticate. */
1631 reply("NSMSG_STAMPED_AUTH");
1635 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1637 } else if (argc == 2) {
1638 if (nickserv_conf.disable_nicks) {
1639 if (!(hi = get_handle_info(user->nick))) {
1640 reply("NSMSG_HANDLE_NOT_FOUND");
1644 /* try to look up their handle from their nick */
1645 struct nick_info *ni;
1646 ni = get_nick_info(user->nick);
1648 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1655 reply("MSG_MISSING_PARAMS", argv[0]);
1656 svccmd_send_help(user, nickserv, cmd);
1660 reply("NSMSG_HANDLE_NOT_FOUND");
1663 /* Responses from here on look up the language used by the handle they asked about. */
1664 passwd = argv[pw_arg];
1665 if (!valid_user_for(user, hi)) {
1666 if (hi->email_addr && nickserv_conf.email_enabled)
1667 send_message_type(4, user, cmd->parent->bot,
1668 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1671 send_message_type(4, user, cmd->parent->bot,
1672 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1674 argv[pw_arg] = "BADMASK";
1677 if (!checkpass(passwd, hi->passwd)) {
1679 send_message_type(4, user, cmd->parent->bot,
1680 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1681 argv[pw_arg] = "BADPASS";
1682 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1683 if (nickserv_conf.autogag_enabled) {
1684 if (!user->auth_policer.params) {
1685 user->auth_policer.last_req = now;
1686 user->auth_policer.params = nickserv_conf.auth_policer_params;
1688 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1690 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1691 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1692 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1694 argv[pw_arg] = "GAGGED";
1699 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1700 send_message_type(4, user, cmd->parent->bot,
1701 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1702 argv[pw_arg] = "SUSPENDED";
1705 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1706 for (used = 0, other = hi->users; other; other = other->next_authed) {
1707 if (++used >= maxlogins) {
1708 send_message_type(4, user, cmd->parent->bot,
1709 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1711 argv[pw_arg] = "MAXLOGINS";
1716 set_user_handle_info(user, hi, 1);
1717 if (nickserv_conf.email_required && !hi->email_addr)
1718 reply("NSMSG_PLEASE_SET_EMAIL");
1719 if (!is_secure_password(hi->handle, passwd, NULL))
1720 reply("NSMSG_WEAK_PASSWORD");
1721 if (hi->passwd[0] != '$')
1722 cryptpass(passwd, hi->passwd);
1723 if (!hi->masks->used) {
1725 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1726 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1727 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1729 argv[pw_arg] = "****";
1730 reply("NSMSG_AUTH_SUCCESS");
1734 static allowauth_func_t *allowauth_func_list;
1735 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1738 reg_allowauth_func(allowauth_func_t func)
1740 if (allowauth_func_used == allowauth_func_size) {
1741 if (allowauth_func_size) {
1742 allowauth_func_size <<= 1;
1743 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1745 allowauth_func_size = 8;
1746 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1749 allowauth_func_list[allowauth_func_used++] = func;
1752 static NICKSERV_FUNC(cmd_allowauth)
1754 struct userNode *target;
1755 struct handle_info *hi;
1758 NICKSERV_MIN_PARMS(2);
1759 if (!(target = GetUserH(argv[1]))) {
1760 reply("MSG_NICK_UNKNOWN", argv[1]);
1763 if (target->handle_info) {
1764 reply("NSMSG_USER_PREV_AUTH", target->nick);
1767 if (IsStamped(target)) {
1768 /* Unauthenticated users might still have been stamped
1769 previously and could therefore have a hidden host;
1770 do not allow them to authenticate to an account. */
1771 reply("NSMSG_USER_PREV_STAMP", target->nick);
1776 else if (!(hi = get_handle_info(argv[2]))) {
1777 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1781 if (hi->opserv_level > user->handle_info->opserv_level) {
1782 reply("MSG_USER_OUTRANKED", hi->handle);
1785 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1786 || (hi->opserv_level > 0))
1787 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1788 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1791 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1792 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1793 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1794 if (nickserv_conf.email_enabled)
1795 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1797 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1798 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1800 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1802 for (n=0; n<allowauth_func_used; n++)
1803 allowauth_func_list[n](user, target, hi);
1807 static NICKSERV_FUNC(cmd_authcookie)
1809 struct handle_info *hi;
1811 NICKSERV_MIN_PARMS(2);
1812 if (user->handle_info) {
1813 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1816 if (IsStamped(user)) {
1817 /* Unauthenticated users might still have been stamped
1818 previously and could therefore have a hidden host;
1819 do not allow them to authenticate to an account. */
1820 reply("NSMSG_STAMPED_AUTHCOOKIE");
1823 if (!(hi = get_handle_info(argv[1]))) {
1824 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1827 if (!hi->email_addr) {
1828 reply("MSG_SET_EMAIL_ADDR");
1831 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1835 static NICKSERV_FUNC(cmd_delcookie)
1837 struct handle_info *hi;
1839 hi = user->handle_info;
1841 reply("NSMSG_NO_COOKIE");
1844 switch (hi->cookie->type) {
1847 reply("NSMSG_MUST_TIME_OUT");
1850 nickserv_eat_cookie(hi->cookie);
1851 reply("NSMSG_ATE_COOKIE");
1857 static NICKSERV_FUNC(cmd_odelcookie)
1859 struct handle_info *hi;
1861 NICKSERV_MIN_PARMS(2);
1863 if (!(hi = get_victim_oper(user, argv[1])))
1867 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
1871 nickserv_eat_cookie(hi->cookie);
1872 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
1877 static NICKSERV_FUNC(cmd_resetpass)
1879 struct handle_info *hi;
1880 char crypted[MD5_CRYPT_LENGTH];
1882 NICKSERV_MIN_PARMS(3);
1883 if (user->handle_info) {
1884 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1887 if (IsStamped(user)) {
1888 /* Unauthenticated users might still have been stamped
1889 previously and could therefore have a hidden host;
1890 do not allow them to activate an account. */
1891 reply("NSMSG_STAMPED_RESETPASS");
1894 if (!(hi = get_handle_info(argv[1]))) {
1895 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1898 if (!hi->email_addr) {
1899 reply("MSG_SET_EMAIL_ADDR");
1902 cryptpass(argv[2], crypted);
1904 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1908 static NICKSERV_FUNC(cmd_cookie)
1910 struct handle_info *hi;
1913 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1916 NICKSERV_MIN_PARMS(3);
1917 if (!(hi = get_handle_info(argv[1]))) {
1918 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1924 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1925 reply("NSMSG_HANDLE_SUSPENDED");
1930 reply("NSMSG_NO_COOKIE");
1934 /* Check validity of operation before comparing cookie to
1935 * prohibit guessing by authed users. */
1936 if (user->handle_info
1937 && (hi->cookie->type != EMAIL_CHANGE)
1938 && (hi->cookie->type != PASSWORD_CHANGE)) {
1939 reply("NSMSG_CANNOT_COOKIE");
1943 if (strcmp(cookie, hi->cookie->cookie)) {
1944 reply("NSMSG_BAD_COOKIE");
1948 switch (hi->cookie->type) {
1950 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1951 set_user_handle_info(user, hi, 1);
1952 reply("NSMSG_HANDLE_ACTIVATED");
1954 case PASSWORD_CHANGE:
1955 set_user_handle_info(user, hi, 1);
1956 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1957 reply("NSMSG_PASSWORD_CHANGED");
1960 nickserv_set_email_addr(hi, hi->cookie->data);
1961 reply("NSMSG_EMAIL_CHANGED");
1964 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1965 set_user_handle_info(user, hi, 1);
1966 nickserv_addmask(user, hi, mask);
1967 reply("NSMSG_AUTH_SUCCESS");
1972 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1973 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1977 nickserv_eat_cookie(hi->cookie);
1982 static NICKSERV_FUNC(cmd_oregnick) {
1984 struct handle_info *target;
1985 struct nick_info *ni;
1987 NICKSERV_MIN_PARMS(3);
1988 if (!(target = modcmd_get_handle_info(user, argv[1])))
1991 if (!is_registerable_nick(nick)) {
1992 reply("NSMSG_BAD_NICK", nick);
1995 ni = dict_find(nickserv_nick_dict, nick, NULL);
1997 reply("NSMSG_NICK_EXISTS", nick);
2000 register_nick(nick, target);
2001 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2005 static NICKSERV_FUNC(cmd_regnick) {
2007 struct nick_info *ni;
2009 if (!is_registerable_nick(user->nick)) {
2010 reply("NSMSG_BAD_NICK", user->nick);
2013 /* count their nicks, see if it's too many */
2014 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2015 if (n >= nickserv_conf.nicks_per_handle) {
2016 reply("NSMSG_TOO_MANY_NICKS");
2019 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2021 reply("NSMSG_NICK_EXISTS", user->nick);
2024 register_nick(user->nick, user->handle_info);
2025 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2029 static NICKSERV_FUNC(cmd_pass)
2031 struct handle_info *hi;
2032 const char *old_pass, *new_pass;
2034 NICKSERV_MIN_PARMS(3);
2035 hi = user->handle_info;
2039 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2040 if (!checkpass(old_pass, hi->passwd)) {
2041 argv[1] = "BADPASS";
2042 reply("NSMSG_PASSWORD_INVALID");
2045 cryptpass(new_pass, hi->passwd);
2047 reply("NSMSG_PASS_SUCCESS");
2052 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2055 char *new_mask = canonicalize_hostmask(strdup(mask));
2056 for (i=0; i<hi->masks->used; i++) {
2057 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2058 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2063 string_list_append(hi->masks, new_mask);
2064 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2068 static NICKSERV_FUNC(cmd_addmask)
2071 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2072 int res = nickserv_addmask(user, user->handle_info, mask);
2076 if (!is_gline(argv[1])) {
2077 reply("NSMSG_MASK_INVALID", argv[1]);
2080 return nickserv_addmask(user, user->handle_info, argv[1]);
2084 static NICKSERV_FUNC(cmd_oaddmask)
2086 struct handle_info *hi;
2088 NICKSERV_MIN_PARMS(3);
2089 if (!(hi = get_victim_oper(user, argv[1])))
2091 return nickserv_addmask(user, hi, argv[2]);
2095 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2098 for (i=0; i<hi->masks->used; i++) {
2099 if (!strcmp(del_mask, hi->masks->list[i])) {
2100 char *old_mask = hi->masks->list[i];
2101 if (hi->masks->used == 1 && !force) {
2102 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2105 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2106 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2111 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2115 static NICKSERV_FUNC(cmd_delmask)
2117 NICKSERV_MIN_PARMS(2);
2118 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2121 static NICKSERV_FUNC(cmd_odelmask)
2123 struct handle_info *hi;
2124 NICKSERV_MIN_PARMS(3);
2125 if (!(hi = get_victim_oper(user, argv[1])))
2127 return nickserv_delmask(user, hi, argv[2], 1);
2131 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2132 unsigned int nn, add = 1, pos;
2133 unsigned long added, removed, flag;
2135 for (added=removed=nn=0; str[nn]; nn++) {
2137 case '+': add = 1; break;
2138 case '-': add = 0; break;
2140 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2141 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2144 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2145 /* cheesy avoidance of looking up the flag name.. */
2146 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2149 flag = 1 << (pos - 1);
2151 added |= flag, removed &= ~flag;
2153 removed |= flag, added &= ~flag;
2158 *premoved = removed;
2163 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2165 unsigned long before, after, added, removed;
2166 struct userNode *uNode;
2168 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2169 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2171 hi->flags = (hi->flags | added) & ~removed;
2172 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2174 /* Strip helping flag if they're only a support helper and not
2175 * currently in #support. */
2176 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2177 struct channelList *schannels;
2179 schannels = chanserv_support_channels();
2180 for (ii = 0; ii < schannels->used; ++ii)
2181 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2183 if (ii == schannels->used)
2184 HANDLE_CLEAR_FLAG(hi, HELPING);
2187 if (after && !before) {
2188 /* Add user to current helper list. */
2189 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2190 userList_append(&curr_helpers, uNode);
2191 } else if (!after && before) {
2192 /* Remove user from current helper list. */
2193 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2194 userList_remove(&curr_helpers, uNode);
2201 set_list(struct userNode *user, struct handle_info *hi, int override)
2205 char *set_display[] = {
2206 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2207 "EMAIL", "MAXLOGINS", "LANGUAGE"
2210 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2212 /* Do this so options are presented in a consistent order. */
2213 for (i = 0; i < ArrayLength(set_display); ++i)
2214 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2215 opt(user, hi, override, 0, NULL);
2218 static NICKSERV_FUNC(cmd_set)
2220 struct handle_info *hi;
2223 hi = user->handle_info;
2225 set_list(user, hi, 0);
2228 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2229 reply("NSMSG_INVALID_OPTION", argv[1]);
2232 return opt(user, hi, 0, argc-1, argv+1);
2235 static NICKSERV_FUNC(cmd_oset)
2237 struct handle_info *hi;
2238 struct svccmd *subcmd;
2240 char cmdname[MAXLEN];
2242 NICKSERV_MIN_PARMS(2);
2244 if (!(hi = get_victim_oper(user, argv[1])))
2248 set_list(user, hi, 0);
2252 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2253 reply("NSMSG_INVALID_OPTION", argv[2]);
2257 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2258 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2259 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2262 return opt(user, hi, 1, argc-2, argv+2);
2265 static OPTION_FUNC(opt_info)
2269 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2271 hi->infoline = NULL;
2273 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2277 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2278 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2282 static OPTION_FUNC(opt_width)
2285 hi->screen_width = strtoul(argv[1], NULL, 0);
2287 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2288 hi->screen_width = MIN_LINE_SIZE;
2289 else if (hi->screen_width > MAX_LINE_SIZE)
2290 hi->screen_width = MAX_LINE_SIZE;
2292 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2296 static OPTION_FUNC(opt_tablewidth)
2299 hi->table_width = strtoul(argv[1], NULL, 0);
2301 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2302 hi->table_width = MIN_LINE_SIZE;
2303 else if (hi->screen_width > MAX_LINE_SIZE)
2304 hi->table_width = MAX_LINE_SIZE;
2306 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2310 static OPTION_FUNC(opt_color)
2313 if (enabled_string(argv[1]))
2314 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2315 else if (disabled_string(argv[1]))
2316 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2318 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2323 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2327 static OPTION_FUNC(opt_privmsg)
2330 if (enabled_string(argv[1]))
2331 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2332 else if (disabled_string(argv[1]))
2333 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2335 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2340 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2344 static OPTION_FUNC(opt_style)
2349 if (!irccasecmp(argv[1], "Zoot"))
2350 hi->userlist_style = HI_STYLE_ZOOT;
2351 else if (!irccasecmp(argv[1], "def"))
2352 hi->userlist_style = HI_STYLE_DEF;
2355 switch (hi->userlist_style) {
2364 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2368 static OPTION_FUNC(opt_password)
2371 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2376 cryptpass(argv[1], hi->passwd);
2378 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2382 static OPTION_FUNC(opt_flags)
2385 unsigned int ii, flen;
2388 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2393 nickserv_apply_flags(user, hi, argv[1]);
2395 for (ii = flen = 0; handle_flags[ii]; ii++)
2396 if (hi->flags & (1 << ii))
2397 flags[flen++] = handle_flags[ii];
2400 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2402 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2406 static OPTION_FUNC(opt_email)
2410 if (!is_valid_email_addr(argv[1])) {
2411 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2414 if ((str = mail_prohibited_address(argv[1]))) {
2415 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2418 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2419 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2421 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2423 nickserv_set_email_addr(hi, argv[1]);
2425 nickserv_eat_cookie(hi->cookie);
2426 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2429 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2433 static OPTION_FUNC(opt_maxlogins)
2435 unsigned char maxlogins;
2437 maxlogins = strtoul(argv[1], NULL, 0);
2438 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2439 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2442 hi->maxlogins = maxlogins;
2444 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2445 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2449 static OPTION_FUNC(opt_language)
2451 struct language *lang;
2453 lang = language_find(argv[1]);
2454 if (irccasecmp(lang->name, argv[1]))
2455 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2456 hi->language = lang;
2458 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2462 static OPTION_FUNC(opt_karma)
2465 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2470 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2471 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2472 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2473 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2475 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2479 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2484 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2485 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2487 if ((user->handle_info->opserv_level < target->opserv_level)
2488 || ((user->handle_info->opserv_level == target->opserv_level)
2489 && (user->handle_info->opserv_level < 1000))) {
2490 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2493 if ((user->handle_info->opserv_level < new_level)
2494 || ((user->handle_info->opserv_level == new_level)
2495 && (user->handle_info->opserv_level < 1000))) {
2496 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2499 if (user->handle_info == target) {
2500 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2503 if (target->opserv_level == new_level)
2505 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2506 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2507 target->opserv_level = new_level;
2511 static OPTION_FUNC(opt_level)
2516 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2520 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2521 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2525 static OPTION_FUNC(opt_epithet)
2528 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2532 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2533 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2536 if ((epithet[0] == '*') && !epithet[1])
2539 hi->epithet = strdup(epithet);
2543 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2545 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2549 static OPTION_FUNC(opt_title)
2554 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2558 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2560 if (strchr(title, '.')) {
2561 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2564 if ((strlen(user->handle_info->handle) + strlen(title) +
2565 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2566 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2571 if (!strcmp(title, "*")) {
2572 hi->fakehost = NULL;
2574 hi->fakehost = malloc(strlen(title)+2);
2575 hi->fakehost[0] = '.';
2576 strcpy(hi->fakehost+1, title);
2579 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2580 title = hi->fakehost + 1;
2584 title = user_find_message(user, "MSG_NONE");
2585 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2589 static OPTION_FUNC(opt_fakehost)
2594 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2598 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2600 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2601 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2605 if (!strcmp(fake, "*"))
2606 hi->fakehost = NULL;
2608 hi->fakehost = strdup(fake);
2609 fake = hi->fakehost;
2612 fake = generate_fakehost(hi);
2614 fake = user_find_message(user, "MSG_NONE");
2615 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2619 static NICKSERV_FUNC(cmd_reclaim)
2621 struct handle_info *hi;
2622 struct nick_info *ni;
2623 struct userNode *victim;
2625 NICKSERV_MIN_PARMS(2);
2626 hi = user->handle_info;
2627 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2629 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2632 if (ni->owner != user->handle_info) {
2633 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2636 victim = GetUserH(ni->nick);
2638 reply("MSG_NICK_UNKNOWN", ni->nick);
2641 if (victim == user) {
2642 reply("NSMSG_NICK_USER_YOU");
2645 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2646 switch (nickserv_conf.reclaim_action) {
2647 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2648 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2649 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2650 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2655 static NICKSERV_FUNC(cmd_unregnick)
2658 struct handle_info *hi;
2659 struct nick_info *ni;
2661 hi = user->handle_info;
2662 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2663 ni = dict_find(nickserv_nick_dict, nick, NULL);
2665 reply("NSMSG_UNKNOWN_NICK", nick);
2668 if (hi != ni->owner) {
2669 reply("NSMSG_NOT_YOUR_NICK", nick);
2672 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2677 static NICKSERV_FUNC(cmd_ounregnick)
2679 struct nick_info *ni;
2681 NICKSERV_MIN_PARMS(2);
2682 if (!(ni = get_nick_info(argv[1]))) {
2683 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2686 if (!oper_outranks(user, ni->owner))
2688 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2693 static NICKSERV_FUNC(cmd_unregister)
2695 struct handle_info *hi;
2698 NICKSERV_MIN_PARMS(2);
2699 hi = user->handle_info;
2702 if (checkpass(passwd, hi->passwd)) {
2703 nickserv_unregister_handle(hi, user);
2706 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2707 reply("NSMSG_PASSWORD_INVALID");
2712 static NICKSERV_FUNC(cmd_ounregister)
2714 struct handle_info *hi;
2715 char reason[MAXLEN];
2718 NICKSERV_MIN_PARMS(2);
2719 if (!(hi = get_victim_oper(user, argv[1])))
2722 if (HANDLE_FLAGGED(hi, NODELETE)) {
2723 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
2727 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
2729 ((hi->flags & nickserv_conf.ounregister_flags)
2731 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
2732 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
2736 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2737 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2738 nickserv_unregister_handle(hi, user);
2742 static NICKSERV_FUNC(cmd_status)
2744 if (nickserv_conf.disable_nicks) {
2745 reply("NSMSG_GLOBAL_STATS_NONICK",
2746 dict_size(nickserv_handle_dict));
2748 if (user->handle_info) {
2750 struct nick_info *ni;
2751 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2752 reply("NSMSG_HANDLE_STATS", cnt);
2754 reply("NSMSG_HANDLE_NONE");
2756 reply("NSMSG_GLOBAL_STATS",
2757 dict_size(nickserv_handle_dict),
2758 dict_size(nickserv_nick_dict));
2763 static NICKSERV_FUNC(cmd_ghost)
2765 struct userNode *target;
2766 char reason[MAXLEN];
2768 NICKSERV_MIN_PARMS(2);
2769 if (!(target = GetUserH(argv[1]))) {
2770 reply("MSG_NICK_UNKNOWN", argv[1]);
2773 if (target == user) {
2774 reply("NSMSG_CANNOT_GHOST_SELF");
2777 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2778 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2781 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2782 DelUser(target, nickserv, 1, reason);
2783 reply("NSMSG_GHOST_KILLED", argv[1]);
2787 static NICKSERV_FUNC(cmd_vacation)
2789 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2790 reply("NSMSG_ON_VACATION");
2794 static NICKSERV_FUNC(cmd_addnote)
2796 struct handle_info *hi;
2797 unsigned long duration;
2800 struct handle_note *prev;
2801 struct handle_note *note;
2803 /* Parse parameters and figure out values for note's fields. */
2804 NICKSERV_MIN_PARMS(4);
2805 hi = get_victim_oper(user, argv[1]);
2808 if(!strcmp(argv[2], "0"))
2810 else if(!(duration = ParseInterval(argv[2])))
2812 reply("MSG_INVALID_DURATION", argv[2]);
2815 if (duration > 2*365*86400) {
2816 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2819 unsplit_string(argv + 3, argc - 3, text);
2820 WALK_NOTES(hi, prev, note) {}
2821 id = prev ? (prev->id + 1) : 1;
2823 /* Create the new note structure. */
2824 note = calloc(1, sizeof(*note) + strlen(text));
2826 note->expires = duration ? (now + duration) : 0;
2829 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2830 strcpy(note->note, text);
2835 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2839 static NICKSERV_FUNC(cmd_delnote)
2841 struct handle_info *hi;
2842 struct handle_note *prev;
2843 struct handle_note *note;
2846 NICKSERV_MIN_PARMS(3);
2847 hi = get_victim_oper(user, argv[1]);
2850 id = strtoul(argv[2], NULL, 10);
2851 WALK_NOTES(hi, prev, note) {
2852 if (id == note->id) {
2854 prev->next = note->next;
2856 hi->notes = note->next;
2858 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2862 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2867 nickserv_saxdb_write(struct saxdb_context *ctx) {
2869 struct handle_info *hi;
2872 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2874 assert(hi->id != 0);
2875 saxdb_start_record(ctx, iter_key(it), 0);
2877 struct handle_cookie *cookie = hi->cookie;
2880 switch (cookie->type) {
2881 case ACTIVATION: type = KEY_ACTIVATION; break;
2882 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2883 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2884 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2885 default: type = NULL; break;
2888 saxdb_start_record(ctx, KEY_COOKIE, 0);
2889 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2890 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2892 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2893 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2894 saxdb_end_record(ctx);
2898 struct handle_note *prev, *note;
2899 saxdb_start_record(ctx, KEY_NOTES, 0);
2900 WALK_NOTES(hi, prev, note) {
2901 snprintf(flags, sizeof(flags), "%d", note->id);
2902 saxdb_start_record(ctx, flags, 0);
2904 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
2905 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
2906 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
2907 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
2908 saxdb_end_record(ctx);
2910 saxdb_end_record(ctx);
2913 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2915 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2917 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2921 for (ii=flen=0; handle_flags[ii]; ++ii)
2922 if (hi->flags & (1 << ii))
2923 flags[flen++] = handle_flags[ii];
2925 saxdb_write_string(ctx, KEY_FLAGS, flags);
2927 saxdb_write_int(ctx, KEY_ID, hi->id);
2929 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2930 if (hi->last_quit_host[0])
2931 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2932 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2934 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
2935 if (hi->masks->used)
2936 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2938 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2940 struct string_list *slist;
2941 struct nick_info *ni;
2943 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2944 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2945 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2949 if (hi->opserv_level)
2950 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2951 if (hi->language != lang_C)
2952 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2953 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2954 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2955 if (hi->screen_width)
2956 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2957 if (hi->table_width)
2958 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2959 flags[0] = hi->userlist_style;
2961 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2962 saxdb_end_record(ctx);
2967 static handle_merge_func_t *handle_merge_func_list;
2968 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2971 reg_handle_merge_func(handle_merge_func_t func)
2973 if (handle_merge_func_used == handle_merge_func_size) {
2974 if (handle_merge_func_size) {
2975 handle_merge_func_size <<= 1;
2976 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2978 handle_merge_func_size = 8;
2979 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2982 handle_merge_func_list[handle_merge_func_used++] = func;
2985 static NICKSERV_FUNC(cmd_merge)
2987 struct handle_info *hi_from, *hi_to;
2988 struct userNode *last_user;
2989 struct userData *cList, *cListNext;
2990 unsigned int ii, jj, n;
2991 char buffer[MAXLEN];
2993 NICKSERV_MIN_PARMS(3);
2995 if (!(hi_from = get_victim_oper(user, argv[1])))
2997 if (!(hi_to = get_victim_oper(user, argv[2])))
2999 if (hi_to == hi_from) {
3000 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3004 for (n=0; n<handle_merge_func_used; n++)
3005 handle_merge_func_list[n](user, hi_to, hi_from);
3007 /* Append "from" handle's nicks to "to" handle's nick list. */
3009 struct nick_info *last_ni;
3010 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3011 last_ni->next = hi_from->nicks;
3013 while (hi_from->nicks) {
3014 hi_from->nicks->owner = hi_to;
3015 hi_from->nicks = hi_from->nicks->next;
3018 /* Merge the hostmasks. */
3019 for (ii=0; ii<hi_from->masks->used; ii++) {
3020 char *mask = hi_from->masks->list[ii];
3021 for (jj=0; jj<hi_to->masks->used; jj++)
3022 if (match_ircglobs(hi_to->masks->list[jj], mask))
3024 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3025 string_list_append(hi_to->masks, strdup(mask));
3028 /* Merge the lists of authed users. */
3030 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3031 last_user->next_authed = hi_from->users;
3033 hi_to->users = hi_from->users;
3035 /* Repoint the old "from" handle's users. */
3036 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3037 last_user->handle_info = hi_to;
3039 hi_from->users = NULL;
3041 /* Merge channel userlists. */
3042 for (cList=hi_from->channels; cList; cList=cListNext) {
3043 struct userData *cList2;
3044 cListNext = cList->u_next;
3045 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3046 if (cList->channel == cList2->channel)
3048 if (cList2 && (cList2->access >= cList->access)) {
3049 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);
3050 /* keep cList2 in hi_to; remove cList from hi_from */
3051 del_channel_user(cList, 1);
3054 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);
3055 /* remove the lower-ranking cList2 from hi_to */
3056 del_channel_user(cList2, 1);
3058 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3060 /* cList needs to be moved from hi_from to hi_to */
3061 cList->handle = hi_to;
3062 /* Remove from linked list for hi_from */
3063 assert(!cList->u_prev);
3064 hi_from->channels = cList->u_next;
3066 cList->u_next->u_prev = cList->u_prev;
3067 /* Add to linked list for hi_to */
3068 cList->u_prev = NULL;
3069 cList->u_next = hi_to->channels;
3070 if (hi_to->channels)
3071 hi_to->channels->u_prev = cList;
3072 hi_to->channels = cList;
3076 /* Do they get an OpServ level promotion? */
3077 if (hi_from->opserv_level > hi_to->opserv_level)
3078 hi_to->opserv_level = hi_from->opserv_level;
3080 /* What about last seen time? */
3081 if (hi_from->lastseen > hi_to->lastseen)
3082 hi_to->lastseen = hi_from->lastseen;
3084 /* New karma is the sum of the two original karmas. */
3085 hi_to->karma += hi_from->karma;
3087 /* Does a fakehost carry over? (This intentionally doesn't set it
3088 * for users previously attached to hi_to. They'll just have to
3091 if (hi_from->fakehost && !hi_to->fakehost)
3092 hi_to->fakehost = strdup(hi_from->fakehost);
3094 /* Notify of success. */
3095 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3096 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3097 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3099 /* Unregister the "from" handle. */
3100 nickserv_unregister_handle(hi_from, NULL);
3105 struct nickserv_discrim {
3106 unsigned long flags_on, flags_off;
3107 unsigned long min_registered, max_registered;
3108 unsigned long lastseen;
3110 int min_level, max_level;
3111 int min_karma, max_karma;
3112 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3113 const char *nickmask;
3114 const char *hostmask;
3115 const char *fakehostmask;
3116 const char *handlemask;
3117 const char *emailmask;
3120 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3122 struct discrim_apply_info {
3123 struct nickserv_discrim *discrim;
3124 discrim_search_func func;
3125 struct userNode *source;
3126 unsigned int matched;
3129 static struct nickserv_discrim *
3130 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3133 struct nickserv_discrim *discrim;
3135 discrim = malloc(sizeof(*discrim));
3136 memset(discrim, 0, sizeof(*discrim));
3137 discrim->min_level = 0;
3138 discrim->max_level = INT_MAX;
3139 discrim->limit = 50;
3140 discrim->min_registered = 0;
3141 discrim->max_registered = ULONG_MAX;
3142 discrim->lastseen = ULONG_MAX;
3143 discrim->min_karma = INT_MIN;
3144 discrim->max_karma = INT_MAX;
3146 for (i=0; i<argc; i++) {
3147 if (i == argc - 1) {
3148 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3151 if (!irccasecmp(argv[i], "limit")) {
3152 discrim->limit = strtoul(argv[++i], NULL, 0);
3153 } else if (!irccasecmp(argv[i], "flags")) {
3154 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3155 } else if (!irccasecmp(argv[i], "registered")) {
3156 const char *cmp = argv[++i];
3157 if (cmp[0] == '<') {
3158 if (cmp[1] == '=') {
3159 discrim->min_registered = now - ParseInterval(cmp+2);
3161 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3163 } else if (cmp[0] == '=') {
3164 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3165 } else if (cmp[0] == '>') {
3166 if (cmp[1] == '=') {
3167 discrim->max_registered = now - ParseInterval(cmp+2);
3169 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3172 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3174 } else if (!irccasecmp(argv[i], "seen")) {
3175 discrim->lastseen = now - ParseInterval(argv[++i]);
3176 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3177 discrim->nickmask = argv[++i];
3178 } else if (!irccasecmp(argv[i], "hostmask")) {
3180 if (!irccasecmp(argv[i], "exact")) {
3181 if (i == argc - 1) {
3182 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3185 discrim->hostmask_type = EXACT;
3186 } else if (!irccasecmp(argv[i], "subset")) {
3187 if (i == argc - 1) {
3188 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3191 discrim->hostmask_type = SUBSET;
3192 } else if (!irccasecmp(argv[i], "superset")) {
3193 if (i == argc - 1) {
3194 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3197 discrim->hostmask_type = SUPERSET;
3198 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3199 if (i == argc - 1) {
3200 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3203 discrim->hostmask_type = LASTQUIT;
3206 discrim->hostmask_type = SUPERSET;
3208 discrim->hostmask = argv[++i];
3209 } else if (!irccasecmp(argv[i], "fakehost")) {
3210 if (!irccasecmp(argv[++i], "*")) {
3211 discrim->fakehostmask = 0;
3213 discrim->fakehostmask = argv[i];
3215 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3216 if (!irccasecmp(argv[++i], "*")) {
3217 discrim->handlemask = 0;
3219 discrim->handlemask = argv[i];
3221 } else if (!irccasecmp(argv[i], "email")) {
3222 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3223 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3225 } else if (!irccasecmp(argv[++i], "*")) {
3226 discrim->emailmask = 0;
3228 discrim->emailmask = argv[i];
3230 } else if (!irccasecmp(argv[i], "access")) {
3231 const char *cmp = argv[++i];
3232 if (cmp[0] == '<') {
3233 if (discrim->min_level == 0) discrim->min_level = 1;
3234 if (cmp[1] == '=') {
3235 discrim->max_level = strtoul(cmp+2, NULL, 0);
3237 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3239 } else if (cmp[0] == '=') {
3240 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3241 } else if (cmp[0] == '>') {
3242 if (cmp[1] == '=') {
3243 discrim->min_level = strtoul(cmp+2, NULL, 0);
3245 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3248 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3250 } else if (!irccasecmp(argv[i], "karma")) {
3251 const char *cmp = argv[++i];
3252 if (cmp[0] == '<') {
3253 if (cmp[1] == '=') {
3254 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3256 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3258 } else if (cmp[0] == '=') {
3259 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3260 } else if (cmp[0] == '>') {
3261 if (cmp[1] == '=') {
3262 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3264 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3267 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3270 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3281 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3283 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3284 || (discrim->flags_off & hi->flags)
3285 || (discrim->min_registered > hi->registered)
3286 || (discrim->max_registered < hi->registered)
3287 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3288 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3289 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3290 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3291 || (discrim->min_level > hi->opserv_level)
3292 || (discrim->max_level < hi->opserv_level)
3293 || (discrim->min_karma > hi->karma)
3294 || (discrim->max_karma < hi->karma)
3298 if (discrim->hostmask) {
3300 for (i=0; i<hi->masks->used; i++) {
3301 const char *mask = hi->masks->list[i];
3302 if ((discrim->hostmask_type == SUBSET)
3303 && (match_ircglobs(discrim->hostmask, mask))) break;
3304 else if ((discrim->hostmask_type == EXACT)
3305 && !irccasecmp(discrim->hostmask, mask)) break;
3306 else if ((discrim->hostmask_type == SUPERSET)
3307 && (match_ircglobs(mask, discrim->hostmask))) break;
3308 else if ((discrim->hostmask_type == LASTQUIT)
3309 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3311 if (i==hi->masks->used) return 0;
3313 if (discrim->nickmask) {
3314 struct nick_info *nick = hi->nicks;
3316 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3319 if (!nick) return 0;
3325 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3327 dict_iterator_t it, next;
3328 unsigned int matched;
3330 for (it = dict_first(nickserv_handle_dict), matched = 0;
3331 it && (matched < discrim->limit);
3333 next = iter_next(it);
3334 if (nickserv_discrim_match(discrim, iter_data(it))) {
3335 dsf(source, iter_data(it));
3343 search_print_func(struct userNode *source, struct handle_info *match)
3345 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3349 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3354 search_unregister_func (struct userNode *source, struct handle_info *match)
3356 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3357 nickserv_unregister_handle(match, source);
3361 nickserv_sort_accounts_by_access(const void *a, const void *b)
3363 const struct handle_info *hi_a = *(const struct handle_info**)a;
3364 const struct handle_info *hi_b = *(const struct handle_info**)b;
3365 if (hi_a->opserv_level != hi_b->opserv_level)
3366 return hi_b->opserv_level - hi_a->opserv_level;
3367 return irccasecmp(hi_a->handle, hi_b->handle);
3371 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3373 struct handle_info_list hil;
3374 struct helpfile_table tbl;
3379 memset(&hil, 0, sizeof(hil));
3380 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3381 struct handle_info *hi = iter_data(it);
3382 if (hi->opserv_level)
3383 handle_info_list_append(&hil, hi);
3385 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3386 tbl.length = hil.used + 1;
3388 tbl.flags = TABLE_NO_FREE;
3389 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3390 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3393 for (ii = 0; ii < hil.used; ) {
3394 ary = malloc(tbl.width * sizeof(ary[0]));
3395 ary[0] = hil.list[ii]->handle;
3396 ary[1] = strtab(hil.list[ii]->opserv_level);
3397 tbl.contents[++ii] = ary;
3399 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3400 reply("MSG_MATCH_COUNT", hil.used);
3401 for (ii = 0; ii < hil.used; ii++)
3402 free(tbl.contents[ii]);
3407 static NICKSERV_FUNC(cmd_search)
3409 struct nickserv_discrim *discrim;
3410 discrim_search_func action;
3411 struct svccmd *subcmd;
3412 unsigned int matches;
3415 NICKSERV_MIN_PARMS(3);
3416 sprintf(buf, "search %s", argv[1]);
3417 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3418 if (!irccasecmp(argv[1], "print"))
3419 action = search_print_func;
3420 else if (!irccasecmp(argv[1], "count"))
3421 action = search_count_func;
3422 else if (!irccasecmp(argv[1], "unregister"))
3423 action = search_unregister_func;
3425 reply("NSMSG_INVALID_ACTION", argv[1]);
3429 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3432 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3436 if (action == search_print_func)
3437 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3438 else if (action == search_count_func)
3439 discrim->limit = INT_MAX;
3441 matches = nickserv_discrim_search(discrim, action, user);
3444 reply("MSG_MATCH_COUNT", matches);
3446 reply("MSG_NO_MATCHES");
3452 static MODCMD_FUNC(cmd_checkpass)
3454 struct handle_info *hi;
3456 NICKSERV_MIN_PARMS(3);
3457 if (!(hi = get_handle_info(argv[1]))) {
3458 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3461 if (checkpass(argv[2], hi->passwd))
3462 reply("CHECKPASS_YES");
3464 reply("CHECKPASS_NO");
3469 static MODCMD_FUNC(cmd_checkemail)
3471 struct handle_info *hi;
3473 NICKSERV_MIN_PARMS(3);
3474 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3477 if (!hi->email_addr)
3478 reply("CHECKEMAIL_NOT_SET");
3479 else if (!irccasecmp(argv[2], hi->email_addr))
3480 reply("CHECKEMAIL_YES");
3482 reply("CHECKEMAIL_NO");
3488 nickserv_db_read_handle(const char *handle, dict_t obj)
3491 struct string_list *masks, *slist;
3492 struct handle_info *hi;
3493 struct userNode *authed_users;
3494 struct userData *channel_list;
3499 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3500 id = str ? strtoul(str, NULL, 0) : 0;
3501 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3503 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3506 if ((hi = get_handle_info(handle))) {
3507 authed_users = hi->users;
3508 channel_list = hi->channels;
3510 hi->channels = NULL;
3511 dict_remove(nickserv_handle_dict, hi->handle);
3513 authed_users = NULL;
3514 channel_list = NULL;
3516 hi = register_handle(handle, str, id);
3518 hi->users = authed_users;
3519 while (authed_users) {
3520 authed_users->handle_info = hi;
3521 authed_users = authed_users->next_authed;
3524 hi->channels = channel_list;
3525 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3526 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3527 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3528 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3529 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3530 hi->language = language_find(str ? str : "C");
3531 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3532 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3533 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3535 hi->infoline = strdup(str);
3536 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3537 hi->registered = str ? strtoul(str, NULL, 0) : now;
3538 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3539 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3540 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3541 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3542 /* We want to read the nicks even if disable_nicks is set. This is so
3543 * that we don't lose the nick data entirely. */
3544 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3546 for (ii=0; ii<slist->used; ii++)
3547 register_nick(slist->list[ii], hi);
3549 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3551 for (ii=0; str[ii]; ii++)
3552 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3554 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3555 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3556 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3557 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3558 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3559 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3560 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3562 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3564 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3565 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3567 nickserv_set_email_addr(hi, str);
3568 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3570 hi->epithet = strdup(str);
3571 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3573 hi->fakehost = strdup(str);
3574 /* Read the "cookie" sub-database (if it exists). */
3575 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3577 const char *data, *type, *expires, *cookie_str;
3578 struct handle_cookie *cookie;
3580 cookie = calloc(1, sizeof(*cookie));
3581 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3582 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3583 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3584 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3585 if (!type || !expires || !cookie_str) {
3586 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3589 if (!irccasecmp(type, KEY_ACTIVATION))
3590 cookie->type = ACTIVATION;
3591 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3592 cookie->type = PASSWORD_CHANGE;
3593 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3594 cookie->type = EMAIL_CHANGE;
3595 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3596 cookie->type = ALLOWAUTH;
3598 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3601 cookie->expires = strtoul(expires, NULL, 0);
3602 if (cookie->expires < now)
3605 cookie->data = strdup(data);
3606 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3610 nickserv_bake_cookie(cookie);
3612 nickserv_free_cookie(cookie);
3614 /* Read the "notes" sub-database (if it exists). */
3615 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3618 struct handle_note *last_note;
3619 struct handle_note *note;
3622 for (it = dict_first(subdb); it; it = iter_next(it)) {
3623 const char *expires;
3627 const char *note_id;
3630 note_id = iter_key(it);
3631 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3633 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
3636 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3637 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3638 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3639 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3640 if (!setter || !text || !set) {
3641 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
3644 note = calloc(1, sizeof(*note) + strlen(text));
3646 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3647 note->set = strtoul(set, NULL, 10);
3648 note->id = strtoul(note_id, NULL, 10);
3649 safestrncpy(note->setter, setter, sizeof(note->setter));
3650 strcpy(note->note, text);
3652 last_note->next = note;
3661 nickserv_saxdb_read(dict_t db) {
3663 struct record_data *rd;
3665 for (it=dict_first(db); it; it=iter_next(it)) {
3667 nickserv_db_read_handle(iter_key(it), rd->d.object);
3672 static NICKSERV_FUNC(cmd_mergedb)
3674 struct timeval start, stop;
3677 NICKSERV_MIN_PARMS(2);
3678 gettimeofday(&start, NULL);
3679 if (!(db = parse_database(argv[1]))) {
3680 reply("NSMSG_DB_UNREADABLE", argv[1]);
3683 nickserv_saxdb_read(db);
3685 gettimeofday(&stop, NULL);
3686 stop.tv_sec -= start.tv_sec;
3687 stop.tv_usec -= start.tv_usec;
3688 if (stop.tv_usec < 0) {
3690 stop.tv_usec += 1000000;
3692 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
3697 expire_handles(UNUSED_ARG(void *data))
3699 dict_iterator_t it, next;
3700 unsigned long expiry;
3701 struct handle_info *hi;
3703 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3704 next = iter_next(it);
3706 if ((hi->opserv_level > 0)
3708 || HANDLE_FLAGGED(hi, FROZEN)
3709 || HANDLE_FLAGGED(hi, NODELETE)) {
3712 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3713 if ((now - hi->lastseen) > expiry) {
3714 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3715 nickserv_unregister_handle(hi, NULL);
3719 if (nickserv_conf.handle_expire_frequency)
3720 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3724 nickserv_load_dict(const char *fname)
3728 if (!(file = fopen(fname, "r"))) {
3729 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3732 while (fgets(line, sizeof(line), file)) {
3735 if (line[strlen(line)-1] == '\n')
3736 line[strlen(line)-1] = 0;
3737 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3740 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3743 static enum reclaim_action
3744 reclaim_action_from_string(const char *str) {
3746 return RECLAIM_NONE;
3747 else if (!irccasecmp(str, "warn"))
3748 return RECLAIM_WARN;
3749 else if (!irccasecmp(str, "svsnick"))
3750 return RECLAIM_SVSNICK;
3751 else if (!irccasecmp(str, "kill"))
3752 return RECLAIM_KILL;
3754 return RECLAIM_NONE;
3758 nickserv_conf_read(void)
3760 dict_t conf_node, child;
3764 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3765 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3768 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3770 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3771 if (nickserv_conf.valid_handle_regex_set)
3772 regfree(&nickserv_conf.valid_handle_regex);
3774 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3775 nickserv_conf.valid_handle_regex_set = !err;
3776 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3778 nickserv_conf.valid_handle_regex_set = 0;
3780 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3781 if (nickserv_conf.valid_nick_regex_set)
3782 regfree(&nickserv_conf.valid_nick_regex);
3784 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3785 nickserv_conf.valid_nick_regex_set = !err;
3786 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3788 nickserv_conf.valid_nick_regex_set = 0;
3790 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3792 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3793 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3794 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3795 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3796 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3797 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3798 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3799 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3800 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3801 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3802 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3803 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3804 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3805 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3806 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3807 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3808 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3809 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3810 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3811 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3812 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3813 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3814 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3815 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3816 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3818 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3819 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3820 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3822 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3823 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3824 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3826 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3827 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3828 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3829 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3830 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3831 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3832 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3833 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3834 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
3835 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
3836 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
3839 nickserv_conf.ounregister_flags = 0;
3841 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
3844 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
3846 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
3848 nickserv_conf.handle_ts_mode = TS_IGNORE;
3849 else if (!irccasecmp(str, "ircu"))
3850 nickserv_conf.handle_ts_mode = TS_IRCU;
3852 nickserv_conf.handle_ts_mode = TS_IGNORE;
3853 if (!nickserv_conf.disable_nicks) {
3854 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3855 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3856 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3857 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3858 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3859 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3860 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3861 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3863 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3864 for (it=dict_first(child); it; it=iter_next(it)) {
3865 const char *key = iter_key(it), *value;
3869 if (!strncasecmp(key, "uc_", 3))
3870 flag = toupper(key[3]);
3871 else if (!strncasecmp(key, "lc_", 3))
3872 flag = tolower(key[3]);
3876 if ((pos = handle_inverse_flags[flag])) {
3877 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3878 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3881 if (nickserv_conf.weak_password_dict)
3882 dict_delete(nickserv_conf.weak_password_dict);
3883 nickserv_conf.weak_password_dict = dict_new();
3884 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3885 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3886 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3887 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3889 nickserv_load_dict(str);
3890 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3891 if (nickserv && str)
3892 NickChange(nickserv, str, 0);
3893 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3894 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3895 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3896 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3897 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3898 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3899 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3900 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3901 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3902 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3903 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3904 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3905 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3906 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3907 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3908 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3909 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3910 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3911 str = conf_get_data("server/network", RECDB_QSTRING);
3912 nickserv_conf.network_name = str ? str : "some IRC network";
3913 if (!nickserv_conf.auth_policer_params) {
3914 nickserv_conf.auth_policer_params = policer_params_new();
3915 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3916 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3918 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3919 for (it=dict_first(child); it; it=iter_next(it))
3920 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3924 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3926 char newnick[NICKLEN+1];
3935 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3937 case RECLAIM_SVSNICK:
3939 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3940 } while (GetUserH(newnick));
3941 irc_svsnick(nickserv, user, newnick);
3944 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3945 DelUser(user, nickserv, 1, msg);
3951 nickserv_reclaim_p(void *data) {
3952 struct userNode *user = data;
3953 struct nick_info *ni = get_nick_info(user->nick);
3955 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3959 check_user_nick(struct userNode *user) {
3960 struct nick_info *ni;
3961 user->modes &= ~FLAGS_REGNICK;
3962 if (!(ni = get_nick_info(user->nick)))
3964 if (user->handle_info == ni->owner) {
3965 user->modes |= FLAGS_REGNICK;
3969 if (nickserv_conf.warn_nick_owned)
3970 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3971 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3973 if (nickserv_conf.auto_reclaim_delay)
3974 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3976 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3980 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
3982 struct handle_info *hi = NULL;
3985 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3986 if ((hi == NULL) && (serial != 0)) {
3988 inttobase64(id, serial, IDLEN);
3989 hi = dict_find(nickserv_id_dict, id, NULL);
3993 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
3994 && (timestamp != hi->registered)) {
3997 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4000 set_user_handle_info(user, hi, 0);
4002 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4007 handle_nick_change(struct userNode *user, const char *old_nick)
4009 struct handle_info *hi;
4011 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4012 dict_remove(nickserv_allow_auth_dict, old_nick);
4013 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4015 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4016 check_user_nick(user);
4020 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4022 dict_remove(nickserv_allow_auth_dict, user->nick);
4023 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4024 set_user_handle_info(user, NULL, 0);
4027 static struct modcmd *
4028 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4030 if (min_level > 0) {
4032 sprintf(buf, "%u", min_level);
4033 if (must_be_qualified) {
4034 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4036 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4038 } else if (min_level == 0) {
4039 if (must_be_qualified) {
4040 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4042 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4045 if (must_be_qualified) {
4046 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4048 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4054 nickserv_db_cleanup(void)
4056 unreg_del_user_func(nickserv_remove_user);
4057 userList_clean(&curr_helpers);
4058 policer_params_delete(nickserv_conf.auth_policer_params);
4059 dict_delete(nickserv_handle_dict);
4060 dict_delete(nickserv_nick_dict);
4061 dict_delete(nickserv_opt_dict);
4062 dict_delete(nickserv_allow_auth_dict);
4063 dict_delete(nickserv_email_dict);
4064 dict_delete(nickserv_id_dict);
4065 dict_delete(nickserv_conf.weak_password_dict);
4066 free(auth_func_list);
4067 free(unreg_func_list);
4069 free(allowauth_func_list);
4070 free(handle_merge_func_list);
4071 free(failpw_func_list);
4072 if (nickserv_conf.valid_handle_regex_set)
4073 regfree(&nickserv_conf.valid_handle_regex);
4074 if (nickserv_conf.valid_nick_regex_set)
4075 regfree(&nickserv_conf.valid_nick_regex);
4079 init_nickserv(const char *nick)
4082 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4083 reg_new_user_func(check_user_nick);
4084 reg_nick_change_func(handle_nick_change);
4085 reg_del_user_func(nickserv_remove_user);
4086 reg_account_func(handle_account);
4088 /* set up handle_inverse_flags */
4089 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4090 for (i=0; handle_flags[i]; i++) {
4091 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4092 flag_access_levels[i] = 0;
4095 conf_register_reload(nickserv_conf_read);
4096 nickserv_opt_dict = dict_new();
4097 nickserv_email_dict = dict_new();
4098 dict_set_free_keys(nickserv_email_dict, free);
4099 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4101 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4102 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4103 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4104 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4105 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4106 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4107 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4108 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4109 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4110 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4111 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4112 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4113 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4114 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4115 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4116 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4117 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4118 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4119 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4120 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4121 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4122 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4123 if (!nickserv_conf.disable_nicks) {
4124 /* nick management commands */
4125 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4126 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4127 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4128 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4129 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4130 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4132 if (nickserv_conf.email_enabled) {
4133 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4134 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4135 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4136 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4137 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4138 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4140 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4141 /* miscellaneous commands */
4142 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4143 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4144 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4145 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4146 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4147 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4149 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4150 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4151 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4152 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4153 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4154 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4155 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4156 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4157 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4158 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4159 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4160 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4161 if (nickserv_conf.titlehost_suffix) {
4162 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4163 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4165 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4166 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4167 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4168 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4170 nickserv_handle_dict = dict_new();
4171 dict_set_free_keys(nickserv_handle_dict, free);
4172 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4174 nickserv_id_dict = dict_new();
4175 dict_set_free_keys(nickserv_id_dict, free);
4177 nickserv_nick_dict = dict_new();
4178 dict_set_free_data(nickserv_nick_dict, free);
4180 nickserv_allow_auth_dict = dict_new();
4182 userList_init(&curr_helpers);
4185 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4186 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4187 nickserv_service = service_register(nickserv);
4189 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4190 reg_exit_func(nickserv_db_cleanup);
4191 if(nickserv_conf.handle_expire_frequency)
4192 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4193 message_register_table(msgtab);