1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2008 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for gag_create(), opserv_bad_channel() */
33 # include "rx/rxposix.h"
36 #define NICKSERV_CONF_NAME "services/nickserv"
38 #define KEY_DISABLE_NICKS "disable_nicks"
39 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
40 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
41 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
42 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
43 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
44 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
45 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
46 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
47 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
48 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
49 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
50 #define KEY_MODOPER_LEVEL "modoper_level"
51 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
52 #define KEY_SET_TITLE_LEVEL "set_title_level"
53 #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
54 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
55 #define KEY_FLAG_LEVELS "flag_levels"
56 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
57 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
58 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
59 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
60 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
61 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
62 #define KEY_DICT_FILE "dict_file"
63 #define KEY_NICK "nick"
64 #define KEY_LANGUAGE "language"
65 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
66 #define KEY_AUTOGAG_DURATION "autogag_duration"
67 #define KEY_AUTH_POLICER "auth_policer"
68 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
69 #define KEY_EMAIL_ENABLED "email_enabled"
70 #define KEY_EMAIL_REQUIRED "email_required"
71 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
72 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
73 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
74 #define KEY_OUNREGISTER_INACTIVE "ounregister_inactive"
75 #define KEY_OUNREGISTER_FLAGS "ounregister_flags"
76 #define KEY_HANDLE_TS_MODE "account_timestamp_mode"
79 #define KEY_PASSWD "passwd"
80 #define KEY_NICKS "nicks"
81 #define KEY_MASKS "masks"
82 #define KEY_OPSERV_LEVEL "opserv_level"
83 #define KEY_FLAGS "flags"
84 #define KEY_REGISTER_ON "register"
85 #define KEY_LAST_SEEN "lastseen"
86 #define KEY_INFO "info"
87 #define KEY_USERLIST_STYLE "user_style"
88 #define KEY_SCREEN_WIDTH "screen_width"
89 #define KEY_LAST_AUTHED_HOST "last_authed_host"
90 #define KEY_LAST_QUIT_HOST "last_quit_host"
91 #define KEY_EMAIL_ADDR "email_addr"
92 #define KEY_COOKIE "cookie"
93 #define KEY_COOKIE_DATA "data"
94 #define KEY_COOKIE_TYPE "type"
95 #define KEY_COOKIE_EXPIRES "expires"
96 #define KEY_ACTIVATION "activation"
97 #define KEY_PASSWORD_CHANGE "password change"
98 #define KEY_EMAIL_CHANGE "email change"
99 #define KEY_ALLOWAUTH "allowauth"
100 #define KEY_EPITHET "epithet"
101 #define KEY_TABLE_WIDTH "table_width"
102 #define KEY_MAXLOGINS "maxlogins"
103 #define KEY_FAKEHOST "fakehost"
104 #define KEY_NOTES "notes"
105 #define KEY_NOTE_EXPIRES "expires"
106 #define KEY_NOTE_SET "set"
107 #define KEY_NOTE_SETTER "setter"
108 #define KEY_NOTE_NOTE "note"
109 #define KEY_KARMA "karma"
111 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
113 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
114 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
115 typedef OPTION_FUNC(option_func_t);
117 DEFINE_LIST(handle_info_list, struct handle_info*)
119 #define NICKSERV_MIN_PARMS(N) do { \
121 reply("MSG_MISSING_PARAMS", argv[0]); \
122 svccmd_send_help(user, nickserv, cmd); \
126 struct userNode *nickserv;
127 struct userList curr_helpers;
128 const char *handle_flags = HANDLE_FLAGS;
130 static struct module *nickserv_module;
131 static struct service *nickserv_service;
132 static struct log_type *NS_LOG;
133 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
134 static dict_t nickserv_id_dict; /* contains struct handle_info* */
135 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
136 static dict_t nickserv_opt_dict; /* contains option_func_t* */
137 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
138 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
139 static char handle_inverse_flags[256];
140 static unsigned int flag_access_levels[32];
141 static const struct message_entry msgtab[] = {
142 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
143 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
144 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
145 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
146 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
147 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
148 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
149 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
150 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
151 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
152 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
153 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
154 { "NSMSG_USE_COOKIE_REGISTER", "To activate your account, you must check your email for the \"cookie\" that has been mailed to it. When you have it, use the $bcookie$b command to complete registration." },
155 { "NSMSG_USE_COOKIE_RESETPASS", "A cookie has been mailed to your account's email address. You must check your email and use the $bcookie$b command to confirm the password change." },
156 { "NSMSG_USE_COOKIE_EMAIL_1", "A cookie has been mailed to the new address you requested. To finish setting your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
157 { "NSMSG_USE_COOKIE_EMAIL_2", "A cookie has been generated, and half mailed to each your old and new addresses. To finish changing your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
158 { "NSMSG_USE_COOKIE_AUTH", "A cookie has been generated and sent to your email address. Once you have checked your email and received the cookie, auth using the $bcookie$b command." },
159 { "NSMSG_COOKIE_LIVE", "Account $b%s$b already has a cookie active. Please either finish using that cookie, wait for it to expire, or auth to the account and use the $bdelcookie$b command." },
160 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
161 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
162 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
163 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
164 { "NSMSG_BAD_COOKIE", "That cookie is not the right one. Please make sure you are copying it EXACTLY from the email; it is case-sensitive, so $bABC$b is different from $babc$b." },
165 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
166 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
167 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
168 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
169 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
170 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
171 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
172 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
173 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
174 { "NSMSG_ATE_COOKIE_FOREIGN", "I ate the cookie for account $b%s$b. It may now have another." },
175 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
176 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
177 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
178 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
179 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
180 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
181 { "NSMSG_USE_AUTHCOOKIE", "Your hostmask is not valid for account $b%1$s$b. Please use the $bauthcookie$b command to grant yourself access. (/msg $S authcookie %1$s)" },
182 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
183 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
184 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
185 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
186 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
187 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
188 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
189 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
190 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
191 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
192 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
193 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
194 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
195 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
196 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
197 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
198 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
199 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
200 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
201 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
202 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
203 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
204 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
205 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
206 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
207 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
208 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
209 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
210 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
211 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
212 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
213 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
214 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
215 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
216 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
217 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
218 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
219 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
220 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
221 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
222 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
223 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
224 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
225 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
226 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
227 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
228 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
229 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
230 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
231 { "NSMSG_WEAK_PASSWORD", "WARNING: You are using a password that is considered weak (easy to guess). It is STRONGLY recommended you change it (now, if not sooner) by typing \"/msg $S@$s PASS oldpass newpass\" (with your current password and a new password)." },
232 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
233 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
234 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
235 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
236 { "NSMSG_AUTH_ALLOWED_MSG", "You may now authenticate to account $b%s$b by typing $b/msg $N@$s auth %s password$b (using your password). If you will be using this computer regularly, please type $b/msg $N addmask$b (AFTER you auth) to permanently add your hostmask." },
237 { "NSMSG_AUTH_ALLOWED_EMAIL", "You may also (after you auth) type $b/msg $N set email user@your.isp$b to set an email address. This will let you use the $bauthcookie$b command to be authenticated in the future." },
238 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
239 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
240 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
241 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
242 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
243 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
244 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
245 { "NSMSG_PASS_SUCCESS", "Password changed." },
246 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
247 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
248 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
249 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
250 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
251 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
252 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
253 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
254 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
255 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
256 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
257 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
258 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
259 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
260 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
261 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
262 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
263 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
264 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
265 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
266 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
267 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
268 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
269 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
270 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
271 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
272 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
273 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
274 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
275 { "NSMSG_NO_ACCESS", "Access denied." },
276 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
277 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
278 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
279 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
280 { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
281 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
282 { "NSMSG_BAD_HANDLE", "Account $b%s$b not registered because it is in use by a network service, is too long, or contains invalid characters." },
283 { "NSMSG_BAD_NICK", "Nickname $b%s$b not registered because it is in use by a network service, is too long, or contains invalid characters." },
284 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
285 { "NSMSG_FAIL_RENAME", "Account $b%s$b not renamed to $b%s$b because it is in use by a network services, or contains invalid characters." },
286 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
287 { "NSMSG_SEARCH_MATCH", "Match: %s" },
288 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
289 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
290 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
291 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
292 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
293 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
294 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
295 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
296 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
297 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
298 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
299 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
300 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
301 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
302 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
303 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
304 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
305 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
306 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
307 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
308 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
309 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
310 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
311 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
312 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
313 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
314 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
315 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
316 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
317 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
318 { "NSEMAIL_ACTIVATION_BODY", "This email has been sent to verify that this email address belongs to the person who tried to register an account on %1$s. Your cookie is:\n %2$s\nTo verify your email address and complete the account registration, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nThis command is only used once to complete your account registration, and never again. Once you have run this command, you will need to authenticate everytime you reconnect to the network. To do this, you will have to type this command every time you reconnect:\n /msg %3$s@%4$s AUTH %5$s your-password\n Please remember to fill in 'your-password' with the actual password you gave to us when you registered.\n\nIf you did NOT request this account, you do not need to do anything. Please contact the %1$s staff if you have questions, and be sure to check our website." },
319 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
320 { "NSEMAIL_PASSWORD_CHANGE_BODY", "This email has been sent to verify that you wish to change the password on your account %5$s. Your cookie is %2$s.\nTo complete the password change, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request your password to be changed, you do not need to do anything. Please contact the %1$s staff if you have questions." },
321 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
322 { "NSEMAIL_EMAIL_CHANGE_BODY_NEW", "This email has been sent to verify that your email address belongs to the same person as account %5$s on %1$s. The SECOND HALF of your cookie is %2$.*6$s.\nTo verify your address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s ?????%2$.*6$s\n(Replace the ????? with the FIRST HALF of the cookie, as sent to your OLD email address.)\nIf you did NOT request this email address to be associated with this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
323 { "NSEMAIL_EMAIL_CHANGE_BODY_OLD", "This email has been sent to verify that you want to change your email for account %5$s on %1$s from this address to %7$s. The FIRST HALF of your cookie is %2$.*6$s\nTo verify your new address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$.*6$s?????\n(Replace the ????? with the SECOND HALF of the cookie, as sent to your NEW email address.)\nIf you did NOT request this change of email address, you do not need to do anything. Please contact the %1$s staff if you have questions." },
324 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
325 { "NSEMAIL_EMAIL_VERIFY_BODY", "This email has been sent to verify that this address belongs to the same person as %5$s on %1$s. Your cookie is %2$s.\nTo verify your address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request this email address to be associated with this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
326 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
327 { "NSEMAIL_ALLOWAUTH_BODY", "This email has been sent to let you authenticate (auth) to account %5$s on %1$s. Your cookie is %2$s.\nTo auth to that account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request this authorization, you do not need to do anything. Please contact the %1$s staff if you have questions." },
328 { "CHECKPASS_YES", "Yes." },
329 { "CHECKPASS_NO", "No." },
330 { "CHECKEMAIL_NOT_SET", "No email set." },
331 { "CHECKEMAIL_YES", "Yes." },
332 { "CHECKEMAIL_NO", "No." },
336 enum reclaim_action {
342 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
343 static void nickserv_reclaim_p(void *data);
344 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
346 enum handle_ts_mode {
352 unsigned int disable_nicks : 1;
353 unsigned int valid_handle_regex_set : 1;
354 unsigned int valid_nick_regex_set : 1;
355 unsigned int autogag_enabled : 1;
356 unsigned int email_enabled : 1;
357 unsigned int email_required : 1;
358 unsigned int default_hostmask : 1;
359 unsigned int warn_nick_owned : 1;
360 unsigned int warn_clone_auth : 1;
361 unsigned long nicks_per_handle;
362 unsigned long password_min_length;
363 unsigned long password_min_digits;
364 unsigned long password_min_upper;
365 unsigned long password_min_lower;
366 unsigned long db_backup_frequency;
367 unsigned long handle_expire_frequency;
368 unsigned long autogag_duration;
369 unsigned long email_visible_level;
370 unsigned long cookie_timeout;
371 unsigned long handle_expire_delay;
372 unsigned long nochan_handle_expire_delay;
373 unsigned long modoper_level;
374 unsigned long set_epithet_level;
375 unsigned long set_title_level;
376 unsigned long set_fakehost_level;
377 unsigned long handles_per_email;
378 unsigned long email_search_level;
379 const char *network_name;
380 const char *titlehost_suffix;
381 regex_t valid_handle_regex;
382 regex_t valid_nick_regex;
383 dict_t weak_password_dict;
384 struct policer_params *auth_policer_params;
385 enum reclaim_action reclaim_action;
386 enum reclaim_action auto_reclaim_action;
387 enum handle_ts_mode handle_ts_mode;
388 unsigned long auto_reclaim_delay;
389 unsigned char default_maxlogins;
390 unsigned char hard_maxlogins;
391 unsigned long ounregister_inactive;
392 unsigned long ounregister_flags;
395 /* We have 2^32 unique account IDs to use. */
396 unsigned long int highest_id = 0;
398 #define WALK_NOTES(HANDLE, PREV, NOTE) \
399 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
400 if (NOTE->expires && NOTE->expires < now) { \
401 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
403 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
407 canonicalize_hostmask(char *mask)
409 char *out = mask, *temp;
410 if ((temp = strchr(mask, '!'))) {
412 while (*temp) *out++ = *temp++;
418 static struct handle_info *
419 register_handle(const char *handle, const char *passwd, unsigned long id)
421 struct handle_info *hi;
423 char id_base64[IDLEN + 1];
426 /* Assign a unique account ID to the account; note that 0 is
427 an invalid account ID. 1 is therefore the first account ID. */
429 id = 1 + highest_id++;
431 /* Note: highest_id is and must always be the highest ID. */
432 if (id > highest_id) {
436 inttobase64(id_base64, id, IDLEN);
438 /* Make sure an account with the same ID doesn't exist. If a
439 duplicate is found, log some details and assign a new one.
440 This should be impossible, but it never hurts to expect it. */
441 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
442 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
447 hi = calloc(1, sizeof(*hi));
448 hi->userlist_style = HI_DEFAULT_STYLE;
449 hi->handle = strdup(handle);
450 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
452 dict_insert(nickserv_handle_dict, hi->handle, hi);
455 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
461 register_nick(const char *nick, struct handle_info *owner)
463 struct nick_info *ni;
464 ni = malloc(sizeof(struct nick_info));
465 safestrncpy(ni->nick, nick, sizeof(ni->nick));
467 ni->next = owner->nicks;
469 dict_insert(nickserv_nick_dict, ni->nick, ni);
473 delete_nick(struct nick_info *ni)
475 struct nick_info *last, *next;
476 struct userNode *user;
477 /* Check to see if we should mark a user as unregistered. */
478 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
479 user->modes &= ~FLAGS_REGNICK;
482 /* Remove ni from the nick_info linked list. */
483 if (ni == ni->owner->nicks) {
484 ni->owner->nicks = ni->next;
486 last = ni->owner->nicks;
492 last->next = next->next;
494 dict_remove(nickserv_nick_dict, ni->nick);
497 static unreg_func_t *unreg_func_list;
498 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
501 reg_unreg_func(unreg_func_t func)
503 if (unreg_func_used == unreg_func_size) {
504 if (unreg_func_size) {
505 unreg_func_size <<= 1;
506 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
509 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
512 unreg_func_list[unreg_func_used++] = func;
516 nickserv_free_cookie(void *data)
518 struct handle_cookie *cookie = data;
519 if (cookie->hi) cookie->hi->cookie = NULL;
520 if (cookie->data) free(cookie->data);
525 free_handle_info(void *vhi)
527 struct handle_info *hi = vhi;
530 inttobase64(id, hi->id, IDLEN);
531 dict_remove(nickserv_id_dict, id);
533 free_string_list(hi->masks);
537 delete_nick(hi->nicks);
542 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
543 nickserv_free_cookie(hi->cookie);
546 struct handle_note *note = hi->notes;
547 hi->notes = note->next;
550 if (hi->email_addr) {
551 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
552 handle_info_list_remove(hil, hi);
554 dict_remove(nickserv_email_dict, hi->email_addr);
559 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
562 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
566 for (n=0; n<unreg_func_used; n++)
567 unreg_func_list[n](notify, hi);
569 set_user_handle_info(hi->users, NULL, 0);
571 if (nickserv_conf.disable_nicks)
572 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
574 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
576 dict_remove(nickserv_handle_dict, hi->handle);
580 get_handle_info(const char *handle)
582 return dict_find(nickserv_handle_dict, handle, 0);
586 get_nick_info(const char *nick)
588 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
592 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
597 for (nn=0; nn<channel->members.used; ++nn) {
598 mn = channel->members.list[nn];
599 if ((mn->user != except) && (mn->user->handle_info == handle))
606 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
607 if (!user->handle_info) {
609 send_message(user, bot, "MSG_AUTHENTICATE");
613 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
615 send_message(user, bot, "NSMSG_NO_ACCESS");
619 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
621 send_message(user, bot, "MSG_OPER_SUSPENDED");
625 if (user->handle_info->opserv_level < min_level) {
627 send_message(user, bot, "NSMSG_NO_ACCESS");
635 is_valid_handle(const char *handle)
637 struct userNode *user;
638 /* cant register a juped nick/service nick as handle, to prevent confusion */
639 user = GetUserH(handle);
640 if (user && IsLocal(user))
642 /* check against maximum length */
643 if (strlen(handle) > NICKSERV_HANDLE_LEN)
645 /* for consistency, only allow account names that could be nicks */
646 if (!is_valid_nick(handle))
648 /* disallow account names that look like bad words */
649 if (opserv_bad_channel(handle))
651 /* test either regex or containing all valid chars */
652 if (nickserv_conf.valid_handle_regex_set) {
653 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
656 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
657 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
661 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
666 is_registerable_nick(const char *nick)
668 /* make sure it could be used as an account name */
669 if (!is_valid_handle(nick))
672 if (strlen(nick) > NICKLEN)
674 /* test either regex or as valid handle */
675 if (nickserv_conf.valid_nick_regex_set) {
676 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
679 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
680 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
688 is_valid_email_addr(const char *email)
690 return strchr(email, '@') != NULL;
694 visible_email_addr(struct userNode *user, struct handle_info *hi)
696 if (hi->email_addr) {
697 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
698 return hi->email_addr;
708 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
710 struct handle_info *hi;
711 struct userNode *target;
715 if (!(hi = get_handle_info(++name))) {
716 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
721 if (!(target = GetUserH(name))) {
722 send_message(user, service, "MSG_NICK_UNKNOWN", name);
725 if (IsLocal(target)) {
726 if (IsService(target))
727 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
729 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
732 if (!(hi = target->handle_info)) {
733 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
741 oper_outranks(struct userNode *user, struct handle_info *hi) {
742 if (user->handle_info->opserv_level > hi->opserv_level)
744 if (user->handle_info->opserv_level == hi->opserv_level) {
745 if ((user->handle_info->opserv_level == 1000)
746 || (user->handle_info == hi)
747 || ((user->handle_info->opserv_level == 0)
748 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
749 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
753 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
757 static struct handle_info *
758 get_victim_oper(struct userNode *user, const char *target)
760 struct handle_info *hi;
761 if (!(hi = smart_get_handle_info(nickserv, user, target)))
763 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
764 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
767 return oper_outranks(user, hi) ? hi : NULL;
771 valid_user_for(struct userNode *user, struct handle_info *hi)
775 /* If no hostmasks on the account, allow it. */
776 if (!hi->masks->used)
778 /* If any hostmask matches, allow it. */
779 for (ii=0; ii<hi->masks->used; ii++)
780 if (user_matches_glob(user, hi->masks->list[ii], 0))
782 /* If they are allowauthed to this account, allow it (removing the aa). */
783 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
784 dict_remove(nickserv_allow_auth_dict, user->nick);
787 /* The user is not allowed to use this account. */
792 is_secure_password(const char *handle, const char *pass, struct userNode *user)
795 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
799 if (len < nickserv_conf.password_min_length) {
801 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
804 if (!irccasecmp(pass, handle)) {
806 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
809 dict_find(nickserv_conf.weak_password_dict, pass, &p);
812 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
815 for (i=0; i<len; i++) {
816 if (isdigit(pass[i]))
818 if (isupper(pass[i]))
820 if (islower(pass[i]))
823 if ((cnt_lower < nickserv_conf.password_min_lower)
824 || (cnt_upper < nickserv_conf.password_min_upper)
825 || (cnt_digits < nickserv_conf.password_min_digits)) {
827 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
833 static auth_func_t *auth_func_list;
834 static unsigned int auth_func_size = 0, auth_func_used = 0;
837 reg_auth_func(auth_func_t func)
839 if (auth_func_used == auth_func_size) {
840 if (auth_func_size) {
841 auth_func_size <<= 1;
842 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
845 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
848 auth_func_list[auth_func_used++] = func;
851 static handle_rename_func_t *rf_list;
852 static unsigned int rf_list_size, rf_list_used;
855 reg_handle_rename_func(handle_rename_func_t func)
857 if (rf_list_used == rf_list_size) {
860 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
863 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
866 rf_list[rf_list_used++] = func;
870 generate_fakehost(struct handle_info *handle)
872 extern const char *hidden_host_suffix;
873 static char buffer[HOSTLEN+1];
875 if (!handle->fakehost) {
876 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
878 } else if (handle->fakehost[0] == '.') {
879 /* A leading dot indicates the stored value is actually a title. */
880 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
883 return handle->fakehost;
887 apply_fakehost(struct handle_info *handle)
889 struct userNode *target;
894 fake = generate_fakehost(handle);
895 for (target = handle->users; target; target = target->next_authed)
896 assign_fakehost(target, fake, 1);
900 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
903 struct handle_info *old_info;
905 /* This can happen if somebody uses COOKIE while authed, or if
906 * they re-auth to their current handle (which is silly, but users
908 if (user->handle_info == hi)
911 if (user->handle_info) {
912 struct userNode *other;
915 userList_remove(&curr_helpers, user);
917 /* remove from next_authed linked list */
918 if (user->handle_info->users == user) {
919 user->handle_info->users = user->next_authed;
921 for (other = user->handle_info->users;
922 other->next_authed != user;
923 other = other->next_authed) ;
924 other->next_authed = user->next_authed;
926 /* if nobody left on old handle, and they're not an oper, remove !god */
927 if (!user->handle_info->users && !user->handle_info->opserv_level)
928 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
929 /* record them as being last seen at this time */
930 user->handle_info->lastseen = now;
931 /* and record their hostmask */
932 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
934 old_info = user->handle_info;
935 user->handle_info = hi;
936 if (hi && !hi->users && !hi->opserv_level)
937 HANDLE_CLEAR_FLAG(hi, HELPING);
938 for (n=0; (n<auth_func_used) && !user->dead; n++)
939 auth_func_list[n](user, old_info);
941 struct nick_info *ni;
943 HANDLE_CLEAR_FLAG(hi, FROZEN);
944 if (nickserv_conf.warn_clone_auth) {
945 struct userNode *other;
946 for (other = hi->users; other; other = other->next_authed)
947 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
949 user->next_authed = hi->users;
952 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
953 userList_append(&curr_helpers, user);
955 if (hi->fakehost || old_info)
959 if (!nickserv_conf.disable_nicks) {
960 struct nick_info *ni;
961 for (ni = hi->nicks; ni; ni = ni->next) {
962 if (!irccasecmp(user->nick, ni->nick)) {
963 user->modes |= FLAGS_REGNICK;
968 StampUser(user, hi->handle, hi->registered, hi->id);
971 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
972 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
974 /* We cannot clear the user's account ID, unfortunately. */
975 user->next_authed = NULL;
979 static struct handle_info*
980 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
982 struct handle_info *hi;
983 struct nick_info *ni;
984 char crypted[MD5_CRYPT_LENGTH];
986 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
987 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
991 if (!is_secure_password(handle, passwd, user))
994 cryptpass(passwd, crypted);
995 hi = register_handle(handle, crypted, 0);
996 hi->masks = alloc_string_list(1);
998 hi->language = lang_C;
999 hi->registered = now;
1001 hi->flags = HI_DEFAULT_FLAGS;
1002 if (settee && !no_auth)
1003 set_user_handle_info(settee, hi, 1);
1006 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1007 else if (nickserv_conf.disable_nicks)
1008 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1009 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1010 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1012 register_nick(user->nick, hi);
1013 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1015 if (settee && (user != settee))
1016 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1021 nickserv_bake_cookie(struct handle_cookie *cookie)
1023 cookie->hi->cookie = cookie;
1024 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1028 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1030 struct handle_cookie *cookie;
1031 char subject[128], body[4096], *misc;
1032 const char *netname, *fmt;
1036 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1040 cookie = calloc(1, sizeof(*cookie));
1042 cookie->type = type;
1043 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1044 cookie->expires = now + nickserv_conf.cookie_timeout;
1045 inttobase64(cookie->cookie, rand(), 5);
1046 inttobase64(cookie->cookie+5, rand(), 5);
1048 netname = nickserv_conf.network_name;
1051 switch (cookie->type) {
1053 hi->passwd[0] = 0; /* invalidate password */
1054 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1055 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1056 snprintf(subject, sizeof(subject), fmt, netname);
1057 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1058 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1061 case PASSWORD_CHANGE:
1062 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1063 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1064 snprintf(subject, sizeof(subject), fmt, netname);
1065 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1066 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1069 misc = hi->email_addr;
1070 hi->email_addr = cookie->data;
1072 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1073 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1074 snprintf(subject, sizeof(subject), fmt, netname);
1075 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1076 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1077 mail_send(nickserv, hi, subject, body, 1);
1078 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1079 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1081 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1082 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1083 snprintf(subject, sizeof(subject), fmt, netname);
1084 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1085 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1086 mail_send(nickserv, hi, subject, body, 1);
1089 hi->email_addr = misc;
1092 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1093 snprintf(subject, sizeof(subject), fmt, netname);
1094 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1095 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1096 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1099 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1103 mail_send(nickserv, hi, subject, body, first_time);
1104 nickserv_bake_cookie(cookie);
1108 nickserv_eat_cookie(struct handle_cookie *cookie)
1110 cookie->hi->cookie = NULL;
1111 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1112 nickserv_free_cookie(cookie);
1116 nickserv_free_email_addr(void *data)
1118 handle_info_list_clean(data);
1123 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1125 struct handle_info_list *hil;
1126 /* Remove from old handle_info_list ... */
1127 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1128 handle_info_list_remove(hil, hi);
1129 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1130 hi->email_addr = NULL;
1132 /* Add to the new list.. */
1133 if (new_email_addr) {
1134 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1135 hil = calloc(1, sizeof(*hil));
1136 hil->tag = strdup(new_email_addr);
1137 handle_info_list_init(hil);
1138 dict_insert(nickserv_email_dict, hil->tag, hil);
1140 handle_info_list_append(hil, hi);
1141 hi->email_addr = hil->tag;
1145 static NICKSERV_FUNC(cmd_register)
1148 struct handle_info *hi;
1149 const char *email_addr, *password;
1152 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1153 /* Require the first handle registered to belong to someone +o. */
1154 reply("NSMSG_REQUIRE_OPER");
1158 if (user->handle_info) {
1159 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1163 if (IsRegistering(user)) {
1164 reply("NSMSG_ALREADY_REGISTERING");
1168 if (IsStamped(user)) {
1169 /* Unauthenticated users might still have been stamped
1170 previously and could therefore have a hidden host;
1171 do not allow them to register a new account. */
1172 reply("NSMSG_STAMPED_REGISTER");
1176 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1178 if (!is_valid_handle(argv[1])) {
1179 reply("NSMSG_BAD_HANDLE", argv[1]);
1183 if ((argc >= 4) && nickserv_conf.email_enabled) {
1184 struct handle_info_list *hil;
1187 /* Remember email address. */
1188 email_addr = argv[3];
1190 /* Check that the email address looks valid.. */
1191 if (!is_valid_email_addr(email_addr)) {
1192 reply("NSMSG_BAD_EMAIL_ADDR");
1196 /* .. and that we are allowed to send to it. */
1197 if ((str = mail_prohibited_address(email_addr))) {
1198 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1202 /* If we do email verify, make sure we don't spam the address. */
1203 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1205 for (nn=0; nn<hil->used; nn++) {
1206 if (hil->list[nn]->cookie) {
1207 reply("NSMSG_EMAIL_UNACTIVATED");
1211 if (hil->used >= nickserv_conf.handles_per_email) {
1212 reply("NSMSG_EMAIL_OVERUSED");
1225 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1227 /* Add any masks they should get. */
1228 if (nickserv_conf.default_hostmask) {
1229 string_list_append(hi->masks, strdup("*@*"));
1231 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1232 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1233 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1236 /* If they're the first to register, give them level 1000. */
1237 if (dict_size(nickserv_handle_dict) == 1) {
1238 hi->opserv_level = 1000;
1239 reply("NSMSG_ROOT_HANDLE", argv[1]);
1242 /* Set their email address. */
1244 nickserv_set_email_addr(hi, email_addr);
1246 /* If they need to do email verification, tell them. */
1248 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1250 /* Set registering flag.. */
1251 user->modes |= FLAGS_REGISTERING;
1256 static NICKSERV_FUNC(cmd_oregister)
1259 struct userNode *settee;
1260 struct handle_info *hi;
1262 NICKSERV_MIN_PARMS(3);
1264 if (!is_valid_handle(argv[1])) {
1265 reply("NSMSG_BAD_HANDLE", argv[1]);
1272 } else if (strchr(argv[3], '@')) {
1273 mask = canonicalize_hostmask(strdup(argv[3]));
1275 settee = GetUserH(argv[4]);
1277 reply("MSG_NICK_UNKNOWN", argv[4]);
1284 } else if ((settee = GetUserH(argv[3]))) {
1285 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1287 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1290 if (settee && settee->handle_info) {
1291 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1295 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1300 string_list_append(hi->masks, mask);
1304 static NICKSERV_FUNC(cmd_handleinfo)
1307 unsigned int i, pos=0, herelen;
1308 struct userNode *target, *next_un;
1309 struct handle_info *hi;
1310 const char *nsmsg_none;
1314 if (!(hi = user->handle_info)) {
1315 reply("NSMSG_MUST_AUTH");
1318 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1322 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1323 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1324 feh = hi->registered;
1325 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1328 intervalString(buff, now - hi->lastseen, user->handle_info);
1329 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1331 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1334 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1335 if (HANDLE_FLAGGED(hi, FROZEN))
1336 reply("NSMSG_HANDLEINFO_VACATION");
1338 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1339 struct do_not_register *dnr;
1340 if ((dnr = chanserv_is_dnr(NULL, hi)))
1341 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1342 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1344 } else if (hi != user->handle_info)
1348 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1350 if (nickserv_conf.email_enabled)
1351 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1355 switch (hi->cookie->type) {
1356 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1357 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1358 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1359 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1360 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1365 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1366 reply("NSMSG_HANDLEINFO_ID", hi->id);
1368 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1370 reply("NSMSG_HANDLEINFO_NO_NOTES");
1372 struct handle_note *prev, *note;
1374 WALK_NOTES(hi, prev, note) {
1375 char set_time[INTERVALLEN];
1376 intervalString(set_time, now - note->set, user->handle_info);
1377 if (note->expires) {
1378 char exp_time[INTERVALLEN];
1379 intervalString(exp_time, note->expires - now, user->handle_info);
1380 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1382 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1389 unsigned long flen = 1;
1390 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1392 for (i=0, flen=1; handle_flags[i]; i++)
1393 if (hi->flags & 1 << i)
1394 flags[flen++] = handle_flags[i];
1396 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1398 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1401 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1402 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1403 || (hi->opserv_level > 0)) {
1404 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1408 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1410 if (hi->last_quit_host[0])
1411 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1413 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1415 if (nickserv_conf.disable_nicks) {
1416 /* nicks disabled; don't show anything about registered nicks */
1417 } else if (hi->nicks) {
1418 struct nick_info *ni, *next_ni;
1419 for (ni = hi->nicks; ni; ni = next_ni) {
1420 herelen = strlen(ni->nick);
1421 if (pos + herelen + 1 > ArrayLength(buff)) {
1423 goto print_nicks_buff;
1427 memcpy(buff+pos, ni->nick, herelen);
1428 pos += herelen; buff[pos++] = ' ';
1432 reply("NSMSG_HANDLEINFO_NICKS", buff);
1437 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1440 if (hi->masks->used) {
1441 for (i=0; i < hi->masks->used; i++) {
1442 herelen = strlen(hi->masks->list[i]);
1443 if (pos + herelen + 1 > ArrayLength(buff)) {
1445 goto print_mask_buff;
1447 memcpy(buff+pos, hi->masks->list[i], herelen);
1448 pos += herelen; buff[pos++] = ' ';
1449 if (i+1 == hi->masks->used) {
1452 reply("NSMSG_HANDLEINFO_MASKS", buff);
1457 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1461 struct userData *channel, *next;
1464 for (channel = hi->channels; channel; channel = next) {
1465 next = channel->u_next;
1466 name = channel->channel->channel->name;
1467 herelen = strlen(name);
1468 if (pos + herelen + 7 > ArrayLength(buff)) {
1470 goto print_chans_buff;
1472 if (IsUserSuspended(channel))
1474 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1478 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1483 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1486 for (target = hi->users; target; target = next_un) {
1487 herelen = strlen(target->nick);
1488 if (pos + herelen + 1 > ArrayLength(buff)) {
1490 goto print_cnick_buff;
1492 next_un = target->next_authed;
1494 memcpy(buff+pos, target->nick, herelen);
1495 pos += herelen; buff[pos++] = ' ';
1499 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1504 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1507 static NICKSERV_FUNC(cmd_userinfo)
1509 struct userNode *target;
1511 NICKSERV_MIN_PARMS(2);
1512 if (!(target = GetUserH(argv[1]))) {
1513 reply("MSG_NICK_UNKNOWN", argv[1]);
1516 if (target->handle_info)
1517 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1519 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1523 static NICKSERV_FUNC(cmd_nickinfo)
1525 struct nick_info *ni;
1527 NICKSERV_MIN_PARMS(2);
1528 if (!(ni = get_nick_info(argv[1]))) {
1529 reply("MSG_NICK_UNKNOWN", argv[1]);
1532 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1536 static NICKSERV_FUNC(cmd_notes)
1538 struct handle_info *hi;
1539 struct handle_note *prev, *note;
1542 NICKSERV_MIN_PARMS(2);
1543 if (!(hi = get_victim_oper(user, argv[1])))
1546 WALK_NOTES(hi, prev, note) {
1547 char set_time[INTERVALLEN];
1548 intervalString(set_time, now - note->set, user->handle_info);
1549 if (note->expires) {
1550 char exp_time[INTERVALLEN];
1551 intervalString(exp_time, note->expires - now, user->handle_info);
1552 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1554 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1558 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1562 static NICKSERV_FUNC(cmd_rename_handle)
1564 struct handle_info *hi;
1565 char msgbuf[MAXLEN], *old_handle;
1568 NICKSERV_MIN_PARMS(3);
1569 if (!(hi = get_victim_oper(user, argv[1])))
1571 if (!is_valid_handle(argv[2])) {
1572 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1575 if (get_handle_info(argv[2])) {
1576 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1580 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1581 hi->handle = strdup(argv[2]);
1582 dict_insert(nickserv_handle_dict, hi->handle, hi);
1583 for (nn=0; nn<rf_list_used; nn++)
1584 rf_list[nn](hi, old_handle);
1585 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1586 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1587 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1592 static failpw_func_t *failpw_func_list;
1593 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1596 reg_failpw_func(failpw_func_t func)
1598 if (failpw_func_used == failpw_func_size) {
1599 if (failpw_func_size) {
1600 failpw_func_size <<= 1;
1601 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1603 failpw_func_size = 8;
1604 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1607 failpw_func_list[failpw_func_used++] = func;
1610 static NICKSERV_FUNC(cmd_auth)
1612 int pw_arg, used, maxlogins;
1613 struct handle_info *hi;
1615 struct userNode *other;
1617 if (user->handle_info) {
1618 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1621 if (IsStamped(user)) {
1622 /* Unauthenticated users might still have been stamped
1623 previously and could therefore have a hidden host;
1624 do not allow them to authenticate. */
1625 reply("NSMSG_STAMPED_AUTH");
1629 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1631 } else if (argc == 2) {
1632 if (nickserv_conf.disable_nicks) {
1633 if (!(hi = get_handle_info(user->nick))) {
1634 reply("NSMSG_HANDLE_NOT_FOUND");
1638 /* try to look up their handle from their nick */
1639 struct nick_info *ni;
1640 ni = get_nick_info(user->nick);
1642 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1649 reply("MSG_MISSING_PARAMS", argv[0]);
1650 svccmd_send_help(user, nickserv, cmd);
1654 reply("NSMSG_HANDLE_NOT_FOUND");
1657 /* Responses from here on look up the language used by the handle they asked about. */
1658 passwd = argv[pw_arg];
1659 if (!valid_user_for(user, hi)) {
1660 if (hi->email_addr && nickserv_conf.email_enabled)
1661 send_message_type(4, user, cmd->parent->bot,
1662 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1665 send_message_type(4, user, cmd->parent->bot,
1666 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1668 argv[pw_arg] = "BADMASK";
1671 if (!checkpass(passwd, hi->passwd)) {
1673 send_message_type(4, user, cmd->parent->bot,
1674 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1675 argv[pw_arg] = "BADPASS";
1676 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1677 if (nickserv_conf.autogag_enabled) {
1678 if (!user->auth_policer.params) {
1679 user->auth_policer.last_req = now;
1680 user->auth_policer.params = nickserv_conf.auth_policer_params;
1682 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1684 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1685 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1686 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1688 argv[pw_arg] = "GAGGED";
1693 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1694 send_message_type(4, user, cmd->parent->bot,
1695 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1696 argv[pw_arg] = "SUSPENDED";
1699 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1700 for (used = 0, other = hi->users; other; other = other->next_authed) {
1701 if (++used >= maxlogins) {
1702 send_message_type(4, user, cmd->parent->bot,
1703 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1705 argv[pw_arg] = "MAXLOGINS";
1710 set_user_handle_info(user, hi, 1);
1711 if (nickserv_conf.email_required && !hi->email_addr)
1712 reply("NSMSG_PLEASE_SET_EMAIL");
1713 if (!is_secure_password(hi->handle, passwd, NULL))
1714 reply("NSMSG_WEAK_PASSWORD");
1715 if (hi->passwd[0] != '$')
1716 cryptpass(passwd, hi->passwd);
1717 if (!hi->masks->used) {
1719 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1720 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1721 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1723 argv[pw_arg] = "****";
1724 reply("NSMSG_AUTH_SUCCESS");
1728 static allowauth_func_t *allowauth_func_list;
1729 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1732 reg_allowauth_func(allowauth_func_t func)
1734 if (allowauth_func_used == allowauth_func_size) {
1735 if (allowauth_func_size) {
1736 allowauth_func_size <<= 1;
1737 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1739 allowauth_func_size = 8;
1740 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1743 allowauth_func_list[allowauth_func_used++] = func;
1746 static NICKSERV_FUNC(cmd_allowauth)
1748 struct userNode *target;
1749 struct handle_info *hi;
1752 NICKSERV_MIN_PARMS(2);
1753 if (!(target = GetUserH(argv[1]))) {
1754 reply("MSG_NICK_UNKNOWN", argv[1]);
1757 if (target->handle_info) {
1758 reply("NSMSG_USER_PREV_AUTH", target->nick);
1761 if (IsStamped(target)) {
1762 /* Unauthenticated users might still have been stamped
1763 previously and could therefore have a hidden host;
1764 do not allow them to authenticate to an account. */
1765 reply("NSMSG_USER_PREV_STAMP", target->nick);
1770 else if (!(hi = get_handle_info(argv[2]))) {
1771 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1775 if (hi->opserv_level > user->handle_info->opserv_level) {
1776 reply("MSG_USER_OUTRANKED", hi->handle);
1779 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1780 || (hi->opserv_level > 0))
1781 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1782 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1785 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1786 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1787 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1788 if (nickserv_conf.email_enabled)
1789 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1791 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1792 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1794 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1796 for (n=0; n<allowauth_func_used; n++)
1797 allowauth_func_list[n](user, target, hi);
1801 static NICKSERV_FUNC(cmd_authcookie)
1803 struct handle_info *hi;
1805 NICKSERV_MIN_PARMS(2);
1806 if (user->handle_info) {
1807 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1810 if (IsStamped(user)) {
1811 /* Unauthenticated users might still have been stamped
1812 previously and could therefore have a hidden host;
1813 do not allow them to authenticate to an account. */
1814 reply("NSMSG_STAMPED_AUTHCOOKIE");
1817 if (!(hi = get_handle_info(argv[1]))) {
1818 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1821 if (!hi->email_addr) {
1822 reply("MSG_SET_EMAIL_ADDR");
1825 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1829 static NICKSERV_FUNC(cmd_delcookie)
1831 struct handle_info *hi;
1833 hi = user->handle_info;
1835 reply("NSMSG_NO_COOKIE");
1838 switch (hi->cookie->type) {
1841 reply("NSMSG_MUST_TIME_OUT");
1844 nickserv_eat_cookie(hi->cookie);
1845 reply("NSMSG_ATE_COOKIE");
1851 static NICKSERV_FUNC(cmd_odelcookie)
1853 struct handle_info *hi;
1855 NICKSERV_MIN_PARMS(2);
1857 if (!(hi = get_victim_oper(user, argv[1])))
1861 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
1865 nickserv_eat_cookie(hi->cookie);
1866 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
1871 static NICKSERV_FUNC(cmd_resetpass)
1873 struct handle_info *hi;
1874 char crypted[MD5_CRYPT_LENGTH];
1876 NICKSERV_MIN_PARMS(3);
1877 if (user->handle_info) {
1878 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1881 if (IsStamped(user)) {
1882 /* Unauthenticated users might still have been stamped
1883 previously and could therefore have a hidden host;
1884 do not allow them to activate an account. */
1885 reply("NSMSG_STAMPED_RESETPASS");
1888 if (!(hi = get_handle_info(argv[1]))) {
1889 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1892 if (!hi->email_addr) {
1893 reply("MSG_SET_EMAIL_ADDR");
1896 cryptpass(argv[2], crypted);
1898 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1902 static NICKSERV_FUNC(cmd_cookie)
1904 struct handle_info *hi;
1907 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1910 NICKSERV_MIN_PARMS(3);
1911 if (!(hi = get_handle_info(argv[1]))) {
1912 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1918 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1919 reply("NSMSG_HANDLE_SUSPENDED");
1924 reply("NSMSG_NO_COOKIE");
1928 /* Check validity of operation before comparing cookie to
1929 * prohibit guessing by authed users. */
1930 if (user->handle_info
1931 && (hi->cookie->type != EMAIL_CHANGE)
1932 && (hi->cookie->type != PASSWORD_CHANGE)) {
1933 reply("NSMSG_CANNOT_COOKIE");
1937 if (strcmp(cookie, hi->cookie->cookie)) {
1938 reply("NSMSG_BAD_COOKIE");
1942 switch (hi->cookie->type) {
1944 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1945 set_user_handle_info(user, hi, 1);
1946 reply("NSMSG_HANDLE_ACTIVATED");
1948 case PASSWORD_CHANGE:
1949 set_user_handle_info(user, hi, 1);
1950 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1951 reply("NSMSG_PASSWORD_CHANGED");
1954 nickserv_set_email_addr(hi, hi->cookie->data);
1955 reply("NSMSG_EMAIL_CHANGED");
1958 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1959 set_user_handle_info(user, hi, 1);
1960 nickserv_addmask(user, hi, mask);
1961 reply("NSMSG_AUTH_SUCCESS");
1966 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1967 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1971 nickserv_eat_cookie(hi->cookie);
1976 static NICKSERV_FUNC(cmd_oregnick) {
1978 struct handle_info *target;
1979 struct nick_info *ni;
1981 NICKSERV_MIN_PARMS(3);
1982 if (!(target = modcmd_get_handle_info(user, argv[1])))
1985 if (!is_registerable_nick(nick)) {
1986 reply("NSMSG_BAD_NICK", nick);
1989 ni = dict_find(nickserv_nick_dict, nick, NULL);
1991 reply("NSMSG_NICK_EXISTS", nick);
1994 register_nick(nick, target);
1995 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1999 static NICKSERV_FUNC(cmd_regnick) {
2001 struct nick_info *ni;
2003 if (!is_registerable_nick(user->nick)) {
2004 reply("NSMSG_BAD_NICK", user->nick);
2007 /* count their nicks, see if it's too many */
2008 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2009 if (n >= nickserv_conf.nicks_per_handle) {
2010 reply("NSMSG_TOO_MANY_NICKS");
2013 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2015 reply("NSMSG_NICK_EXISTS", user->nick);
2018 register_nick(user->nick, user->handle_info);
2019 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2023 static NICKSERV_FUNC(cmd_pass)
2025 struct handle_info *hi;
2026 const char *old_pass, *new_pass;
2028 NICKSERV_MIN_PARMS(3);
2029 hi = user->handle_info;
2033 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2034 if (!checkpass(old_pass, hi->passwd)) {
2035 argv[1] = "BADPASS";
2036 reply("NSMSG_PASSWORD_INVALID");
2039 cryptpass(new_pass, hi->passwd);
2041 reply("NSMSG_PASS_SUCCESS");
2046 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2049 char *new_mask = canonicalize_hostmask(strdup(mask));
2050 for (i=0; i<hi->masks->used; i++) {
2051 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2052 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2057 string_list_append(hi->masks, new_mask);
2058 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2062 static NICKSERV_FUNC(cmd_addmask)
2065 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2066 int res = nickserv_addmask(user, user->handle_info, mask);
2070 if (!is_gline(argv[1])) {
2071 reply("NSMSG_MASK_INVALID", argv[1]);
2074 return nickserv_addmask(user, user->handle_info, argv[1]);
2078 static NICKSERV_FUNC(cmd_oaddmask)
2080 struct handle_info *hi;
2082 NICKSERV_MIN_PARMS(3);
2083 if (!(hi = get_victim_oper(user, argv[1])))
2085 return nickserv_addmask(user, hi, argv[2]);
2089 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2092 for (i=0; i<hi->masks->used; i++) {
2093 if (!strcmp(del_mask, hi->masks->list[i])) {
2094 char *old_mask = hi->masks->list[i];
2095 if (hi->masks->used == 1 && !force) {
2096 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2099 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2100 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2105 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2109 static NICKSERV_FUNC(cmd_delmask)
2111 NICKSERV_MIN_PARMS(2);
2112 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2115 static NICKSERV_FUNC(cmd_odelmask)
2117 struct handle_info *hi;
2118 NICKSERV_MIN_PARMS(3);
2119 if (!(hi = get_victim_oper(user, argv[1])))
2121 return nickserv_delmask(user, hi, argv[2], 1);
2125 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2126 unsigned int nn, add = 1, pos;
2127 unsigned long added, removed, flag;
2129 for (added=removed=nn=0; str[nn]; nn++) {
2131 case '+': add = 1; break;
2132 case '-': add = 0; break;
2134 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2135 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2138 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2139 /* cheesy avoidance of looking up the flag name.. */
2140 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2143 flag = 1 << (pos - 1);
2145 added |= flag, removed &= ~flag;
2147 removed |= flag, added &= ~flag;
2152 *premoved = removed;
2157 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2159 unsigned long before, after, added, removed;
2160 struct userNode *uNode;
2162 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2163 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2165 hi->flags = (hi->flags | added) & ~removed;
2166 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2168 /* Strip helping flag if they're only a support helper and not
2169 * currently in #support. */
2170 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2171 struct channelList *schannels;
2173 schannels = chanserv_support_channels();
2174 for (ii = 0; ii < schannels->used; ++ii)
2175 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2177 if (ii == schannels->used)
2178 HANDLE_CLEAR_FLAG(hi, HELPING);
2181 if (after && !before) {
2182 /* Add user to current helper list. */
2183 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2184 userList_append(&curr_helpers, uNode);
2185 } else if (!after && before) {
2186 /* Remove user from current helper list. */
2187 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2188 userList_remove(&curr_helpers, uNode);
2195 set_list(struct userNode *user, struct handle_info *hi, int override)
2199 char *set_display[] = {
2200 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2201 "EMAIL", "MAXLOGINS", "LANGUAGE"
2204 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2206 /* Do this so options are presented in a consistent order. */
2207 for (i = 0; i < ArrayLength(set_display); ++i)
2208 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2209 opt(user, hi, override, 0, NULL);
2212 static NICKSERV_FUNC(cmd_set)
2214 struct handle_info *hi;
2217 hi = user->handle_info;
2219 set_list(user, hi, 0);
2222 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2223 reply("NSMSG_INVALID_OPTION", argv[1]);
2226 return opt(user, hi, 0, argc-1, argv+1);
2229 static NICKSERV_FUNC(cmd_oset)
2231 struct handle_info *hi;
2232 struct svccmd *subcmd;
2234 char cmdname[MAXLEN];
2236 NICKSERV_MIN_PARMS(2);
2238 if (!(hi = get_victim_oper(user, argv[1])))
2242 set_list(user, hi, 0);
2246 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2247 reply("NSMSG_INVALID_OPTION", argv[2]);
2251 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2252 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2253 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2256 return opt(user, hi, 1, argc-2, argv+2);
2259 static OPTION_FUNC(opt_info)
2263 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2265 hi->infoline = NULL;
2267 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2271 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2272 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2276 static OPTION_FUNC(opt_width)
2279 hi->screen_width = strtoul(argv[1], NULL, 0);
2281 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2282 hi->screen_width = MIN_LINE_SIZE;
2283 else if (hi->screen_width > MAX_LINE_SIZE)
2284 hi->screen_width = MAX_LINE_SIZE;
2286 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2290 static OPTION_FUNC(opt_tablewidth)
2293 hi->table_width = strtoul(argv[1], NULL, 0);
2295 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2296 hi->table_width = MIN_LINE_SIZE;
2297 else if (hi->screen_width > MAX_LINE_SIZE)
2298 hi->table_width = MAX_LINE_SIZE;
2300 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2304 static OPTION_FUNC(opt_color)
2307 if (enabled_string(argv[1]))
2308 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2309 else if (disabled_string(argv[1]))
2310 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2312 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2317 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2321 static OPTION_FUNC(opt_privmsg)
2324 if (enabled_string(argv[1]))
2325 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2326 else if (disabled_string(argv[1]))
2327 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2329 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2334 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2338 static OPTION_FUNC(opt_style)
2343 if (!irccasecmp(argv[1], "Zoot"))
2344 hi->userlist_style = HI_STYLE_ZOOT;
2345 else if (!irccasecmp(argv[1], "def"))
2346 hi->userlist_style = HI_STYLE_DEF;
2349 switch (hi->userlist_style) {
2358 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2362 static OPTION_FUNC(opt_password)
2365 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2370 cryptpass(argv[1], hi->passwd);
2372 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2376 static OPTION_FUNC(opt_flags)
2379 unsigned int ii, flen;
2382 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2387 nickserv_apply_flags(user, hi, argv[1]);
2389 for (ii = flen = 0; handle_flags[ii]; ii++)
2390 if (hi->flags & (1 << ii))
2391 flags[flen++] = handle_flags[ii];
2394 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2396 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2400 static OPTION_FUNC(opt_email)
2404 if (!is_valid_email_addr(argv[1])) {
2405 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2408 if ((str = mail_prohibited_address(argv[1]))) {
2409 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2412 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2413 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2415 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2417 nickserv_set_email_addr(hi, argv[1]);
2419 nickserv_eat_cookie(hi->cookie);
2420 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2423 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2427 static OPTION_FUNC(opt_maxlogins)
2429 unsigned char maxlogins;
2431 maxlogins = strtoul(argv[1], NULL, 0);
2432 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2433 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2436 hi->maxlogins = maxlogins;
2438 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2439 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2443 static OPTION_FUNC(opt_language)
2445 struct language *lang;
2447 lang = language_find(argv[1]);
2448 if (irccasecmp(lang->name, argv[1]))
2449 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2450 hi->language = lang;
2452 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2456 static OPTION_FUNC(opt_karma)
2459 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2464 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2465 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2466 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2467 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2469 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2473 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2478 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2479 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2481 if ((user->handle_info->opserv_level < target->opserv_level)
2482 || ((user->handle_info->opserv_level == target->opserv_level)
2483 && (user->handle_info->opserv_level < 1000))) {
2484 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2487 if ((user->handle_info->opserv_level < new_level)
2488 || ((user->handle_info->opserv_level == new_level)
2489 && (user->handle_info->opserv_level < 1000))) {
2490 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2493 if (user->handle_info == target) {
2494 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2497 if (target->opserv_level == new_level)
2499 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2500 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2501 target->opserv_level = new_level;
2505 static OPTION_FUNC(opt_level)
2510 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2514 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2515 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2519 static OPTION_FUNC(opt_epithet)
2522 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2526 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2527 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2530 if ((epithet[0] == '*') && !epithet[1])
2533 hi->epithet = strdup(epithet);
2537 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2539 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2543 static OPTION_FUNC(opt_title)
2548 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2552 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2554 if (strchr(title, '.')) {
2555 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2558 if ((strlen(user->handle_info->handle) + strlen(title) +
2559 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2560 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2565 if (!strcmp(title, "*")) {
2566 hi->fakehost = NULL;
2568 hi->fakehost = malloc(strlen(title)+2);
2569 hi->fakehost[0] = '.';
2570 strcpy(hi->fakehost+1, title);
2573 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2574 title = hi->fakehost + 1;
2578 title = user_find_message(user, "MSG_NONE");
2579 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2583 static OPTION_FUNC(opt_fakehost)
2588 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2592 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2594 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2595 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2599 if (!strcmp(fake, "*"))
2600 hi->fakehost = NULL;
2602 hi->fakehost = strdup(fake);
2603 fake = hi->fakehost;
2606 fake = generate_fakehost(hi);
2608 fake = user_find_message(user, "MSG_NONE");
2609 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2613 static NICKSERV_FUNC(cmd_reclaim)
2615 struct handle_info *hi;
2616 struct nick_info *ni;
2617 struct userNode *victim;
2619 NICKSERV_MIN_PARMS(2);
2620 hi = user->handle_info;
2621 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2623 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2626 if (ni->owner != user->handle_info) {
2627 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2630 victim = GetUserH(ni->nick);
2632 reply("MSG_NICK_UNKNOWN", ni->nick);
2635 if (victim == user) {
2636 reply("NSMSG_NICK_USER_YOU");
2639 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2640 switch (nickserv_conf.reclaim_action) {
2641 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2642 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2643 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2644 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2649 static NICKSERV_FUNC(cmd_unregnick)
2652 struct handle_info *hi;
2653 struct nick_info *ni;
2655 hi = user->handle_info;
2656 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2657 ni = dict_find(nickserv_nick_dict, nick, NULL);
2659 reply("NSMSG_UNKNOWN_NICK", nick);
2662 if (hi != ni->owner) {
2663 reply("NSMSG_NOT_YOUR_NICK", nick);
2666 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2671 static NICKSERV_FUNC(cmd_ounregnick)
2673 struct nick_info *ni;
2675 NICKSERV_MIN_PARMS(2);
2676 if (!(ni = get_nick_info(argv[1]))) {
2677 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2680 if (!oper_outranks(user, ni->owner))
2682 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2687 static NICKSERV_FUNC(cmd_unregister)
2689 struct handle_info *hi;
2692 NICKSERV_MIN_PARMS(2);
2693 hi = user->handle_info;
2696 if (checkpass(passwd, hi->passwd)) {
2697 nickserv_unregister_handle(hi, user);
2700 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2701 reply("NSMSG_PASSWORD_INVALID");
2706 static NICKSERV_FUNC(cmd_ounregister)
2708 struct handle_info *hi;
2709 char reason[MAXLEN];
2712 NICKSERV_MIN_PARMS(2);
2713 if (!(hi = get_victim_oper(user, argv[1])))
2716 if (HANDLE_FLAGGED(hi, NODELETE)) {
2717 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
2721 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
2723 ((hi->flags & nickserv_conf.ounregister_flags)
2725 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
2726 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
2730 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2731 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2732 nickserv_unregister_handle(hi, user);
2736 static NICKSERV_FUNC(cmd_status)
2738 if (nickserv_conf.disable_nicks) {
2739 reply("NSMSG_GLOBAL_STATS_NONICK",
2740 dict_size(nickserv_handle_dict));
2742 if (user->handle_info) {
2744 struct nick_info *ni;
2745 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2746 reply("NSMSG_HANDLE_STATS", cnt);
2748 reply("NSMSG_HANDLE_NONE");
2750 reply("NSMSG_GLOBAL_STATS",
2751 dict_size(nickserv_handle_dict),
2752 dict_size(nickserv_nick_dict));
2757 static NICKSERV_FUNC(cmd_ghost)
2759 struct userNode *target;
2760 char reason[MAXLEN];
2762 NICKSERV_MIN_PARMS(2);
2763 if (!(target = GetUserH(argv[1]))) {
2764 reply("MSG_NICK_UNKNOWN", argv[1]);
2767 if (target == user) {
2768 reply("NSMSG_CANNOT_GHOST_SELF");
2771 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2772 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2775 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2776 DelUser(target, nickserv, 1, reason);
2777 reply("NSMSG_GHOST_KILLED", argv[1]);
2781 static NICKSERV_FUNC(cmd_vacation)
2783 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2784 reply("NSMSG_ON_VACATION");
2788 static NICKSERV_FUNC(cmd_addnote)
2790 struct handle_info *hi;
2791 unsigned long duration;
2794 struct handle_note *prev;
2795 struct handle_note *note;
2797 /* Parse parameters and figure out values for note's fields. */
2798 NICKSERV_MIN_PARMS(4);
2799 hi = get_victim_oper(user, argv[1]);
2802 if(!strcmp(argv[2], "0"))
2804 else if(!(duration = ParseInterval(argv[2])))
2806 reply("MSG_INVALID_DURATION", argv[2]);
2809 if (duration > 2*365*86400) {
2810 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2813 unsplit_string(argv + 3, argc - 3, text);
2814 WALK_NOTES(hi, prev, note) {}
2815 id = prev ? (prev->id + 1) : 1;
2817 /* Create the new note structure. */
2818 note = calloc(1, sizeof(*note) + strlen(text));
2820 note->expires = duration ? (now + duration) : 0;
2823 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2824 strcpy(note->note, text);
2829 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2833 static NICKSERV_FUNC(cmd_delnote)
2835 struct handle_info *hi;
2836 struct handle_note *prev;
2837 struct handle_note *note;
2840 NICKSERV_MIN_PARMS(3);
2841 hi = get_victim_oper(user, argv[1]);
2844 id = strtoul(argv[2], NULL, 10);
2845 WALK_NOTES(hi, prev, note) {
2846 if (id == note->id) {
2848 prev->next = note->next;
2850 hi->notes = note->next;
2852 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2856 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2861 nickserv_saxdb_write(struct saxdb_context *ctx) {
2863 struct handle_info *hi;
2866 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2868 assert(hi->id != 0);
2869 saxdb_start_record(ctx, iter_key(it), 0);
2871 struct handle_cookie *cookie = hi->cookie;
2874 switch (cookie->type) {
2875 case ACTIVATION: type = KEY_ACTIVATION; break;
2876 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2877 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2878 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2879 default: type = NULL; break;
2882 saxdb_start_record(ctx, KEY_COOKIE, 0);
2883 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2884 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2886 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2887 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2888 saxdb_end_record(ctx);
2892 struct handle_note *prev, *note;
2893 saxdb_start_record(ctx, KEY_NOTES, 0);
2894 WALK_NOTES(hi, prev, note) {
2895 snprintf(flags, sizeof(flags), "%d", note->id);
2896 saxdb_start_record(ctx, flags, 0);
2898 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
2899 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
2900 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
2901 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
2902 saxdb_end_record(ctx);
2904 saxdb_end_record(ctx);
2907 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2909 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2911 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2915 for (ii=flen=0; handle_flags[ii]; ++ii)
2916 if (hi->flags & (1 << ii))
2917 flags[flen++] = handle_flags[ii];
2919 saxdb_write_string(ctx, KEY_FLAGS, flags);
2921 saxdb_write_int(ctx, KEY_ID, hi->id);
2923 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2924 if (hi->last_quit_host[0])
2925 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2926 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2928 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
2929 if (hi->masks->used)
2930 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2932 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2934 struct string_list *slist;
2935 struct nick_info *ni;
2937 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2938 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2939 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2943 if (hi->opserv_level)
2944 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2945 if (hi->language != lang_C)
2946 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2947 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2948 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2949 if (hi->screen_width)
2950 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2951 if (hi->table_width)
2952 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2953 flags[0] = hi->userlist_style;
2955 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2956 saxdb_end_record(ctx);
2961 static handle_merge_func_t *handle_merge_func_list;
2962 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2965 reg_handle_merge_func(handle_merge_func_t func)
2967 if (handle_merge_func_used == handle_merge_func_size) {
2968 if (handle_merge_func_size) {
2969 handle_merge_func_size <<= 1;
2970 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2972 handle_merge_func_size = 8;
2973 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2976 handle_merge_func_list[handle_merge_func_used++] = func;
2979 static NICKSERV_FUNC(cmd_merge)
2981 struct handle_info *hi_from, *hi_to;
2982 struct userNode *last_user;
2983 struct userData *cList, *cListNext;
2984 unsigned int ii, jj, n;
2985 char buffer[MAXLEN];
2987 NICKSERV_MIN_PARMS(3);
2989 if (!(hi_from = get_victim_oper(user, argv[1])))
2991 if (!(hi_to = get_victim_oper(user, argv[2])))
2993 if (hi_to == hi_from) {
2994 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2998 for (n=0; n<handle_merge_func_used; n++)
2999 handle_merge_func_list[n](user, hi_to, hi_from);
3001 /* Append "from" handle's nicks to "to" handle's nick list. */
3003 struct nick_info *last_ni;
3004 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3005 last_ni->next = hi_from->nicks;
3007 while (hi_from->nicks) {
3008 hi_from->nicks->owner = hi_to;
3009 hi_from->nicks = hi_from->nicks->next;
3012 /* Merge the hostmasks. */
3013 for (ii=0; ii<hi_from->masks->used; ii++) {
3014 char *mask = hi_from->masks->list[ii];
3015 for (jj=0; jj<hi_to->masks->used; jj++)
3016 if (match_ircglobs(hi_to->masks->list[jj], mask))
3018 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3019 string_list_append(hi_to->masks, strdup(mask));
3022 /* Merge the lists of authed users. */
3024 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3025 last_user->next_authed = hi_from->users;
3027 hi_to->users = hi_from->users;
3029 /* Repoint the old "from" handle's users. */
3030 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3031 last_user->handle_info = hi_to;
3033 hi_from->users = NULL;
3035 /* Merge channel userlists. */
3036 for (cList=hi_from->channels; cList; cList=cListNext) {
3037 struct userData *cList2;
3038 cListNext = cList->u_next;
3039 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3040 if (cList->channel == cList2->channel)
3042 if (cList2 && (cList2->access >= cList->access)) {
3043 log_module(NS_LOG, LOG_INFO, "Merge: %s had only %d access in %s (versus %d for %s)", hi_from->handle, cList->access, cList->channel->channel->name, cList2->access, hi_to->handle);
3044 /* keep cList2 in hi_to; remove cList from hi_from */
3045 del_channel_user(cList, 1);
3048 log_module(NS_LOG, LOG_INFO, "Merge: %s had only %d access in %s (versus %d for %s)", hi_to->handle, cList2->access, cList->channel->channel->name, cList->access, hi_from->handle);
3049 /* remove the lower-ranking cList2 from hi_to */
3050 del_channel_user(cList2, 1);
3052 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3054 /* cList needs to be moved from hi_from to hi_to */
3055 cList->handle = hi_to;
3056 /* Remove from linked list for hi_from */
3057 assert(!cList->u_prev);
3058 hi_from->channels = cList->u_next;
3060 cList->u_next->u_prev = cList->u_prev;
3061 /* Add to linked list for hi_to */
3062 cList->u_prev = NULL;
3063 cList->u_next = hi_to->channels;
3064 if (hi_to->channels)
3065 hi_to->channels->u_prev = cList;
3066 hi_to->channels = cList;
3070 /* Do they get an OpServ level promotion? */
3071 if (hi_from->opserv_level > hi_to->opserv_level)
3072 hi_to->opserv_level = hi_from->opserv_level;
3074 /* What about last seen time? */
3075 if (hi_from->lastseen > hi_to->lastseen)
3076 hi_to->lastseen = hi_from->lastseen;
3078 /* New karma is the sum of the two original karmas. */
3079 hi_to->karma += hi_from->karma;
3081 /* Does a fakehost carry over? (This intentionally doesn't set it
3082 * for users previously attached to hi_to. They'll just have to
3085 if (hi_from->fakehost && !hi_to->fakehost)
3086 hi_to->fakehost = strdup(hi_from->fakehost);
3088 /* Notify of success. */
3089 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3090 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3091 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3093 /* Unregister the "from" handle. */
3094 nickserv_unregister_handle(hi_from, NULL);
3099 struct nickserv_discrim {
3100 unsigned long flags_on, flags_off;
3101 unsigned long min_registered, max_registered;
3102 unsigned long lastseen;
3104 int min_level, max_level;
3105 int min_karma, max_karma;
3106 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3107 const char *nickmask;
3108 const char *hostmask;
3109 const char *fakehostmask;
3110 const char *handlemask;
3111 const char *emailmask;
3114 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3116 struct discrim_apply_info {
3117 struct nickserv_discrim *discrim;
3118 discrim_search_func func;
3119 struct userNode *source;
3120 unsigned int matched;
3123 static struct nickserv_discrim *
3124 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3127 struct nickserv_discrim *discrim;
3129 discrim = malloc(sizeof(*discrim));
3130 memset(discrim, 0, sizeof(*discrim));
3131 discrim->min_level = 0;
3132 discrim->max_level = INT_MAX;
3133 discrim->limit = 50;
3134 discrim->min_registered = 0;
3135 discrim->max_registered = ULONG_MAX;
3136 discrim->lastseen = ULONG_MAX;
3137 discrim->min_karma = INT_MIN;
3138 discrim->max_karma = INT_MAX;
3140 for (i=0; i<argc; i++) {
3141 if (i == argc - 1) {
3142 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3145 if (!irccasecmp(argv[i], "limit")) {
3146 discrim->limit = strtoul(argv[++i], NULL, 0);
3147 } else if (!irccasecmp(argv[i], "flags")) {
3148 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3149 } else if (!irccasecmp(argv[i], "registered")) {
3150 const char *cmp = argv[++i];
3151 if (cmp[0] == '<') {
3152 if (cmp[1] == '=') {
3153 discrim->min_registered = now - ParseInterval(cmp+2);
3155 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3157 } else if (cmp[0] == '=') {
3158 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3159 } else if (cmp[0] == '>') {
3160 if (cmp[1] == '=') {
3161 discrim->max_registered = now - ParseInterval(cmp+2);
3163 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3166 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3168 } else if (!irccasecmp(argv[i], "seen")) {
3169 discrim->lastseen = now - ParseInterval(argv[++i]);
3170 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3171 discrim->nickmask = argv[++i];
3172 } else if (!irccasecmp(argv[i], "hostmask")) {
3174 if (!irccasecmp(argv[i], "exact")) {
3175 if (i == argc - 1) {
3176 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3179 discrim->hostmask_type = EXACT;
3180 } else if (!irccasecmp(argv[i], "subset")) {
3181 if (i == argc - 1) {
3182 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3185 discrim->hostmask_type = SUBSET;
3186 } else if (!irccasecmp(argv[i], "superset")) {
3187 if (i == argc - 1) {
3188 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3191 discrim->hostmask_type = SUPERSET;
3192 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3193 if (i == argc - 1) {
3194 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3197 discrim->hostmask_type = LASTQUIT;
3200 discrim->hostmask_type = SUPERSET;
3202 discrim->hostmask = argv[++i];
3203 } else if (!irccasecmp(argv[i], "fakehost")) {
3204 if (!irccasecmp(argv[++i], "*")) {
3205 discrim->fakehostmask = 0;
3207 discrim->fakehostmask = argv[i];
3209 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3210 if (!irccasecmp(argv[++i], "*")) {
3211 discrim->handlemask = 0;
3213 discrim->handlemask = argv[i];
3215 } else if (!irccasecmp(argv[i], "email")) {
3216 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3217 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3219 } else if (!irccasecmp(argv[++i], "*")) {
3220 discrim->emailmask = 0;
3222 discrim->emailmask = argv[i];
3224 } else if (!irccasecmp(argv[i], "access")) {
3225 const char *cmp = argv[++i];
3226 if (cmp[0] == '<') {
3227 if (discrim->min_level == 0) discrim->min_level = 1;
3228 if (cmp[1] == '=') {
3229 discrim->max_level = strtoul(cmp+2, NULL, 0);
3231 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3233 } else if (cmp[0] == '=') {
3234 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3235 } else if (cmp[0] == '>') {
3236 if (cmp[1] == '=') {
3237 discrim->min_level = strtoul(cmp+2, NULL, 0);
3239 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3242 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3244 } else if (!irccasecmp(argv[i], "karma")) {
3245 const char *cmp = argv[++i];
3246 if (cmp[0] == '<') {
3247 if (cmp[1] == '=') {
3248 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3250 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3252 } else if (cmp[0] == '=') {
3253 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3254 } else if (cmp[0] == '>') {
3255 if (cmp[1] == '=') {
3256 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3258 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3261 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3264 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3275 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3277 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3278 || (discrim->flags_off & hi->flags)
3279 || (discrim->min_registered > hi->registered)
3280 || (discrim->max_registered < hi->registered)
3281 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3282 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3283 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3284 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3285 || (discrim->min_level > hi->opserv_level)
3286 || (discrim->max_level < hi->opserv_level)
3287 || (discrim->min_karma > hi->karma)
3288 || (discrim->max_karma < hi->karma)
3292 if (discrim->hostmask) {
3294 for (i=0; i<hi->masks->used; i++) {
3295 const char *mask = hi->masks->list[i];
3296 if ((discrim->hostmask_type == SUBSET)
3297 && (match_ircglobs(discrim->hostmask, mask))) break;
3298 else if ((discrim->hostmask_type == EXACT)
3299 && !irccasecmp(discrim->hostmask, mask)) break;
3300 else if ((discrim->hostmask_type == SUPERSET)
3301 && (match_ircglobs(mask, discrim->hostmask))) break;
3302 else if ((discrim->hostmask_type == LASTQUIT)
3303 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3305 if (i==hi->masks->used) return 0;
3307 if (discrim->nickmask) {
3308 struct nick_info *nick = hi->nicks;
3310 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3313 if (!nick) return 0;
3319 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3321 dict_iterator_t it, next;
3322 unsigned int matched;
3324 for (it = dict_first(nickserv_handle_dict), matched = 0;
3325 it && (matched < discrim->limit);
3327 next = iter_next(it);
3328 if (nickserv_discrim_match(discrim, iter_data(it))) {
3329 dsf(source, iter_data(it));
3337 search_print_func(struct userNode *source, struct handle_info *match)
3339 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3343 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3348 search_unregister_func (struct userNode *source, struct handle_info *match)
3350 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3351 nickserv_unregister_handle(match, source);
3355 nickserv_sort_accounts_by_access(const void *a, const void *b)
3357 const struct handle_info *hi_a = *(const struct handle_info**)a;
3358 const struct handle_info *hi_b = *(const struct handle_info**)b;
3359 if (hi_a->opserv_level != hi_b->opserv_level)
3360 return hi_b->opserv_level - hi_a->opserv_level;
3361 return irccasecmp(hi_a->handle, hi_b->handle);
3365 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3367 struct handle_info_list hil;
3368 struct helpfile_table tbl;
3373 memset(&hil, 0, sizeof(hil));
3374 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3375 struct handle_info *hi = iter_data(it);
3376 if (hi->opserv_level)
3377 handle_info_list_append(&hil, hi);
3379 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3380 tbl.length = hil.used + 1;
3382 tbl.flags = TABLE_NO_FREE;
3383 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3384 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3387 for (ii = 0; ii < hil.used; ) {
3388 ary = malloc(tbl.width * sizeof(ary[0]));
3389 ary[0] = hil.list[ii]->handle;
3390 ary[1] = strtab(hil.list[ii]->opserv_level);
3391 tbl.contents[++ii] = ary;
3393 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3394 reply("MSG_MATCH_COUNT", hil.used);
3395 for (ii = 0; ii < hil.used; ii++)
3396 free(tbl.contents[ii]);
3401 static NICKSERV_FUNC(cmd_search)
3403 struct nickserv_discrim *discrim;
3404 discrim_search_func action;
3405 struct svccmd *subcmd;
3406 unsigned int matches;
3409 NICKSERV_MIN_PARMS(3);
3410 sprintf(buf, "search %s", argv[1]);
3411 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3412 if (!irccasecmp(argv[1], "print"))
3413 action = search_print_func;
3414 else if (!irccasecmp(argv[1], "count"))
3415 action = search_count_func;
3416 else if (!irccasecmp(argv[1], "unregister"))
3417 action = search_unregister_func;
3419 reply("NSMSG_INVALID_ACTION", argv[1]);
3423 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3426 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3430 if (action == search_print_func)
3431 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3432 else if (action == search_count_func)
3433 discrim->limit = INT_MAX;
3435 matches = nickserv_discrim_search(discrim, action, user);
3438 reply("MSG_MATCH_COUNT", matches);
3440 reply("MSG_NO_MATCHES");
3446 static MODCMD_FUNC(cmd_checkpass)
3448 struct handle_info *hi;
3450 NICKSERV_MIN_PARMS(3);
3451 if (!(hi = get_handle_info(argv[1]))) {
3452 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3455 if (checkpass(argv[2], hi->passwd))
3456 reply("CHECKPASS_YES");
3458 reply("CHECKPASS_NO");
3463 static MODCMD_FUNC(cmd_checkemail)
3465 struct handle_info *hi;
3467 NICKSERV_MIN_PARMS(3);
3468 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3471 if (!hi->email_addr)
3472 reply("CHECKEMAIL_NOT_SET");
3473 else if (!irccasecmp(argv[2], hi->email_addr))
3474 reply("CHECKEMAIL_YES");
3476 reply("CHECKEMAIL_NO");
3482 nickserv_db_read_handle(const char *handle, dict_t obj)
3485 struct string_list *masks, *slist;
3486 struct handle_info *hi;
3487 struct userNode *authed_users;
3488 struct userData *channels;
3493 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3494 id = str ? strtoul(str, NULL, 0) : 0;
3495 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3497 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3500 if ((hi = get_handle_info(handle))) {
3501 authed_users = hi->users;
3502 channels = hi->channels;
3504 hi->channels = NULL;
3505 dict_remove(nickserv_handle_dict, hi->handle);
3507 authed_users = NULL;
3510 hi = register_handle(handle, str, id);
3512 hi->users = authed_users;
3513 while (authed_users) {
3514 authed_users->handle_info = hi;
3515 authed_users = authed_users->next_authed;
3518 hi->channels = channels;
3519 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3520 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3521 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3522 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3523 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3524 hi->language = language_find(str ? str : "C");
3525 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3526 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3527 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3529 hi->infoline = strdup(str);
3530 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3531 hi->registered = str ? strtoul(str, NULL, 0) : now;
3532 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3533 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3534 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3535 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3536 /* We want to read the nicks even if disable_nicks is set. This is so
3537 * that we don't lose the nick data entirely. */
3538 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3540 for (ii=0; ii<slist->used; ii++)
3541 register_nick(slist->list[ii], hi);
3543 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3545 for (ii=0; str[ii]; ii++)
3546 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3548 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3549 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3550 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3551 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3552 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3553 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3554 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3556 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3558 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3559 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3561 nickserv_set_email_addr(hi, str);
3562 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3564 hi->epithet = strdup(str);
3565 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3567 hi->fakehost = strdup(str);
3568 /* Read the "cookie" sub-database (if it exists). */
3569 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3571 const char *data, *type, *expires, *cookie_str;
3572 struct handle_cookie *cookie;
3574 cookie = calloc(1, sizeof(*cookie));
3575 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3576 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3577 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3578 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3579 if (!type || !expires || !cookie_str) {
3580 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3583 if (!irccasecmp(type, KEY_ACTIVATION))
3584 cookie->type = ACTIVATION;
3585 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3586 cookie->type = PASSWORD_CHANGE;
3587 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3588 cookie->type = EMAIL_CHANGE;
3589 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3590 cookie->type = ALLOWAUTH;
3592 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3595 cookie->expires = strtoul(expires, NULL, 0);
3596 if (cookie->expires < now)
3599 cookie->data = strdup(data);
3600 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3604 nickserv_bake_cookie(cookie);
3606 nickserv_free_cookie(cookie);
3608 /* Read the "notes" sub-database (if it exists). */
3609 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3612 struct handle_note *last_note;
3613 struct handle_note *note;
3616 for (it = dict_first(subdb); it; it = iter_next(it)) {
3617 const char *expires;
3625 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3627 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", id, hi->handle);
3630 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3631 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3632 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3633 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3634 if (!setter || !text || !set) {
3635 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", id, hi->handle);
3638 note = calloc(1, sizeof(*note) + strlen(text));
3640 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3641 note->set = strtoul(set, NULL, 10);
3642 note->id = strtoul(id, NULL, 10);
3643 safestrncpy(note->setter, setter, sizeof(note->setter));
3644 strcpy(note->note, text);
3646 last_note->next = note;
3655 nickserv_saxdb_read(dict_t db) {
3657 struct record_data *rd;
3659 for (it=dict_first(db); it; it=iter_next(it)) {
3661 nickserv_db_read_handle(iter_key(it), rd->d.object);
3666 static NICKSERV_FUNC(cmd_mergedb)
3668 struct timeval start, stop;
3671 NICKSERV_MIN_PARMS(2);
3672 gettimeofday(&start, NULL);
3673 if (!(db = parse_database(argv[1]))) {
3674 reply("NSMSG_DB_UNREADABLE", argv[1]);
3677 nickserv_saxdb_read(db);
3679 gettimeofday(&stop, NULL);
3680 stop.tv_sec -= start.tv_sec;
3681 stop.tv_usec -= start.tv_usec;
3682 if (stop.tv_usec < 0) {
3684 stop.tv_usec += 1000000;
3686 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
3691 expire_handles(UNUSED_ARG(void *data))
3693 dict_iterator_t it, next;
3694 unsigned long expiry;
3695 struct handle_info *hi;
3697 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3698 next = iter_next(it);
3700 if ((hi->opserv_level > 0)
3702 || HANDLE_FLAGGED(hi, FROZEN)
3703 || HANDLE_FLAGGED(hi, NODELETE)) {
3706 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3707 if ((now - hi->lastseen) > expiry) {
3708 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3709 nickserv_unregister_handle(hi, NULL);
3713 if (nickserv_conf.handle_expire_frequency)
3714 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3718 nickserv_load_dict(const char *fname)
3722 if (!(file = fopen(fname, "r"))) {
3723 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3726 while (fgets(line, sizeof(line), file)) {
3729 if (line[strlen(line)-1] == '\n')
3730 line[strlen(line)-1] = 0;
3731 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3734 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3737 static enum reclaim_action
3738 reclaim_action_from_string(const char *str) {
3740 return RECLAIM_NONE;
3741 else if (!irccasecmp(str, "warn"))
3742 return RECLAIM_WARN;
3743 else if (!irccasecmp(str, "svsnick"))
3744 return RECLAIM_SVSNICK;
3745 else if (!irccasecmp(str, "kill"))
3746 return RECLAIM_KILL;
3748 return RECLAIM_NONE;
3752 nickserv_conf_read(void)
3754 dict_t conf_node, child;
3758 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3759 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3762 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3764 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3765 if (nickserv_conf.valid_handle_regex_set)
3766 regfree(&nickserv_conf.valid_handle_regex);
3768 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3769 nickserv_conf.valid_handle_regex_set = !err;
3770 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3772 nickserv_conf.valid_handle_regex_set = 0;
3774 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3775 if (nickserv_conf.valid_nick_regex_set)
3776 regfree(&nickserv_conf.valid_nick_regex);
3778 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3779 nickserv_conf.valid_nick_regex_set = !err;
3780 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3782 nickserv_conf.valid_nick_regex_set = 0;
3784 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3786 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3787 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3788 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3789 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3790 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3791 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3792 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3793 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3794 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3795 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3796 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3797 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3798 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3799 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3800 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3801 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3802 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3803 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3804 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3805 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3806 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3807 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3808 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3809 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3810 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3812 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3813 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3814 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3816 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3817 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3818 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3820 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3821 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3822 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3823 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3824 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3825 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3826 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3827 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3828 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
3829 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
3830 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
3833 nickserv_conf.ounregister_flags = 0;
3835 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
3838 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
3840 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
3842 nickserv_conf.handle_ts_mode = TS_IGNORE;
3843 else if (!irccasecmp(str, "ircu"))
3844 nickserv_conf.handle_ts_mode = TS_IRCU;
3846 nickserv_conf.handle_ts_mode = TS_IGNORE;
3847 if (!nickserv_conf.disable_nicks) {
3848 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3849 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3850 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3851 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3852 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3853 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3854 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3855 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3857 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3858 for (it=dict_first(child); it; it=iter_next(it)) {
3859 const char *key = iter_key(it), *value;
3863 if (!strncasecmp(key, "uc_", 3))
3864 flag = toupper(key[3]);
3865 else if (!strncasecmp(key, "lc_", 3))
3866 flag = tolower(key[3]);
3870 if ((pos = handle_inverse_flags[flag])) {
3871 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3872 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3875 if (nickserv_conf.weak_password_dict)
3876 dict_delete(nickserv_conf.weak_password_dict);
3877 nickserv_conf.weak_password_dict = dict_new();
3878 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3879 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3880 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3881 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3883 nickserv_load_dict(str);
3884 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3885 if (nickserv && str)
3886 NickChange(nickserv, str, 0);
3887 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3888 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3889 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3890 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3891 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3892 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3893 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3894 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3895 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3896 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3897 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3898 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3899 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3900 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3901 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3902 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3903 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3904 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3905 str = conf_get_data("server/network", RECDB_QSTRING);
3906 nickserv_conf.network_name = str ? str : "some IRC network";
3907 if (!nickserv_conf.auth_policer_params) {
3908 nickserv_conf.auth_policer_params = policer_params_new();
3909 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3910 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3912 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3913 for (it=dict_first(child); it; it=iter_next(it))
3914 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3918 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3920 char newnick[NICKLEN+1];
3929 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3931 case RECLAIM_SVSNICK:
3933 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3934 } while (GetUserH(newnick));
3935 irc_svsnick(nickserv, user, newnick);
3938 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3939 DelUser(user, nickserv, 1, msg);
3945 nickserv_reclaim_p(void *data) {
3946 struct userNode *user = data;
3947 struct nick_info *ni = get_nick_info(user->nick);
3949 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3953 check_user_nick(struct userNode *user) {
3954 struct nick_info *ni;
3955 user->modes &= ~FLAGS_REGNICK;
3956 if (!(ni = get_nick_info(user->nick)))
3958 if (user->handle_info == ni->owner) {
3959 user->modes |= FLAGS_REGNICK;
3963 if (nickserv_conf.warn_nick_owned)
3964 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3965 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3967 if (nickserv_conf.auto_reclaim_delay)
3968 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3970 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3974 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
3976 struct handle_info *hi = NULL;
3979 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3980 if ((hi == NULL) && (serial != 0)) {
3982 inttobase64(id, serial, IDLEN);
3983 hi = dict_find(nickserv_id_dict, id, NULL);
3987 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
3988 && (timestamp != hi->registered)) {
3991 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3994 set_user_handle_info(user, hi, 0);
3996 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4001 handle_nick_change(struct userNode *user, const char *old_nick)
4003 struct handle_info *hi;
4005 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4006 dict_remove(nickserv_allow_auth_dict, old_nick);
4007 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4009 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4010 check_user_nick(user);
4014 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4016 dict_remove(nickserv_allow_auth_dict, user->nick);
4017 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4018 set_user_handle_info(user, NULL, 0);
4021 static struct modcmd *
4022 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4024 if (min_level > 0) {
4026 sprintf(buf, "%u", min_level);
4027 if (must_be_qualified) {
4028 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4030 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4032 } else if (min_level == 0) {
4033 if (must_be_qualified) {
4034 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4036 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4039 if (must_be_qualified) {
4040 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4042 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4048 nickserv_db_cleanup(void)
4050 unreg_del_user_func(nickserv_remove_user);
4051 userList_clean(&curr_helpers);
4052 policer_params_delete(nickserv_conf.auth_policer_params);
4053 dict_delete(nickserv_handle_dict);
4054 dict_delete(nickserv_nick_dict);
4055 dict_delete(nickserv_opt_dict);
4056 dict_delete(nickserv_allow_auth_dict);
4057 dict_delete(nickserv_email_dict);
4058 dict_delete(nickserv_id_dict);
4059 dict_delete(nickserv_conf.weak_password_dict);
4060 free(auth_func_list);
4061 free(unreg_func_list);
4063 free(allowauth_func_list);
4064 free(handle_merge_func_list);
4065 free(failpw_func_list);
4066 if (nickserv_conf.valid_handle_regex_set)
4067 regfree(&nickserv_conf.valid_handle_regex);
4068 if (nickserv_conf.valid_nick_regex_set)
4069 regfree(&nickserv_conf.valid_nick_regex);
4073 init_nickserv(const char *nick)
4076 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4077 reg_new_user_func(check_user_nick);
4078 reg_nick_change_func(handle_nick_change);
4079 reg_del_user_func(nickserv_remove_user);
4080 reg_account_func(handle_account);
4082 /* set up handle_inverse_flags */
4083 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4084 for (i=0; handle_flags[i]; i++) {
4085 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4086 flag_access_levels[i] = 0;
4089 conf_register_reload(nickserv_conf_read);
4090 nickserv_opt_dict = dict_new();
4091 nickserv_email_dict = dict_new();
4092 dict_set_free_keys(nickserv_email_dict, free);
4093 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4095 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4096 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4097 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4098 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4099 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4100 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4101 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4102 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4103 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4104 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4105 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4106 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4107 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4108 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4109 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4110 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4111 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4112 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4113 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4114 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4115 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4116 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4117 if (!nickserv_conf.disable_nicks) {
4118 /* nick management commands */
4119 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4120 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4121 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4122 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4123 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4124 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4126 if (nickserv_conf.email_enabled) {
4127 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4128 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4129 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4130 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4131 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4132 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4134 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4135 /* miscellaneous commands */
4136 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4137 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4138 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4139 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4140 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4141 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4143 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4144 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4145 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4146 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4147 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4148 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4149 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4150 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4151 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4152 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4153 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4154 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4155 if (nickserv_conf.titlehost_suffix) {
4156 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4157 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4159 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4160 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4161 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4162 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4164 nickserv_handle_dict = dict_new();
4165 dict_set_free_keys(nickserv_handle_dict, free);
4166 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4168 nickserv_id_dict = dict_new();
4169 dict_set_free_keys(nickserv_id_dict, free);
4171 nickserv_nick_dict = dict_new();
4172 dict_set_free_data(nickserv_nick_dict, free);
4174 nickserv_allow_auth_dict = dict_new();
4176 userList_init(&curr_helpers);
4179 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4180 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4181 nickserv_service = service_register(nickserv);
4183 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4184 reg_exit_func(nickserv_db_cleanup);
4185 if(nickserv_conf.handle_expire_frequency)
4186 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4187 message_register_table(msgtab);