1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2006 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"
78 #define KEY_PASSWD "passwd"
79 #define KEY_NICKS "nicks"
80 #define KEY_MASKS "masks"
81 #define KEY_OPSERV_LEVEL "opserv_level"
82 #define KEY_FLAGS "flags"
83 #define KEY_REGISTER_ON "register"
84 #define KEY_LAST_SEEN "lastseen"
85 #define KEY_INFO "info"
86 #define KEY_USERLIST_STYLE "user_style"
87 #define KEY_SCREEN_WIDTH "screen_width"
88 #define KEY_LAST_AUTHED_HOST "last_authed_host"
89 #define KEY_LAST_QUIT_HOST "last_quit_host"
90 #define KEY_EMAIL_ADDR "email_addr"
91 #define KEY_COOKIE "cookie"
92 #define KEY_COOKIE_DATA "data"
93 #define KEY_COOKIE_TYPE "type"
94 #define KEY_COOKIE_EXPIRES "expires"
95 #define KEY_ACTIVATION "activation"
96 #define KEY_PASSWORD_CHANGE "password change"
97 #define KEY_EMAIL_CHANGE "email change"
98 #define KEY_ALLOWAUTH "allowauth"
99 #define KEY_EPITHET "epithet"
100 #define KEY_TABLE_WIDTH "table_width"
101 #define KEY_MAXLOGINS "maxlogins"
102 #define KEY_FAKEHOST "fakehost"
103 #define KEY_NOTES "notes"
104 #define KEY_NOTE_EXPIRES "expires"
105 #define KEY_NOTE_SET "set"
106 #define KEY_NOTE_SETTER "setter"
107 #define KEY_NOTE_NOTE "note"
108 #define KEY_KARMA "karma"
110 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
112 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
113 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
114 typedef OPTION_FUNC(option_func_t);
116 DEFINE_LIST(handle_info_list, struct handle_info*);
118 #define NICKSERV_MIN_PARMS(N) do { \
120 reply("MSG_MISSING_PARAMS", argv[0]); \
121 svccmd_send_help(user, nickserv, cmd); \
125 struct userNode *nickserv;
126 struct userList curr_helpers;
127 const char *handle_flags = HANDLE_FLAGS;
129 static struct module *nickserv_module;
130 static struct service *nickserv_service;
131 static struct log_type *NS_LOG;
132 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
133 static dict_t nickserv_id_dict; /* contains struct handle_info* */
134 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
135 static dict_t nickserv_opt_dict; /* contains option_func_t* */
136 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
137 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
138 static char handle_inverse_flags[256];
139 static unsigned int flag_access_levels[32];
140 static const struct message_entry msgtab[] = {
141 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
142 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
143 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
144 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
145 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
146 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
147 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
148 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
149 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
150 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
151 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
152 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
153 { "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." },
154 { "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." },
155 { "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." },
156 { "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." },
157 { "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." },
158 { "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." },
159 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
160 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
161 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
162 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
163 { "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." },
164 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
165 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
166 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
167 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
168 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
169 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
170 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
171 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
172 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
173 { "NSMSG_ATE_COOKIE_FOREIGN", "I ate the cookie for account $b%s$b. It may now have another." },
174 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
175 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
176 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
177 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
178 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
179 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
180 { "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)" },
181 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
182 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
183 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
184 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
185 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
186 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
187 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
188 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
189 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
190 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
191 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
192 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
193 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
194 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
195 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
196 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
197 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
198 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
199 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
200 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
201 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
202 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
203 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
204 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
205 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
206 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
207 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
208 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
209 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
210 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
211 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
212 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
213 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
214 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
215 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
216 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
217 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
218 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
219 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
220 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
221 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
222 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
223 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
224 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
225 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
226 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
227 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
228 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
229 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
230 { "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)." },
231 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
232 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
233 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
234 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
235 { "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." },
236 { "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." },
237 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
238 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
239 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
240 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
241 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
242 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
243 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
244 { "NSMSG_PASS_SUCCESS", "Password changed." },
245 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
246 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
247 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
248 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
249 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
250 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
251 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
252 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
253 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
254 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
255 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
256 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
257 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
258 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
259 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
260 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
261 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
262 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
263 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
264 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
265 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
266 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
267 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
268 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
269 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
270 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
271 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
272 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
273 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
274 { "NSMSG_NO_ACCESS", "Access denied." },
275 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
276 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
277 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
278 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
279 { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
280 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
281 { "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." },
282 { "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." },
283 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
284 { "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." },
285 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
286 { "NSMSG_SEARCH_MATCH", "Match: %s" },
287 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
288 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
289 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
290 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
291 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
292 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
293 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
294 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
295 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
296 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
297 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
298 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
299 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
300 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
301 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
302 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
303 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
304 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
305 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
306 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
307 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
308 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
309 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
310 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
311 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
312 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
313 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
314 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
315 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
316 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
317 { "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." },
318 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
319 { "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." },
320 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
321 { "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." },
322 { "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." },
323 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
324 { "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." },
325 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
326 { "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." },
327 { "CHECKPASS_YES", "Yes." },
328 { "CHECKPASS_NO", "No." },
329 { "CHECKEMAIL_NOT_SET", "No email set." },
330 { "CHECKEMAIL_YES", "Yes." },
331 { "CHECKEMAIL_NO", "No." },
335 enum reclaim_action {
341 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
342 static void nickserv_reclaim_p(void *data);
343 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
346 unsigned int disable_nicks : 1;
347 unsigned int valid_handle_regex_set : 1;
348 unsigned int valid_nick_regex_set : 1;
349 unsigned int autogag_enabled : 1;
350 unsigned int email_enabled : 1;
351 unsigned int email_required : 1;
352 unsigned int default_hostmask : 1;
353 unsigned int warn_nick_owned : 1;
354 unsigned int warn_clone_auth : 1;
355 unsigned long nicks_per_handle;
356 unsigned long password_min_length;
357 unsigned long password_min_digits;
358 unsigned long password_min_upper;
359 unsigned long password_min_lower;
360 unsigned long db_backup_frequency;
361 unsigned long handle_expire_frequency;
362 unsigned long autogag_duration;
363 unsigned long email_visible_level;
364 unsigned long cookie_timeout;
365 unsigned long handle_expire_delay;
366 unsigned long nochan_handle_expire_delay;
367 unsigned long modoper_level;
368 unsigned long set_epithet_level;
369 unsigned long set_title_level;
370 unsigned long set_fakehost_level;
371 unsigned long handles_per_email;
372 unsigned long email_search_level;
373 const char *network_name;
374 const char *titlehost_suffix;
375 regex_t valid_handle_regex;
376 regex_t valid_nick_regex;
377 dict_t weak_password_dict;
378 struct policer_params *auth_policer_params;
379 enum reclaim_action reclaim_action;
380 enum reclaim_action auto_reclaim_action;
381 unsigned long auto_reclaim_delay;
382 unsigned char default_maxlogins;
383 unsigned char hard_maxlogins;
384 unsigned long ounregister_inactive;
385 unsigned long ounregister_flags;
388 /* We have 2^32 unique account IDs to use. */
389 unsigned long int highest_id = 0;
391 #define WALK_NOTES(HANDLE, PREV, NOTE) \
392 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
393 if (NOTE->expires && NOTE->expires < now) { \
394 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
396 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
400 canonicalize_hostmask(char *mask)
402 char *out = mask, *temp;
403 if ((temp = strchr(mask, '!'))) {
405 while (*temp) *out++ = *temp++;
411 static struct handle_info *
412 register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
414 struct handle_info *hi;
416 #ifdef WITH_PROTOCOL_BAHAMUT
417 char id_base64[IDLEN + 1];
420 /* Assign a unique account ID to the account; note that 0 is
421 an invalid account ID. 1 is therefore the first account ID. */
423 id = 1 + highest_id++;
425 /* Note: highest_id is and must always be the highest ID. */
426 if (id > highest_id) {
430 inttobase64(id_base64, id, IDLEN);
432 /* Make sure an account with the same ID doesn't exist. If a
433 duplicate is found, log some details and assign a new one.
434 This should be impossible, but it never hurts to expect it. */
435 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
436 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
442 hi = calloc(1, sizeof(*hi));
443 hi->userlist_style = HI_DEFAULT_STYLE;
444 hi->handle = strdup(handle);
445 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
447 dict_insert(nickserv_handle_dict, hi->handle, hi);
449 #ifdef WITH_PROTOCOL_BAHAMUT
451 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
458 register_nick(const char *nick, struct handle_info *owner)
460 struct nick_info *ni;
461 ni = malloc(sizeof(struct nick_info));
462 safestrncpy(ni->nick, nick, sizeof(ni->nick));
464 ni->next = owner->nicks;
466 dict_insert(nickserv_nick_dict, ni->nick, ni);
470 delete_nick(struct nick_info *ni)
472 struct nick_info *last, *next;
473 struct userNode *user;
474 /* Check to see if we should mark a user as unregistered. */
475 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
476 user->modes &= ~FLAGS_REGNICK;
479 /* Remove ni from the nick_info linked list. */
480 if (ni == ni->owner->nicks) {
481 ni->owner->nicks = ni->next;
483 last = ni->owner->nicks;
489 last->next = next->next;
491 dict_remove(nickserv_nick_dict, ni->nick);
494 static unreg_func_t *unreg_func_list;
495 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
498 reg_unreg_func(unreg_func_t func)
500 if (unreg_func_used == unreg_func_size) {
501 if (unreg_func_size) {
502 unreg_func_size <<= 1;
503 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
506 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
509 unreg_func_list[unreg_func_used++] = func;
513 nickserv_free_cookie(void *data)
515 struct handle_cookie *cookie = data;
516 if (cookie->hi) cookie->hi->cookie = NULL;
517 if (cookie->data) free(cookie->data);
522 free_handle_info(void *vhi)
524 struct handle_info *hi = vhi;
526 #ifdef WITH_PROTOCOL_BAHAMUT
529 inttobase64(id, hi->id, IDLEN);
530 dict_remove(nickserv_id_dict, id);
533 free_string_list(hi->masks);
537 delete_nick(hi->nicks);
542 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
543 nickserv_free_cookie(hi->cookie);
546 struct handle_note *note = hi->notes;
547 hi->notes = note->next;
550 if (hi->email_addr) {
551 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
552 handle_info_list_remove(hil, hi);
554 dict_remove(nickserv_email_dict, hi->email_addr);
559 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
562 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
566 for (n=0; n<unreg_func_used; n++)
567 unreg_func_list[n](notify, hi);
569 set_user_handle_info(hi->users, NULL, 0);
571 if (nickserv_conf.disable_nicks)
572 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
574 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
576 dict_remove(nickserv_handle_dict, hi->handle);
580 get_handle_info(const char *handle)
582 return dict_find(nickserv_handle_dict, handle, 0);
586 get_nick_info(const char *nick)
588 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
592 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
597 for (nn=0; nn<channel->members.used; ++nn) {
598 mn = channel->members.list[nn];
599 if ((mn->user != except) && (mn->user->handle_info == handle))
606 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
607 if (!user->handle_info) {
609 send_message(user, bot, "MSG_AUTHENTICATE");
613 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
615 send_message(user, bot, "NSMSG_NO_ACCESS");
619 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
621 send_message(user, bot, "MSG_OPER_SUSPENDED");
625 if (user->handle_info->opserv_level < min_level) {
627 send_message(user, bot, "NSMSG_NO_ACCESS");
635 is_valid_handle(const char *handle)
637 struct userNode *user;
638 /* cant register a juped nick/service nick as handle, to prevent confusion */
639 user = GetUserH(handle);
640 if (user && IsLocal(user))
642 /* check against maximum length */
643 if (strlen(handle) > NICKSERV_HANDLE_LEN)
645 /* for consistency, only allow account names that could be nicks */
646 if (!is_valid_nick(handle))
648 /* disallow account names that look like bad words */
649 if (opserv_bad_channel(handle))
651 /* test either regex or containing all valid chars */
652 if (nickserv_conf.valid_handle_regex_set) {
653 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
656 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
657 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
661 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
666 is_registerable_nick(const char *nick)
668 /* make sure it could be used as an account name */
669 if (!is_valid_handle(nick))
672 if (strlen(nick) > NICKLEN)
674 /* test either regex or as valid handle */
675 if (nickserv_conf.valid_nick_regex_set) {
676 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
679 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
680 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
688 is_valid_email_addr(const char *email)
690 return strchr(email, '@') != NULL;
694 visible_email_addr(struct userNode *user, struct handle_info *hi)
696 if (hi->email_addr) {
697 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
698 return hi->email_addr;
708 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
710 struct handle_info *hi;
711 struct userNode *target;
715 if (!(hi = get_handle_info(++name))) {
716 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
721 if (!(target = GetUserH(name))) {
722 send_message(user, service, "MSG_NICK_UNKNOWN", name);
725 if (IsLocal(target)) {
726 if (IsService(target))
727 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
729 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
732 if (!(hi = target->handle_info)) {
733 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
741 oper_outranks(struct userNode *user, struct handle_info *hi) {
742 if (user->handle_info->opserv_level > hi->opserv_level)
744 if (user->handle_info->opserv_level == hi->opserv_level) {
745 if ((user->handle_info->opserv_level == 1000)
746 || (user->handle_info == hi)
747 || ((user->handle_info->opserv_level == 0)
748 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
749 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
753 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
757 static struct handle_info *
758 get_victim_oper(struct userNode *user, const char *target)
760 struct handle_info *hi;
761 if (!(hi = smart_get_handle_info(nickserv, user, target)))
763 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
764 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
767 return oper_outranks(user, hi) ? hi : NULL;
771 valid_user_for(struct userNode *user, struct handle_info *hi)
775 /* If no hostmasks on the account, allow it. */
776 if (!hi->masks->used)
778 /* If any hostmask matches, allow it. */
779 for (ii=0; ii<hi->masks->used; ii++)
780 if (user_matches_glob(user, hi->masks->list[ii], 0))
782 /* If they are allowauthed to this account, allow it (removing the aa). */
783 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
784 dict_remove(nickserv_allow_auth_dict, user->nick);
787 /* The user is not allowed to use this account. */
792 is_secure_password(const char *handle, const char *pass, struct userNode *user)
795 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
799 if (len < nickserv_conf.password_min_length) {
801 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
804 if (!irccasecmp(pass, handle)) {
806 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
809 dict_find(nickserv_conf.weak_password_dict, pass, &p);
812 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
815 for (i=0; i<len; i++) {
816 if (isdigit(pass[i]))
818 if (isupper(pass[i]))
820 if (islower(pass[i]))
823 if ((cnt_lower < nickserv_conf.password_min_lower)
824 || (cnt_upper < nickserv_conf.password_min_upper)
825 || (cnt_digits < nickserv_conf.password_min_digits)) {
827 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
833 static auth_func_t *auth_func_list;
834 static unsigned int auth_func_size = 0, auth_func_used = 0;
837 reg_auth_func(auth_func_t func)
839 if (auth_func_used == auth_func_size) {
840 if (auth_func_size) {
841 auth_func_size <<= 1;
842 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
845 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
848 auth_func_list[auth_func_used++] = func;
851 static handle_rename_func_t *rf_list;
852 static unsigned int rf_list_size, rf_list_used;
855 reg_handle_rename_func(handle_rename_func_t func)
857 if (rf_list_used == rf_list_size) {
860 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
863 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
866 rf_list[rf_list_used++] = func;
870 generate_fakehost(struct handle_info *handle)
872 extern const char *hidden_host_suffix;
873 static char buffer[HOSTLEN+1];
875 if (!handle->fakehost) {
876 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
878 } else if (handle->fakehost[0] == '.') {
879 /* A leading dot indicates the stored value is actually a title. */
880 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
883 return handle->fakehost;
887 apply_fakehost(struct handle_info *handle)
889 struct userNode *target;
894 fake = generate_fakehost(handle);
895 for (target = handle->users; target; target = target->next_authed)
896 assign_fakehost(target, fake, 1);
900 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
903 struct handle_info *old_info;
905 /* This can happen if somebody uses COOKIE while authed, or if
906 * they re-auth to their current handle (which is silly, but users
908 if (user->handle_info == hi)
911 if (user->handle_info) {
912 struct userNode *other;
915 userList_remove(&curr_helpers, user);
917 /* remove from next_authed linked list */
918 if (user->handle_info->users == user) {
919 user->handle_info->users = user->next_authed;
921 for (other = user->handle_info->users;
922 other->next_authed != user;
923 other = other->next_authed) ;
924 other->next_authed = user->next_authed;
926 /* if nobody left on old handle, and they're not an oper, remove !god */
927 if (!user->handle_info->users && !user->handle_info->opserv_level)
928 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
929 /* record them as being last seen at this time */
930 user->handle_info->lastseen = now;
931 /* and record their hostmask */
932 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
934 old_info = user->handle_info;
935 user->handle_info = hi;
936 if (hi && !hi->users && !hi->opserv_level)
937 HANDLE_CLEAR_FLAG(hi, HELPING);
938 for (n=0; n<auth_func_used; n++)
939 auth_func_list[n](user, old_info);
941 struct nick_info *ni;
943 HANDLE_CLEAR_FLAG(hi, FROZEN);
944 if (nickserv_conf.warn_clone_auth) {
945 struct userNode *other;
946 for (other = hi->users; other; other = other->next_authed)
947 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
949 user->next_authed = hi->users;
952 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
953 userList_append(&curr_helpers, user);
955 if (hi->fakehost || old_info)
959 #ifdef WITH_PROTOCOL_BAHAMUT
960 /* Stamp users with their account ID. */
962 inttobase64(id, hi->id, IDLEN);
963 #elif WITH_PROTOCOL_P10
964 /* Stamp users with their account name. */
965 char *id = hi->handle;
967 const char *id = "???";
969 if (!nickserv_conf.disable_nicks) {
970 struct nick_info *ni;
971 for (ni = hi->nicks; ni; ni = ni->next) {
972 if (!irccasecmp(user->nick, ni->nick)) {
973 user->modes |= FLAGS_REGNICK;
981 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
982 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
984 /* We cannot clear the user's account ID, unfortunately. */
985 user->next_authed = NULL;
989 static struct handle_info*
990 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
992 struct handle_info *hi;
993 struct nick_info *ni;
994 char crypted[MD5_CRYPT_LENGTH];
996 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
997 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
1001 if (!is_secure_password(handle, passwd, user))
1004 cryptpass(passwd, crypted);
1005 hi = register_handle(handle, crypted, 0);
1006 hi->masks = alloc_string_list(1);
1008 hi->language = lang_C;
1009 hi->registered = now;
1011 hi->flags = HI_DEFAULT_FLAGS;
1012 if (settee && !no_auth)
1013 set_user_handle_info(settee, hi, 1);
1016 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1017 else if (nickserv_conf.disable_nicks)
1018 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1019 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1020 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1022 register_nick(user->nick, hi);
1023 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1025 if (settee && (user != settee))
1026 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1031 nickserv_bake_cookie(struct handle_cookie *cookie)
1033 cookie->hi->cookie = cookie;
1034 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1038 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1040 struct handle_cookie *cookie;
1041 char subject[128], body[4096], *misc;
1042 const char *netname, *fmt;
1046 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1050 cookie = calloc(1, sizeof(*cookie));
1052 cookie->type = type;
1053 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1054 cookie->expires = now + nickserv_conf.cookie_timeout;
1055 inttobase64(cookie->cookie, rand(), 5);
1056 inttobase64(cookie->cookie+5, rand(), 5);
1058 netname = nickserv_conf.network_name;
1061 switch (cookie->type) {
1063 hi->passwd[0] = 0; /* invalidate password */
1064 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1065 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1066 snprintf(subject, sizeof(subject), fmt, netname);
1067 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1068 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1071 case PASSWORD_CHANGE:
1072 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1073 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1074 snprintf(subject, sizeof(subject), fmt, netname);
1075 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1076 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1079 misc = hi->email_addr;
1080 hi->email_addr = cookie->data;
1082 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1083 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1084 snprintf(subject, sizeof(subject), fmt, netname);
1085 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1086 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1087 mail_send(nickserv, hi, subject, body, 1);
1088 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1089 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1091 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1092 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1093 snprintf(subject, sizeof(subject), fmt, netname);
1094 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1095 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1096 mail_send(nickserv, hi, subject, body, 1);
1099 hi->email_addr = misc;
1102 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1103 snprintf(subject, sizeof(subject), fmt, netname);
1104 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1105 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1106 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1109 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1113 mail_send(nickserv, hi, subject, body, first_time);
1114 nickserv_bake_cookie(cookie);
1118 nickserv_eat_cookie(struct handle_cookie *cookie)
1120 cookie->hi->cookie = NULL;
1121 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1122 nickserv_free_cookie(cookie);
1126 nickserv_free_email_addr(void *data)
1128 handle_info_list_clean(data);
1133 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1135 struct handle_info_list *hil;
1136 /* Remove from old handle_info_list ... */
1137 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1138 handle_info_list_remove(hil, hi);
1139 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1140 hi->email_addr = NULL;
1142 /* Add to the new list.. */
1143 if (new_email_addr) {
1144 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1145 hil = calloc(1, sizeof(*hil));
1146 hil->tag = strdup(new_email_addr);
1147 handle_info_list_init(hil);
1148 dict_insert(nickserv_email_dict, hil->tag, hil);
1150 handle_info_list_append(hil, hi);
1151 hi->email_addr = hil->tag;
1155 static NICKSERV_FUNC(cmd_register)
1158 struct handle_info *hi;
1159 const char *email_addr, *password;
1162 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1163 /* Require the first handle registered to belong to someone +o. */
1164 reply("NSMSG_REQUIRE_OPER");
1168 if (user->handle_info) {
1169 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1173 if (IsRegistering(user)) {
1174 reply("NSMSG_ALREADY_REGISTERING");
1178 if (IsStamped(user)) {
1179 /* Unauthenticated users might still have been stamped
1180 previously and could therefore have a hidden host;
1181 do not allow them to register a new account. */
1182 reply("NSMSG_STAMPED_REGISTER");
1186 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1188 if (!is_valid_handle(argv[1])) {
1189 reply("NSMSG_BAD_HANDLE", argv[1]);
1193 if ((argc >= 4) && nickserv_conf.email_enabled) {
1194 struct handle_info_list *hil;
1197 /* Remember email address. */
1198 email_addr = argv[3];
1200 /* Check that the email address looks valid.. */
1201 if (!is_valid_email_addr(email_addr)) {
1202 reply("NSMSG_BAD_EMAIL_ADDR");
1206 /* .. and that we are allowed to send to it. */
1207 if ((str = mail_prohibited_address(email_addr))) {
1208 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1212 /* If we do email verify, make sure we don't spam the address. */
1213 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1215 for (nn=0; nn<hil->used; nn++) {
1216 if (hil->list[nn]->cookie) {
1217 reply("NSMSG_EMAIL_UNACTIVATED");
1221 if (hil->used >= nickserv_conf.handles_per_email) {
1222 reply("NSMSG_EMAIL_OVERUSED");
1235 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1237 /* Add any masks they should get. */
1238 if (nickserv_conf.default_hostmask) {
1239 string_list_append(hi->masks, strdup("*@*"));
1241 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1242 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1243 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1246 /* If they're the first to register, give them level 1000. */
1247 if (dict_size(nickserv_handle_dict) == 1) {
1248 hi->opserv_level = 1000;
1249 reply("NSMSG_ROOT_HANDLE", argv[1]);
1252 /* Set their email address. */
1254 nickserv_set_email_addr(hi, email_addr);
1256 /* If they need to do email verification, tell them. */
1258 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1260 /* Set registering flag.. */
1261 user->modes |= FLAGS_REGISTERING;
1266 static NICKSERV_FUNC(cmd_oregister)
1269 struct userNode *settee;
1270 struct handle_info *hi;
1272 NICKSERV_MIN_PARMS(3);
1274 if (!is_valid_handle(argv[1])) {
1275 reply("NSMSG_BAD_HANDLE", argv[1]);
1282 } else if (strchr(argv[3], '@')) {
1283 mask = canonicalize_hostmask(strdup(argv[3]));
1285 settee = GetUserH(argv[4]);
1287 reply("MSG_NICK_UNKNOWN", argv[4]);
1294 } else if ((settee = GetUserH(argv[3]))) {
1295 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1297 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1300 if (settee && settee->handle_info) {
1301 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1305 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1310 string_list_append(hi->masks, mask);
1314 static NICKSERV_FUNC(cmd_handleinfo)
1317 unsigned int i, pos=0, herelen;
1318 struct userNode *target, *next_un;
1319 struct handle_info *hi;
1320 const char *nsmsg_none;
1324 if (!(hi = user->handle_info)) {
1325 reply("NSMSG_MUST_AUTH");
1328 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1332 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1333 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1334 #ifdef WITH_PROTOCOL_BAHAMUT
1335 reply("NSMSG_HANDLEINFO_ID", hi->id);
1337 feh = hi->registered;
1338 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1341 intervalString(buff, now - hi->lastseen, user->handle_info);
1342 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1344 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1347 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1348 if (HANDLE_FLAGGED(hi, FROZEN))
1349 reply("NSMSG_HANDLEINFO_VACATION");
1351 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1352 struct do_not_register *dnr;
1353 if ((dnr = chanserv_is_dnr(NULL, hi)))
1354 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1355 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1357 } else if (hi != user->handle_info)
1361 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1363 if (nickserv_conf.email_enabled)
1364 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1368 switch (hi->cookie->type) {
1369 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1370 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1371 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1372 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1373 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1378 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1380 reply("NSMSG_HANDLEINFO_NO_NOTES");
1382 struct handle_note *prev, *note;
1384 WALK_NOTES(hi, prev, note) {
1385 char set_time[INTERVALLEN];
1386 intervalString(set_time, now - note->set, user->handle_info);
1387 if (note->expires) {
1388 char exp_time[INTERVALLEN];
1389 intervalString(exp_time, note->expires - now, user->handle_info);
1390 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1392 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1399 unsigned long flen = 1;
1400 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1402 for (i=0, flen=1; handle_flags[i]; i++)
1403 if (hi->flags & 1 << i)
1404 flags[flen++] = handle_flags[i];
1406 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1408 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1411 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1412 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1413 || (hi->opserv_level > 0)) {
1414 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1418 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1420 if (hi->last_quit_host[0])
1421 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1423 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1425 if (nickserv_conf.disable_nicks) {
1426 /* nicks disabled; don't show anything about registered nicks */
1427 } else if (hi->nicks) {
1428 struct nick_info *ni, *next_ni;
1429 for (ni = hi->nicks; ni; ni = next_ni) {
1430 herelen = strlen(ni->nick);
1431 if (pos + herelen + 1 > ArrayLength(buff)) {
1433 goto print_nicks_buff;
1437 memcpy(buff+pos, ni->nick, herelen);
1438 pos += herelen; buff[pos++] = ' ';
1442 reply("NSMSG_HANDLEINFO_NICKS", buff);
1447 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1450 if (hi->masks->used) {
1451 for (i=0; i < hi->masks->used; i++) {
1452 herelen = strlen(hi->masks->list[i]);
1453 if (pos + herelen + 1 > ArrayLength(buff)) {
1455 goto print_mask_buff;
1457 memcpy(buff+pos, hi->masks->list[i], herelen);
1458 pos += herelen; buff[pos++] = ' ';
1459 if (i+1 == hi->masks->used) {
1462 reply("NSMSG_HANDLEINFO_MASKS", buff);
1467 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1471 struct userData *channel, *next;
1474 for (channel = hi->channels; channel; channel = next) {
1475 next = channel->u_next;
1476 name = channel->channel->channel->name;
1477 herelen = strlen(name);
1478 if (pos + herelen + 7 > ArrayLength(buff)) {
1480 goto print_chans_buff;
1482 if (IsUserSuspended(channel))
1484 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1488 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1493 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1496 for (target = hi->users; target; target = next_un) {
1497 herelen = strlen(target->nick);
1498 if (pos + herelen + 1 > ArrayLength(buff)) {
1500 goto print_cnick_buff;
1502 next_un = target->next_authed;
1504 memcpy(buff+pos, target->nick, herelen);
1505 pos += herelen; buff[pos++] = ' ';
1509 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1517 static NICKSERV_FUNC(cmd_userinfo)
1519 struct userNode *target;
1521 NICKSERV_MIN_PARMS(2);
1522 if (!(target = GetUserH(argv[1]))) {
1523 reply("MSG_NICK_UNKNOWN", argv[1]);
1526 if (target->handle_info)
1527 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1529 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1533 static NICKSERV_FUNC(cmd_nickinfo)
1535 struct nick_info *ni;
1537 NICKSERV_MIN_PARMS(2);
1538 if (!(ni = get_nick_info(argv[1]))) {
1539 reply("MSG_NICK_UNKNOWN", argv[1]);
1542 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1546 static NICKSERV_FUNC(cmd_notes)
1548 struct handle_info *hi;
1549 struct handle_note *prev, *note;
1552 NICKSERV_MIN_PARMS(2);
1553 if (!(hi = get_victim_oper(user, argv[1])))
1556 WALK_NOTES(hi, prev, note) {
1557 char set_time[INTERVALLEN];
1558 intervalString(set_time, now - note->set, user->handle_info);
1559 if (note->expires) {
1560 char exp_time[INTERVALLEN];
1561 intervalString(exp_time, note->expires - now, user->handle_info);
1562 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1564 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1568 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1572 static NICKSERV_FUNC(cmd_rename_handle)
1574 struct handle_info *hi;
1575 char msgbuf[MAXLEN], *old_handle;
1578 NICKSERV_MIN_PARMS(3);
1579 if (!(hi = get_victim_oper(user, argv[1])))
1581 if (!is_valid_handle(argv[2])) {
1582 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1585 if (get_handle_info(argv[2])) {
1586 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1590 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1591 hi->handle = strdup(argv[2]);
1592 dict_insert(nickserv_handle_dict, hi->handle, hi);
1593 for (nn=0; nn<rf_list_used; nn++)
1594 rf_list[nn](hi, old_handle);
1595 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1596 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1597 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1602 static failpw_func_t *failpw_func_list;
1603 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1606 reg_failpw_func(failpw_func_t func)
1608 if (failpw_func_used == failpw_func_size) {
1609 if (failpw_func_size) {
1610 failpw_func_size <<= 1;
1611 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1613 failpw_func_size = 8;
1614 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1617 failpw_func_list[failpw_func_used++] = func;
1620 static NICKSERV_FUNC(cmd_auth)
1622 int pw_arg, used, maxlogins;
1623 struct handle_info *hi;
1625 struct userNode *other;
1627 if (user->handle_info) {
1628 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1631 if (IsStamped(user)) {
1632 /* Unauthenticated users might still have been stamped
1633 previously and could therefore have a hidden host;
1634 do not allow them to authenticate. */
1635 reply("NSMSG_STAMPED_AUTH");
1639 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1641 } else if (argc == 2) {
1642 if (nickserv_conf.disable_nicks) {
1643 if (!(hi = get_handle_info(user->nick))) {
1644 reply("NSMSG_HANDLE_NOT_FOUND");
1648 /* try to look up their handle from their nick */
1649 struct nick_info *ni;
1650 ni = get_nick_info(user->nick);
1652 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1659 reply("MSG_MISSING_PARAMS", argv[0]);
1660 svccmd_send_help(user, nickserv, cmd);
1664 reply("NSMSG_HANDLE_NOT_FOUND");
1667 /* Responses from here on look up the language used by the handle they asked about. */
1668 passwd = argv[pw_arg];
1669 if (!valid_user_for(user, hi)) {
1670 if (hi->email_addr && nickserv_conf.email_enabled)
1671 send_message_type(4, user, cmd->parent->bot,
1672 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1675 send_message_type(4, user, cmd->parent->bot,
1676 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1678 argv[pw_arg] = "BADMASK";
1681 if (!checkpass(passwd, hi->passwd)) {
1683 send_message_type(4, user, cmd->parent->bot,
1684 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1685 argv[pw_arg] = "BADPASS";
1686 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1687 if (nickserv_conf.autogag_enabled) {
1688 if (!user->auth_policer.params) {
1689 user->auth_policer.last_req = now;
1690 user->auth_policer.params = nickserv_conf.auth_policer_params;
1692 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1694 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1695 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1696 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1698 argv[pw_arg] = "GAGGED";
1703 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1704 send_message_type(4, user, cmd->parent->bot,
1705 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1706 argv[pw_arg] = "SUSPENDED";
1709 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1710 for (used = 0, other = hi->users; other; other = other->next_authed) {
1711 if (++used >= maxlogins) {
1712 send_message_type(4, user, cmd->parent->bot,
1713 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1715 argv[pw_arg] = "MAXLOGINS";
1720 set_user_handle_info(user, hi, 1);
1721 if (nickserv_conf.email_required && !hi->email_addr)
1722 reply("NSMSG_PLEASE_SET_EMAIL");
1723 if (!is_secure_password(hi->handle, passwd, NULL))
1724 reply("NSMSG_WEAK_PASSWORD");
1725 if (hi->passwd[0] != '$')
1726 cryptpass(passwd, hi->passwd);
1727 if (!hi->masks->used) {
1729 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1730 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1731 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1733 argv[pw_arg] = "****";
1734 reply("NSMSG_AUTH_SUCCESS");
1738 static allowauth_func_t *allowauth_func_list;
1739 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1742 reg_allowauth_func(allowauth_func_t func)
1744 if (allowauth_func_used == allowauth_func_size) {
1745 if (allowauth_func_size) {
1746 allowauth_func_size <<= 1;
1747 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1749 allowauth_func_size = 8;
1750 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1753 allowauth_func_list[allowauth_func_used++] = func;
1756 static NICKSERV_FUNC(cmd_allowauth)
1758 struct userNode *target;
1759 struct handle_info *hi;
1762 NICKSERV_MIN_PARMS(2);
1763 if (!(target = GetUserH(argv[1]))) {
1764 reply("MSG_NICK_UNKNOWN", argv[1]);
1767 if (target->handle_info) {
1768 reply("NSMSG_USER_PREV_AUTH", target->nick);
1771 if (IsStamped(target)) {
1772 /* Unauthenticated users might still have been stamped
1773 previously and could therefore have a hidden host;
1774 do not allow them to authenticate to an account. */
1775 reply("NSMSG_USER_PREV_STAMP", target->nick);
1780 else if (!(hi = get_handle_info(argv[2]))) {
1781 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1785 if (hi->opserv_level > user->handle_info->opserv_level) {
1786 reply("MSG_USER_OUTRANKED", hi->handle);
1789 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1790 || (hi->opserv_level > 0))
1791 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1792 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1795 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1796 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1797 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1798 if (nickserv_conf.email_enabled)
1799 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1801 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1802 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1804 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1806 for (n=0; n<allowauth_func_used; n++)
1807 allowauth_func_list[n](user, target, hi);
1811 static NICKSERV_FUNC(cmd_authcookie)
1813 struct handle_info *hi;
1815 NICKSERV_MIN_PARMS(2);
1816 if (user->handle_info) {
1817 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1820 if (IsStamped(user)) {
1821 /* Unauthenticated users might still have been stamped
1822 previously and could therefore have a hidden host;
1823 do not allow them to authenticate to an account. */
1824 reply("NSMSG_STAMPED_AUTHCOOKIE");
1827 if (!(hi = get_handle_info(argv[1]))) {
1828 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1831 if (!hi->email_addr) {
1832 reply("MSG_SET_EMAIL_ADDR");
1835 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1839 static NICKSERV_FUNC(cmd_delcookie)
1841 struct handle_info *hi;
1843 hi = user->handle_info;
1845 reply("NSMSG_NO_COOKIE");
1848 switch (hi->cookie->type) {
1851 reply("NSMSG_MUST_TIME_OUT");
1854 nickserv_eat_cookie(hi->cookie);
1855 reply("NSMSG_ATE_COOKIE");
1861 static NICKSERV_FUNC(cmd_odelcookie)
1863 struct handle_info *hi;
1865 NICKSERV_MIN_PARMS(2);
1867 if (!(hi = get_victim_oper(user, argv[1])))
1871 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
1875 nickserv_eat_cookie(hi->cookie);
1876 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
1881 static NICKSERV_FUNC(cmd_resetpass)
1883 struct handle_info *hi;
1884 char crypted[MD5_CRYPT_LENGTH];
1886 NICKSERV_MIN_PARMS(3);
1887 if (user->handle_info) {
1888 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1891 if (IsStamped(user)) {
1892 /* Unauthenticated users might still have been stamped
1893 previously and could therefore have a hidden host;
1894 do not allow them to activate an account. */
1895 reply("NSMSG_STAMPED_RESETPASS");
1898 if (!(hi = get_handle_info(argv[1]))) {
1899 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1902 if (!hi->email_addr) {
1903 reply("MSG_SET_EMAIL_ADDR");
1906 cryptpass(argv[2], crypted);
1908 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1912 static NICKSERV_FUNC(cmd_cookie)
1914 struct handle_info *hi;
1917 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1920 NICKSERV_MIN_PARMS(3);
1921 if (!(hi = get_handle_info(argv[1]))) {
1922 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1928 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1929 reply("NSMSG_HANDLE_SUSPENDED");
1934 reply("NSMSG_NO_COOKIE");
1938 /* Check validity of operation before comparing cookie to
1939 * prohibit guessing by authed users. */
1940 if (user->handle_info
1941 && (hi->cookie->type != EMAIL_CHANGE)
1942 && (hi->cookie->type != PASSWORD_CHANGE)) {
1943 reply("NSMSG_CANNOT_COOKIE");
1947 if (strcmp(cookie, hi->cookie->cookie)) {
1948 reply("NSMSG_BAD_COOKIE");
1952 switch (hi->cookie->type) {
1954 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1955 set_user_handle_info(user, hi, 1);
1956 reply("NSMSG_HANDLE_ACTIVATED");
1958 case PASSWORD_CHANGE:
1959 set_user_handle_info(user, hi, 1);
1960 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1961 reply("NSMSG_PASSWORD_CHANGED");
1964 nickserv_set_email_addr(hi, hi->cookie->data);
1965 reply("NSMSG_EMAIL_CHANGED");
1968 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1969 set_user_handle_info(user, hi, 1);
1970 nickserv_addmask(user, hi, mask);
1971 reply("NSMSG_AUTH_SUCCESS");
1976 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1977 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1981 nickserv_eat_cookie(hi->cookie);
1986 static NICKSERV_FUNC(cmd_oregnick) {
1988 struct handle_info *target;
1989 struct nick_info *ni;
1991 NICKSERV_MIN_PARMS(3);
1992 if (!(target = modcmd_get_handle_info(user, argv[1])))
1995 if (!is_registerable_nick(nick)) {
1996 reply("NSMSG_BAD_NICK", nick);
1999 ni = dict_find(nickserv_nick_dict, nick, NULL);
2001 reply("NSMSG_NICK_EXISTS", nick);
2004 register_nick(nick, target);
2005 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2009 static NICKSERV_FUNC(cmd_regnick) {
2011 struct nick_info *ni;
2013 if (!is_registerable_nick(user->nick)) {
2014 reply("NSMSG_BAD_NICK", user->nick);
2017 /* count their nicks, see if it's too many */
2018 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2019 if (n >= nickserv_conf.nicks_per_handle) {
2020 reply("NSMSG_TOO_MANY_NICKS");
2023 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2025 reply("NSMSG_NICK_EXISTS", user->nick);
2028 register_nick(user->nick, user->handle_info);
2029 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2033 static NICKSERV_FUNC(cmd_pass)
2035 struct handle_info *hi;
2036 const char *old_pass, *new_pass;
2038 NICKSERV_MIN_PARMS(3);
2039 hi = user->handle_info;
2043 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2044 if (!checkpass(old_pass, hi->passwd)) {
2045 argv[1] = "BADPASS";
2046 reply("NSMSG_PASSWORD_INVALID");
2049 cryptpass(new_pass, hi->passwd);
2051 reply("NSMSG_PASS_SUCCESS");
2056 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2059 char *new_mask = canonicalize_hostmask(strdup(mask));
2060 for (i=0; i<hi->masks->used; i++) {
2061 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2062 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2067 string_list_append(hi->masks, new_mask);
2068 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2072 static NICKSERV_FUNC(cmd_addmask)
2075 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2076 int res = nickserv_addmask(user, user->handle_info, mask);
2080 if (!is_gline(argv[1])) {
2081 reply("NSMSG_MASK_INVALID", argv[1]);
2084 return nickserv_addmask(user, user->handle_info, argv[1]);
2088 static NICKSERV_FUNC(cmd_oaddmask)
2090 struct handle_info *hi;
2092 NICKSERV_MIN_PARMS(3);
2093 if (!(hi = get_victim_oper(user, argv[1])))
2095 return nickserv_addmask(user, hi, argv[2]);
2099 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2102 for (i=0; i<hi->masks->used; i++) {
2103 if (!strcmp(del_mask, hi->masks->list[i])) {
2104 char *old_mask = hi->masks->list[i];
2105 if (hi->masks->used == 1 && !force) {
2106 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2109 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2110 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2115 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2119 static NICKSERV_FUNC(cmd_delmask)
2121 NICKSERV_MIN_PARMS(2);
2122 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2125 static NICKSERV_FUNC(cmd_odelmask)
2127 struct handle_info *hi;
2128 NICKSERV_MIN_PARMS(3);
2129 if (!(hi = get_victim_oper(user, argv[1])))
2131 return nickserv_delmask(user, hi, argv[2], 1);
2135 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2136 unsigned int nn, add = 1, pos;
2137 unsigned long added, removed, flag;
2139 for (added=removed=nn=0; str[nn]; nn++) {
2141 case '+': add = 1; break;
2142 case '-': add = 0; break;
2144 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2145 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2148 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2149 /* cheesy avoidance of looking up the flag name.. */
2150 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2153 flag = 1 << (pos - 1);
2155 added |= flag, removed &= ~flag;
2157 removed |= flag, added &= ~flag;
2162 *premoved = removed;
2167 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2169 unsigned long before, after, added, removed;
2170 struct userNode *uNode;
2172 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2173 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2175 hi->flags = (hi->flags | added) & ~removed;
2176 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2178 /* Strip helping flag if they're only a support helper and not
2179 * currently in #support. */
2180 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2181 struct channelList *schannels;
2183 schannels = chanserv_support_channels();
2184 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2185 for (ii = 0; ii < schannels->used; ++ii)
2186 if (GetUserMode(schannels->list[ii], uNode))
2188 if (ii < schannels->used)
2192 HANDLE_CLEAR_FLAG(hi, HELPING);
2195 if (after && !before) {
2196 /* Add user to current helper list. */
2197 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2198 userList_append(&curr_helpers, uNode);
2199 } else if (!after && before) {
2200 /* Remove user from current helper list. */
2201 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2202 userList_remove(&curr_helpers, uNode);
2209 set_list(struct userNode *user, struct handle_info *hi, int override)
2213 char *set_display[] = {
2214 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2215 "EMAIL", "MAXLOGINS", "LANGUAGE"
2218 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2220 /* Do this so options are presented in a consistent order. */
2221 for (i = 0; i < ArrayLength(set_display); ++i)
2222 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2223 opt(user, hi, override, 0, NULL);
2226 static NICKSERV_FUNC(cmd_set)
2228 struct handle_info *hi;
2231 hi = user->handle_info;
2233 set_list(user, hi, 0);
2236 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2237 reply("NSMSG_INVALID_OPTION", argv[1]);
2240 return opt(user, hi, 0, argc-1, argv+1);
2243 static NICKSERV_FUNC(cmd_oset)
2245 struct handle_info *hi;
2246 struct svccmd *subcmd;
2248 char cmdname[MAXLEN];
2250 NICKSERV_MIN_PARMS(2);
2252 if (!(hi = get_victim_oper(user, argv[1])))
2256 set_list(user, hi, 0);
2260 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2261 reply("NSMSG_INVALID_OPTION", argv[2]);
2265 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2266 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2267 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2270 return opt(user, hi, 1, argc-2, argv+2);
2273 static OPTION_FUNC(opt_info)
2277 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2279 hi->infoline = NULL;
2281 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2285 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2286 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2290 static OPTION_FUNC(opt_width)
2293 hi->screen_width = strtoul(argv[1], NULL, 0);
2295 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2296 hi->screen_width = MIN_LINE_SIZE;
2297 else if (hi->screen_width > MAX_LINE_SIZE)
2298 hi->screen_width = MAX_LINE_SIZE;
2300 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2304 static OPTION_FUNC(opt_tablewidth)
2307 hi->table_width = strtoul(argv[1], NULL, 0);
2309 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2310 hi->table_width = MIN_LINE_SIZE;
2311 else if (hi->screen_width > MAX_LINE_SIZE)
2312 hi->table_width = MAX_LINE_SIZE;
2314 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2318 static OPTION_FUNC(opt_color)
2321 if (enabled_string(argv[1]))
2322 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2323 else if (disabled_string(argv[1]))
2324 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2326 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2331 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2335 static OPTION_FUNC(opt_privmsg)
2338 if (enabled_string(argv[1]))
2339 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2340 else if (disabled_string(argv[1]))
2341 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2343 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2348 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2352 static OPTION_FUNC(opt_style)
2357 if (!irccasecmp(argv[1], "Zoot"))
2358 hi->userlist_style = HI_STYLE_ZOOT;
2359 else if (!irccasecmp(argv[1], "def"))
2360 hi->userlist_style = HI_STYLE_DEF;
2363 switch (hi->userlist_style) {
2372 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2376 static OPTION_FUNC(opt_password)
2379 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2384 cryptpass(argv[1], hi->passwd);
2386 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2390 static OPTION_FUNC(opt_flags)
2393 unsigned int ii, flen;
2396 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2401 nickserv_apply_flags(user, hi, argv[1]);
2403 for (ii = flen = 0; handle_flags[ii]; ii++)
2404 if (hi->flags & (1 << ii))
2405 flags[flen++] = handle_flags[ii];
2408 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2410 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2414 static OPTION_FUNC(opt_email)
2418 if (!is_valid_email_addr(argv[1])) {
2419 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2422 if ((str = mail_prohibited_address(argv[1]))) {
2423 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2426 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2427 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2429 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2431 nickserv_set_email_addr(hi, argv[1]);
2433 nickserv_eat_cookie(hi->cookie);
2434 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2437 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2441 static OPTION_FUNC(opt_maxlogins)
2443 unsigned char maxlogins;
2445 maxlogins = strtoul(argv[1], NULL, 0);
2446 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2447 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2450 hi->maxlogins = maxlogins;
2452 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2453 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2457 static OPTION_FUNC(opt_language)
2459 struct language *lang;
2461 lang = language_find(argv[1]);
2462 if (irccasecmp(lang->name, argv[1]))
2463 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2464 hi->language = lang;
2466 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2470 static OPTION_FUNC(opt_karma)
2473 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2478 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2479 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2480 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2481 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2483 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2487 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2492 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2493 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2495 if ((user->handle_info->opserv_level < target->opserv_level)
2496 || ((user->handle_info->opserv_level == target->opserv_level)
2497 && (user->handle_info->opserv_level < 1000))) {
2498 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2501 if ((user->handle_info->opserv_level < new_level)
2502 || ((user->handle_info->opserv_level == new_level)
2503 && (user->handle_info->opserv_level < 1000))) {
2504 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2507 if (user->handle_info == target) {
2508 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2511 if (target->opserv_level == new_level)
2513 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2514 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2515 target->opserv_level = new_level;
2519 static OPTION_FUNC(opt_level)
2524 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2528 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2529 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2533 static OPTION_FUNC(opt_epithet)
2536 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2540 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2541 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2544 if ((epithet[0] == '*') && !epithet[1])
2547 hi->epithet = strdup(epithet);
2551 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2553 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2557 static OPTION_FUNC(opt_title)
2562 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2566 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2568 if (strchr(title, '.')) {
2569 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2572 if ((strlen(user->handle_info->handle) + strlen(title) +
2573 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2574 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2579 if (!strcmp(title, "*")) {
2580 hi->fakehost = NULL;
2582 hi->fakehost = malloc(strlen(title)+2);
2583 hi->fakehost[0] = '.';
2584 strcpy(hi->fakehost+1, title);
2587 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2588 title = hi->fakehost + 1;
2592 title = user_find_message(user, "MSG_NONE");
2593 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2597 static OPTION_FUNC(opt_fakehost)
2602 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2606 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2608 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2609 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2613 if (!strcmp(fake, "*"))
2614 hi->fakehost = NULL;
2616 hi->fakehost = strdup(fake);
2617 fake = hi->fakehost;
2620 fake = generate_fakehost(hi);
2622 fake = user_find_message(user, "MSG_NONE");
2623 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2627 static NICKSERV_FUNC(cmd_reclaim)
2629 struct handle_info *hi;
2630 struct nick_info *ni;
2631 struct userNode *victim;
2633 NICKSERV_MIN_PARMS(2);
2634 hi = user->handle_info;
2635 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2637 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2640 if (ni->owner != user->handle_info) {
2641 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2644 victim = GetUserH(ni->nick);
2646 reply("MSG_NICK_UNKNOWN", ni->nick);
2649 if (victim == user) {
2650 reply("NSMSG_NICK_USER_YOU");
2653 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2654 switch (nickserv_conf.reclaim_action) {
2655 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2656 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2657 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2658 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2663 static NICKSERV_FUNC(cmd_unregnick)
2666 struct handle_info *hi;
2667 struct nick_info *ni;
2669 hi = user->handle_info;
2670 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2671 ni = dict_find(nickserv_nick_dict, nick, NULL);
2673 reply("NSMSG_UNKNOWN_NICK", nick);
2676 if (hi != ni->owner) {
2677 reply("NSMSG_NOT_YOUR_NICK", nick);
2680 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2685 static NICKSERV_FUNC(cmd_ounregnick)
2687 struct nick_info *ni;
2689 NICKSERV_MIN_PARMS(2);
2690 if (!(ni = get_nick_info(argv[1]))) {
2691 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2694 if (!oper_outranks(user, ni->owner))
2696 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2701 static NICKSERV_FUNC(cmd_unregister)
2703 struct handle_info *hi;
2706 NICKSERV_MIN_PARMS(2);
2707 hi = user->handle_info;
2710 if (checkpass(passwd, hi->passwd)) {
2711 nickserv_unregister_handle(hi, user);
2714 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2715 reply("NSMSG_PASSWORD_INVALID");
2720 static NICKSERV_FUNC(cmd_ounregister)
2722 struct handle_info *hi;
2723 char reason[MAXLEN];
2726 NICKSERV_MIN_PARMS(2);
2727 if (!(hi = get_victim_oper(user, argv[1])))
2730 if (HANDLE_FLAGGED(hi, NODELETE)) {
2731 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
2735 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
2737 ((hi->flags & nickserv_conf.ounregister_flags)
2739 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
2740 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
2744 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2745 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2746 nickserv_unregister_handle(hi, user);
2750 static NICKSERV_FUNC(cmd_status)
2752 if (nickserv_conf.disable_nicks) {
2753 reply("NSMSG_GLOBAL_STATS_NONICK",
2754 dict_size(nickserv_handle_dict));
2756 if (user->handle_info) {
2758 struct nick_info *ni;
2759 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2760 reply("NSMSG_HANDLE_STATS", cnt);
2762 reply("NSMSG_HANDLE_NONE");
2764 reply("NSMSG_GLOBAL_STATS",
2765 dict_size(nickserv_handle_dict),
2766 dict_size(nickserv_nick_dict));
2771 static NICKSERV_FUNC(cmd_ghost)
2773 struct userNode *target;
2774 char reason[MAXLEN];
2776 NICKSERV_MIN_PARMS(2);
2777 if (!(target = GetUserH(argv[1]))) {
2778 reply("MSG_NICK_UNKNOWN", argv[1]);
2781 if (target == user) {
2782 reply("NSMSG_CANNOT_GHOST_SELF");
2785 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2786 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2789 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2790 DelUser(target, nickserv, 1, reason);
2791 reply("NSMSG_GHOST_KILLED", argv[1]);
2795 static NICKSERV_FUNC(cmd_vacation)
2797 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2798 reply("NSMSG_ON_VACATION");
2802 static NICKSERV_FUNC(cmd_addnote)
2804 struct handle_info *hi;
2805 unsigned long duration;
2808 struct handle_note *prev;
2809 struct handle_note *note;
2811 /* Parse parameters and figure out values for note's fields. */
2812 NICKSERV_MIN_PARMS(4);
2813 hi = get_victim_oper(user, argv[1]);
2816 if(!strcmp(argv[2], "0"))
2818 else if(!(duration = ParseInterval(argv[2])))
2820 reply("MSG_INVALID_DURATION", argv[2]);
2823 if (duration > 2*365*86400) {
2824 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2827 unsplit_string(argv + 3, argc - 3, text);
2828 WALK_NOTES(hi, prev, note) {}
2829 id = prev ? (prev->id + 1) : 1;
2831 /* Create the new note structure. */
2832 note = calloc(1, sizeof(*note) + strlen(text));
2834 note->expires = duration ? (now + duration) : 0;
2837 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2838 strcpy(note->note, text);
2843 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2847 static NICKSERV_FUNC(cmd_delnote)
2849 struct handle_info *hi;
2850 struct handle_note *prev;
2851 struct handle_note *note;
2854 NICKSERV_MIN_PARMS(3);
2855 hi = get_victim_oper(user, argv[1]);
2858 id = strtoul(argv[2], NULL, 10);
2859 WALK_NOTES(hi, prev, note) {
2860 if (id == note->id) {
2862 prev->next = note->next;
2864 hi->notes = note->next;
2866 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2870 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2875 nickserv_saxdb_write(struct saxdb_context *ctx) {
2877 struct handle_info *hi;
2880 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2882 #ifdef WITH_PROTOCOL_BAHAMUT
2885 saxdb_start_record(ctx, iter_key(it), 0);
2887 struct handle_cookie *cookie = hi->cookie;
2890 switch (cookie->type) {
2891 case ACTIVATION: type = KEY_ACTIVATION; break;
2892 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2893 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2894 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2895 default: type = NULL; break;
2898 saxdb_start_record(ctx, KEY_COOKIE, 0);
2899 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2900 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2902 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2903 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2904 saxdb_end_record(ctx);
2908 struct handle_note *prev, *note;
2909 saxdb_start_record(ctx, KEY_NOTES, 0);
2910 WALK_NOTES(hi, prev, note) {
2911 snprintf(flags, sizeof(flags), "%d", note->id);
2912 saxdb_start_record(ctx, flags, 0);
2914 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
2915 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
2916 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
2917 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
2918 saxdb_end_record(ctx);
2920 saxdb_end_record(ctx);
2923 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2925 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2927 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2931 for (ii=flen=0; handle_flags[ii]; ++ii)
2932 if (hi->flags & (1 << ii))
2933 flags[flen++] = handle_flags[ii];
2935 saxdb_write_string(ctx, KEY_FLAGS, flags);
2937 #ifdef WITH_PROTOCOL_BAHAMUT
2938 saxdb_write_int(ctx, KEY_ID, hi->id);
2941 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2942 if (hi->last_quit_host[0])
2943 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2944 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2946 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
2947 if (hi->masks->used)
2948 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2950 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2952 struct string_list *slist;
2953 struct nick_info *ni;
2955 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2956 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2957 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2961 if (hi->opserv_level)
2962 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2963 if (hi->language != lang_C)
2964 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2965 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2966 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2967 if (hi->screen_width)
2968 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2969 if (hi->table_width)
2970 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2971 flags[0] = hi->userlist_style;
2973 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2974 saxdb_end_record(ctx);
2979 static handle_merge_func_t *handle_merge_func_list;
2980 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2983 reg_handle_merge_func(handle_merge_func_t func)
2985 if (handle_merge_func_used == handle_merge_func_size) {
2986 if (handle_merge_func_size) {
2987 handle_merge_func_size <<= 1;
2988 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2990 handle_merge_func_size = 8;
2991 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2994 handle_merge_func_list[handle_merge_func_used++] = func;
2997 static NICKSERV_FUNC(cmd_merge)
2999 struct handle_info *hi_from, *hi_to;
3000 struct userNode *last_user;
3001 struct userData *cList, *cListNext;
3002 unsigned int ii, jj, n;
3003 char buffer[MAXLEN];
3005 NICKSERV_MIN_PARMS(3);
3007 if (!(hi_from = get_victim_oper(user, argv[1])))
3009 if (!(hi_to = get_victim_oper(user, argv[2])))
3011 if (hi_to == hi_from) {
3012 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3016 for (n=0; n<handle_merge_func_used; n++)
3017 handle_merge_func_list[n](user, hi_to, hi_from);
3019 /* Append "from" handle's nicks to "to" handle's nick list. */
3021 struct nick_info *last_ni;
3022 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3023 last_ni->next = hi_from->nicks;
3025 while (hi_from->nicks) {
3026 hi_from->nicks->owner = hi_to;
3027 hi_from->nicks = hi_from->nicks->next;
3030 /* Merge the hostmasks. */
3031 for (ii=0; ii<hi_from->masks->used; ii++) {
3032 char *mask = hi_from->masks->list[ii];
3033 for (jj=0; jj<hi_to->masks->used; jj++)
3034 if (match_ircglobs(hi_to->masks->list[jj], mask))
3036 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3037 string_list_append(hi_to->masks, strdup(mask));
3040 /* Merge the lists of authed users. */
3042 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3043 last_user->next_authed = hi_from->users;
3045 hi_to->users = hi_from->users;
3047 /* Repoint the old "from" handle's users. */
3048 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3049 last_user->handle_info = hi_to;
3051 hi_from->users = NULL;
3053 /* Merge channel userlists. */
3054 for (cList=hi_from->channels; cList; cList=cListNext) {
3055 struct userData *cList2;
3056 cListNext = cList->u_next;
3057 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3058 if (cList->channel == cList2->channel)
3060 if (cList2 && (cList2->access >= cList->access)) {
3061 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);
3062 /* keep cList2 in hi_to; remove cList from hi_from */
3063 del_channel_user(cList, 1);
3066 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);
3067 /* remove the lower-ranking cList2 from hi_to */
3068 del_channel_user(cList2, 1);
3070 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3072 /* cList needs to be moved from hi_from to hi_to */
3073 cList->handle = hi_to;
3074 /* Remove from linked list for hi_from */
3075 assert(!cList->u_prev);
3076 hi_from->channels = cList->u_next;
3078 cList->u_next->u_prev = cList->u_prev;
3079 /* Add to linked list for hi_to */
3080 cList->u_prev = NULL;
3081 cList->u_next = hi_to->channels;
3082 if (hi_to->channels)
3083 hi_to->channels->u_prev = cList;
3084 hi_to->channels = cList;
3088 /* Do they get an OpServ level promotion? */
3089 if (hi_from->opserv_level > hi_to->opserv_level)
3090 hi_to->opserv_level = hi_from->opserv_level;
3092 /* What about last seen time? */
3093 if (hi_from->lastseen > hi_to->lastseen)
3094 hi_to->lastseen = hi_from->lastseen;
3096 /* New karma is the sum of the two original karmas. */
3097 hi_to->karma += hi_from->karma;
3099 /* Does a fakehost carry over? (This intentionally doesn't set it
3100 * for users previously attached to hi_to. They'll just have to
3103 if (hi_from->fakehost && !hi_to->fakehost)
3104 hi_to->fakehost = strdup(hi_from->fakehost);
3106 /* Notify of success. */
3107 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3108 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3109 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3111 /* Unregister the "from" handle. */
3112 nickserv_unregister_handle(hi_from, NULL);
3117 struct nickserv_discrim {
3118 unsigned long flags_on, flags_off;
3119 unsigned long min_registered, max_registered;
3120 unsigned long lastseen;
3122 int min_level, max_level;
3123 int min_karma, max_karma;
3124 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3125 const char *nickmask;
3126 const char *hostmask;
3127 const char *fakehostmask;
3128 const char *handlemask;
3129 const char *emailmask;
3132 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3134 struct discrim_apply_info {
3135 struct nickserv_discrim *discrim;
3136 discrim_search_func func;
3137 struct userNode *source;
3138 unsigned int matched;
3141 static struct nickserv_discrim *
3142 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3145 struct nickserv_discrim *discrim;
3147 discrim = malloc(sizeof(*discrim));
3148 memset(discrim, 0, sizeof(*discrim));
3149 discrim->min_level = 0;
3150 discrim->max_level = INT_MAX;
3151 discrim->limit = 50;
3152 discrim->min_registered = 0;
3153 discrim->max_registered = ULONG_MAX;
3154 discrim->lastseen = ULONG_MAX;
3155 discrim->min_karma = INT_MIN;
3156 discrim->max_karma = INT_MAX;
3158 for (i=0; i<argc; i++) {
3159 if (i == argc - 1) {
3160 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3163 if (!irccasecmp(argv[i], "limit")) {
3164 discrim->limit = strtoul(argv[++i], NULL, 0);
3165 } else if (!irccasecmp(argv[i], "flags")) {
3166 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3167 } else if (!irccasecmp(argv[i], "registered")) {
3168 const char *cmp = argv[++i];
3169 if (cmp[0] == '<') {
3170 if (cmp[1] == '=') {
3171 discrim->min_registered = now - ParseInterval(cmp+2);
3173 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3175 } else if (cmp[0] == '=') {
3176 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3177 } else if (cmp[0] == '>') {
3178 if (cmp[1] == '=') {
3179 discrim->max_registered = now - ParseInterval(cmp+2);
3181 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3184 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3186 } else if (!irccasecmp(argv[i], "seen")) {
3187 discrim->lastseen = now - ParseInterval(argv[++i]);
3188 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3189 discrim->nickmask = argv[++i];
3190 } else if (!irccasecmp(argv[i], "hostmask")) {
3192 if (!irccasecmp(argv[i], "exact")) {
3193 if (i == argc - 1) {
3194 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3197 discrim->hostmask_type = EXACT;
3198 } else if (!irccasecmp(argv[i], "subset")) {
3199 if (i == argc - 1) {
3200 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3203 discrim->hostmask_type = SUBSET;
3204 } else if (!irccasecmp(argv[i], "superset")) {
3205 if (i == argc - 1) {
3206 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3209 discrim->hostmask_type = SUPERSET;
3210 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3211 if (i == argc - 1) {
3212 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3215 discrim->hostmask_type = LASTQUIT;
3218 discrim->hostmask_type = SUPERSET;
3220 discrim->hostmask = argv[++i];
3221 } else if (!irccasecmp(argv[i], "fakehost")) {
3222 if (!irccasecmp(argv[++i], "*")) {
3223 discrim->fakehostmask = 0;
3225 discrim->fakehostmask = argv[i];
3227 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3228 if (!irccasecmp(argv[++i], "*")) {
3229 discrim->handlemask = 0;
3231 discrim->handlemask = argv[i];
3233 } else if (!irccasecmp(argv[i], "email")) {
3234 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3235 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3237 } else if (!irccasecmp(argv[++i], "*")) {
3238 discrim->emailmask = 0;
3240 discrim->emailmask = argv[i];
3242 } else if (!irccasecmp(argv[i], "access")) {
3243 const char *cmp = argv[++i];
3244 if (cmp[0] == '<') {
3245 if (discrim->min_level == 0) discrim->min_level = 1;
3246 if (cmp[1] == '=') {
3247 discrim->max_level = strtoul(cmp+2, NULL, 0);
3249 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3251 } else if (cmp[0] == '=') {
3252 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3253 } else if (cmp[0] == '>') {
3254 if (cmp[1] == '=') {
3255 discrim->min_level = strtoul(cmp+2, NULL, 0);
3257 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3260 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3262 } else if (!irccasecmp(argv[i], "karma")) {
3263 const char *cmp = argv[++i];
3264 if (cmp[0] == '<') {
3265 if (cmp[1] == '=') {
3266 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3268 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3270 } else if (cmp[0] == '=') {
3271 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3272 } else if (cmp[0] == '>') {
3273 if (cmp[1] == '=') {
3274 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3276 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3279 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3282 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3293 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3295 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3296 || (discrim->flags_off & hi->flags)
3297 || (discrim->min_registered > hi->registered)
3298 || (discrim->max_registered < hi->registered)
3299 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3300 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3301 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3302 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3303 || (discrim->min_level > hi->opserv_level)
3304 || (discrim->max_level < hi->opserv_level)
3305 || (discrim->min_karma > hi->karma)
3306 || (discrim->max_karma < hi->karma)
3310 if (discrim->hostmask) {
3312 for (i=0; i<hi->masks->used; i++) {
3313 const char *mask = hi->masks->list[i];
3314 if ((discrim->hostmask_type == SUBSET)
3315 && (match_ircglobs(discrim->hostmask, mask))) break;
3316 else if ((discrim->hostmask_type == EXACT)
3317 && !irccasecmp(discrim->hostmask, mask)) break;
3318 else if ((discrim->hostmask_type == SUPERSET)
3319 && (match_ircglobs(mask, discrim->hostmask))) break;
3320 else if ((discrim->hostmask_type == LASTQUIT)
3321 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3323 if (i==hi->masks->used) return 0;
3325 if (discrim->nickmask) {
3326 struct nick_info *nick = hi->nicks;
3328 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3331 if (!nick) return 0;
3337 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3339 dict_iterator_t it, next;
3340 unsigned int matched;
3342 for (it = dict_first(nickserv_handle_dict), matched = 0;
3343 it && (matched < discrim->limit);
3345 next = iter_next(it);
3346 if (nickserv_discrim_match(discrim, iter_data(it))) {
3347 dsf(source, iter_data(it));
3355 search_print_func(struct userNode *source, struct handle_info *match)
3357 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3361 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3366 search_unregister_func (struct userNode *source, struct handle_info *match)
3368 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3369 nickserv_unregister_handle(match, source);
3373 nickserv_sort_accounts_by_access(const void *a, const void *b)
3375 const struct handle_info *hi_a = *(const struct handle_info**)a;
3376 const struct handle_info *hi_b = *(const struct handle_info**)b;
3377 if (hi_a->opserv_level != hi_b->opserv_level)
3378 return hi_b->opserv_level - hi_a->opserv_level;
3379 return irccasecmp(hi_a->handle, hi_b->handle);
3383 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3385 struct handle_info_list hil;
3386 struct helpfile_table tbl;
3391 memset(&hil, 0, sizeof(hil));
3392 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3393 struct handle_info *hi = iter_data(it);
3394 if (hi->opserv_level)
3395 handle_info_list_append(&hil, hi);
3397 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3398 tbl.length = hil.used + 1;
3400 tbl.flags = TABLE_NO_FREE;
3401 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3402 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3405 for (ii = 0; ii < hil.used; ) {
3406 ary = malloc(tbl.width * sizeof(ary[0]));
3407 ary[0] = hil.list[ii]->handle;
3408 ary[1] = strtab(hil.list[ii]->opserv_level);
3409 tbl.contents[++ii] = ary;
3411 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3412 reply("MSG_MATCH_COUNT", hil.used);
3413 for (ii = 0; ii < hil.used; ii++)
3414 free(tbl.contents[ii]);
3419 static NICKSERV_FUNC(cmd_search)
3421 struct nickserv_discrim *discrim;
3422 discrim_search_func action;
3423 struct svccmd *subcmd;
3424 unsigned int matches;
3427 NICKSERV_MIN_PARMS(3);
3428 sprintf(buf, "search %s", argv[1]);
3429 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3430 if (!irccasecmp(argv[1], "print"))
3431 action = search_print_func;
3432 else if (!irccasecmp(argv[1], "count"))
3433 action = search_count_func;
3434 else if (!irccasecmp(argv[1], "unregister"))
3435 action = search_unregister_func;
3437 reply("NSMSG_INVALID_ACTION", argv[1]);
3441 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3444 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3448 if (action == search_print_func)
3449 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3450 else if (action == search_count_func)
3451 discrim->limit = INT_MAX;
3453 matches = nickserv_discrim_search(discrim, action, user);
3456 reply("MSG_MATCH_COUNT", matches);
3458 reply("MSG_NO_MATCHES");
3464 static MODCMD_FUNC(cmd_checkpass)
3466 struct handle_info *hi;
3468 NICKSERV_MIN_PARMS(3);
3469 if (!(hi = get_handle_info(argv[1]))) {
3470 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3473 if (checkpass(argv[2], hi->passwd))
3474 reply("CHECKPASS_YES");
3476 reply("CHECKPASS_NO");
3481 static MODCMD_FUNC(cmd_checkemail)
3483 struct handle_info *hi;
3485 NICKSERV_MIN_PARMS(3);
3486 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3487 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3490 if (!hi->email_addr)
3491 reply("CHECKEMAIL_NOT_SET");
3492 else if (!irccasecmp(argv[2], hi->email_addr))
3493 reply("CHECKEMAIL_YES");
3495 reply("CHECKEMAIL_NO");
3501 nickserv_db_read_handle(const char *handle, dict_t obj)
3504 struct string_list *masks, *slist;
3505 struct handle_info *hi;
3506 struct userNode *authed_users;
3507 struct userData *channels;
3508 unsigned long int id;
3512 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3513 id = str ? strtoul(str, NULL, 0) : 0;
3514 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3516 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3519 if ((hi = get_handle_info(handle))) {
3520 authed_users = hi->users;
3521 channels = hi->channels;
3523 hi->channels = NULL;
3524 dict_remove(nickserv_handle_dict, hi->handle);
3526 authed_users = NULL;
3529 hi = register_handle(handle, str, id);
3531 hi->users = authed_users;
3532 while (authed_users) {
3533 authed_users->handle_info = hi;
3534 authed_users = authed_users->next_authed;
3537 hi->channels = channels;
3538 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3539 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3540 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3541 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3542 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3543 hi->language = language_find(str ? str : "C");
3544 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3545 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3546 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3548 hi->infoline = strdup(str);
3549 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3550 hi->registered = str ? strtoul(str, NULL, 0) : now;
3551 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3552 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3553 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3554 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3555 /* We want to read the nicks even if disable_nicks is set. This is so
3556 * that we don't lose the nick data entirely. */
3557 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3559 for (ii=0; ii<slist->used; ii++)
3560 register_nick(slist->list[ii], hi);
3562 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3564 for (ii=0; str[ii]; ii++)
3565 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3567 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3568 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3569 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3570 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3571 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3572 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3573 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3575 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3577 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3578 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3580 nickserv_set_email_addr(hi, str);
3581 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3583 hi->epithet = strdup(str);
3584 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3586 hi->fakehost = strdup(str);
3587 /* Read the "cookie" sub-database (if it exists). */
3588 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3590 const char *data, *type, *expires, *cookie_str;
3591 struct handle_cookie *cookie;
3593 cookie = calloc(1, sizeof(*cookie));
3594 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3595 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3596 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3597 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3598 if (!type || !expires || !cookie_str) {
3599 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3602 if (!irccasecmp(type, KEY_ACTIVATION))
3603 cookie->type = ACTIVATION;
3604 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3605 cookie->type = PASSWORD_CHANGE;
3606 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3607 cookie->type = EMAIL_CHANGE;
3608 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3609 cookie->type = ALLOWAUTH;
3611 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3614 cookie->expires = strtoul(expires, NULL, 0);
3615 if (cookie->expires < now)
3618 cookie->data = strdup(data);
3619 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3623 nickserv_bake_cookie(cookie);
3625 nickserv_free_cookie(cookie);
3627 /* Read the "notes" sub-database (if it exists). */
3628 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3631 struct handle_note *last_note;
3632 struct handle_note *note;
3635 for (it = dict_first(subdb); it; it = iter_next(it)) {
3636 const char *expires;
3644 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3646 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", id, hi->handle);
3649 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3650 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3651 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3652 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3653 if (!setter || !text || !set) {
3654 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", id, hi->handle);
3657 note = calloc(1, sizeof(*note) + strlen(text));
3659 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3660 note->set = strtoul(set, NULL, 10);
3661 note->id = strtoul(id, NULL, 10);
3662 safestrncpy(note->setter, setter, sizeof(note->setter));
3663 strcpy(note->note, text);
3665 last_note->next = note;
3674 nickserv_saxdb_read(dict_t db) {
3676 struct record_data *rd;
3678 for (it=dict_first(db); it; it=iter_next(it)) {
3680 nickserv_db_read_handle(iter_key(it), rd->d.object);
3685 static NICKSERV_FUNC(cmd_mergedb)
3687 struct timeval start, stop;
3690 NICKSERV_MIN_PARMS(2);
3691 gettimeofday(&start, NULL);
3692 if (!(db = parse_database(argv[1]))) {
3693 reply("NSMSG_DB_UNREADABLE", argv[1]);
3696 nickserv_saxdb_read(db);
3698 gettimeofday(&stop, NULL);
3699 stop.tv_sec -= start.tv_sec;
3700 stop.tv_usec -= start.tv_usec;
3701 if (stop.tv_usec < 0) {
3703 stop.tv_usec += 1000000;
3705 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
3710 expire_handles(UNUSED_ARG(void *data))
3712 dict_iterator_t it, next;
3713 unsigned long expiry;
3714 struct handle_info *hi;
3716 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3717 next = iter_next(it);
3719 if ((hi->opserv_level > 0)
3721 || HANDLE_FLAGGED(hi, FROZEN)
3722 || HANDLE_FLAGGED(hi, NODELETE)) {
3725 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3726 if ((now - hi->lastseen) > expiry) {
3727 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3728 nickserv_unregister_handle(hi, NULL);
3732 if (nickserv_conf.handle_expire_frequency)
3733 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3737 nickserv_load_dict(const char *fname)
3741 if (!(file = fopen(fname, "r"))) {
3742 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3745 while (!feof(file)) {
3746 fgets(line, sizeof(line), file);
3749 if (line[strlen(line)-1] == '\n')
3750 line[strlen(line)-1] = 0;
3751 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3754 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3757 static enum reclaim_action
3758 reclaim_action_from_string(const char *str) {
3760 return RECLAIM_NONE;
3761 else if (!irccasecmp(str, "warn"))
3762 return RECLAIM_WARN;
3763 else if (!irccasecmp(str, "svsnick"))
3764 return RECLAIM_SVSNICK;
3765 else if (!irccasecmp(str, "kill"))
3766 return RECLAIM_KILL;
3768 return RECLAIM_NONE;
3772 nickserv_conf_read(void)
3774 dict_t conf_node, child;
3778 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3779 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3782 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3784 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3785 if (nickserv_conf.valid_handle_regex_set)
3786 regfree(&nickserv_conf.valid_handle_regex);
3788 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3789 nickserv_conf.valid_handle_regex_set = !err;
3790 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3792 nickserv_conf.valid_handle_regex_set = 0;
3794 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3795 if (nickserv_conf.valid_nick_regex_set)
3796 regfree(&nickserv_conf.valid_nick_regex);
3798 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3799 nickserv_conf.valid_nick_regex_set = !err;
3800 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3802 nickserv_conf.valid_nick_regex_set = 0;
3804 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3806 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3807 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3808 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3809 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3810 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3811 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3812 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3813 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3814 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3815 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3816 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3817 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3818 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3819 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3820 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3821 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3822 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3823 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3824 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3825 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3826 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3827 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3828 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3829 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3830 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3832 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3833 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3834 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3836 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3837 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3838 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3840 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3841 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3842 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3843 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3844 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3845 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3846 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3847 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3848 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
3849 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
3850 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
3853 nickserv_conf.ounregister_flags = 0;
3855 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
3858 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
3860 if (!nickserv_conf.disable_nicks) {
3861 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3862 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3863 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3864 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3865 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3866 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3867 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3868 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3870 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3871 for (it=dict_first(child); it; it=iter_next(it)) {
3872 const char *key = iter_key(it), *value;
3876 if (!strncasecmp(key, "uc_", 3))
3877 flag = toupper(key[3]);
3878 else if (!strncasecmp(key, "lc_", 3))
3879 flag = tolower(key[3]);
3883 if ((pos = handle_inverse_flags[flag])) {
3884 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3885 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3888 if (nickserv_conf.weak_password_dict)
3889 dict_delete(nickserv_conf.weak_password_dict);
3890 nickserv_conf.weak_password_dict = dict_new();
3891 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3892 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3893 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3894 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3896 nickserv_load_dict(str);
3897 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3898 if (nickserv && str)
3899 NickChange(nickserv, str, 0);
3900 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3901 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3902 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3903 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3904 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3905 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3906 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3907 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3908 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3909 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3910 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3911 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3912 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3913 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3914 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3915 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3916 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3917 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3918 str = conf_get_data("server/network", RECDB_QSTRING);
3919 nickserv_conf.network_name = str ? str : "some IRC network";
3920 if (!nickserv_conf.auth_policer_params) {
3921 nickserv_conf.auth_policer_params = policer_params_new();
3922 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3923 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3925 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3926 for (it=dict_first(child); it; it=iter_next(it))
3927 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3931 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3933 char newnick[NICKLEN+1];
3942 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3944 case RECLAIM_SVSNICK:
3946 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3947 } while (GetUserH(newnick));
3948 irc_svsnick(nickserv, user, newnick);
3951 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3952 DelUser(user, nickserv, 1, msg);
3958 nickserv_reclaim_p(void *data) {
3959 struct userNode *user = data;
3960 struct nick_info *ni = get_nick_info(user->nick);
3962 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3966 check_user_nick(struct userNode *user) {
3967 struct nick_info *ni;
3968 user->modes &= ~FLAGS_REGNICK;
3969 if (!(ni = get_nick_info(user->nick)))
3971 if (user->handle_info == ni->owner) {
3972 user->modes |= FLAGS_REGNICK;
3976 if (nickserv_conf.warn_nick_owned)
3977 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3978 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3980 if (nickserv_conf.auto_reclaim_delay)
3981 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3983 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3988 handle_new_user(struct userNode *user)
3990 return check_user_nick(user);
3994 handle_account(struct userNode *user, const char *stamp)
3996 struct handle_info *hi;
3998 #ifdef WITH_PROTOCOL_P10
3999 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4001 hi = dict_find(nickserv_id_dict, stamp, NULL);
4005 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4008 set_user_handle_info(user, hi, 0);
4010 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
4015 handle_nick_change(struct userNode *user, const char *old_nick)
4017 struct handle_info *hi;
4019 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4020 dict_remove(nickserv_allow_auth_dict, old_nick);
4021 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4023 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4024 check_user_nick(user);
4028 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4030 dict_remove(nickserv_allow_auth_dict, user->nick);
4031 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4032 set_user_handle_info(user, NULL, 0);
4035 static struct modcmd *
4036 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4038 if (min_level > 0) {
4040 sprintf(buf, "%u", min_level);
4041 if (must_be_qualified) {
4042 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4044 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4046 } else if (min_level == 0) {
4047 if (must_be_qualified) {
4048 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4050 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4053 if (must_be_qualified) {
4054 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4056 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4062 nickserv_db_cleanup(void)
4064 unreg_del_user_func(nickserv_remove_user);
4065 userList_clean(&curr_helpers);
4066 policer_params_delete(nickserv_conf.auth_policer_params);
4067 dict_delete(nickserv_handle_dict);
4068 dict_delete(nickserv_nick_dict);
4069 dict_delete(nickserv_opt_dict);
4070 dict_delete(nickserv_allow_auth_dict);
4071 dict_delete(nickserv_email_dict);
4072 dict_delete(nickserv_id_dict);
4073 dict_delete(nickserv_conf.weak_password_dict);
4074 free(auth_func_list);
4075 free(unreg_func_list);
4077 free(allowauth_func_list);
4078 free(handle_merge_func_list);
4079 free(failpw_func_list);
4080 if (nickserv_conf.valid_handle_regex_set)
4081 regfree(&nickserv_conf.valid_handle_regex);
4082 if (nickserv_conf.valid_nick_regex_set)
4083 regfree(&nickserv_conf.valid_nick_regex);
4087 init_nickserv(const char *nick)
4090 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4091 reg_new_user_func(handle_new_user);
4092 reg_nick_change_func(handle_nick_change);
4093 reg_del_user_func(nickserv_remove_user);
4094 reg_account_func(handle_account);
4096 /* set up handle_inverse_flags */
4097 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4098 for (i=0; handle_flags[i]; i++) {
4099 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4100 flag_access_levels[i] = 0;
4103 conf_register_reload(nickserv_conf_read);
4104 nickserv_opt_dict = dict_new();
4105 nickserv_email_dict = dict_new();
4106 dict_set_free_keys(nickserv_email_dict, free);
4107 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4109 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4110 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4111 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4112 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4113 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4114 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4115 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4116 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4117 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4118 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4119 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4120 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4121 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4122 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4123 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4124 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4125 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4126 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4127 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4128 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4129 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4130 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4131 if (!nickserv_conf.disable_nicks) {
4132 /* nick management commands */
4133 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4134 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4135 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4136 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4137 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4138 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4140 if (nickserv_conf.email_enabled) {
4141 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4142 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4143 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4144 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4145 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4146 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4148 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4149 /* miscellaneous commands */
4150 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4151 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4152 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4153 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4154 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4155 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4157 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4158 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4159 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4160 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4161 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4162 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4163 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4164 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4165 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4166 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4167 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4168 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4169 if (nickserv_conf.titlehost_suffix) {
4170 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4171 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4173 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4174 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4175 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4176 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4178 nickserv_handle_dict = dict_new();
4179 dict_set_free_keys(nickserv_handle_dict, free);
4180 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4182 nickserv_id_dict = dict_new();
4183 dict_set_free_keys(nickserv_id_dict, free);
4185 nickserv_nick_dict = dict_new();
4186 dict_set_free_data(nickserv_nick_dict, free);
4188 nickserv_allow_auth_dict = dict_new();
4190 userList_init(&curr_helpers);
4193 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4194 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4195 nickserv_service = service_register(nickserv);
4197 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4198 reg_exit_func(nickserv_db_cleanup);
4199 if(nickserv_conf.handle_expire_frequency)
4200 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4201 message_register_table(msgtab);