1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2008 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for gag_create(), opserv_bad_channel() */
33 # include "rx/rxposix.h"
36 #define NICKSERV_CONF_NAME "services/nickserv"
38 #define KEY_DISABLE_NICKS "disable_nicks"
39 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
40 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
41 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
42 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
43 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
44 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
45 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
46 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
47 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
48 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
49 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
50 #define KEY_MODOPER_LEVEL "modoper_level"
51 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
52 #define KEY_SET_TITLE_LEVEL "set_title_level"
53 #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
54 #define KEY_SET_FAKEIDENT_LEVEL "set_fakeident_level"
55 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
56 #define KEY_FLAG_LEVELS "flag_levels"
57 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
58 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
59 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
60 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
61 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
62 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
63 #define KEY_DICT_FILE "dict_file"
64 #define KEY_NICK "nick"
65 #define KEY_LANGUAGE "language"
66 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
67 #define KEY_AUTOGAG_DURATION "autogag_duration"
68 #define KEY_AUTH_POLICER "auth_policer"
69 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
70 #define KEY_EMAIL_ENABLED "email_enabled"
71 #define KEY_EMAIL_REQUIRED "email_required"
72 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
73 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
74 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
75 #define KEY_OUNREGISTER_INACTIVE "ounregister_inactive"
76 #define KEY_OUNREGISTER_FLAGS "ounregister_flags"
77 #define KEY_HANDLE_TS_MODE "account_timestamp_mode"
80 #define KEY_PASSWD "passwd"
81 #define KEY_NICKS "nicks"
82 #define KEY_MASKS "masks"
83 #define KEY_OPSERV_LEVEL "opserv_level"
84 #define KEY_FLAGS "flags"
85 #define KEY_REGISTER_ON "register"
86 #define KEY_LAST_SEEN "lastseen"
87 #define KEY_INFO "info"
88 #define KEY_USERLIST_STYLE "user_style"
89 #define KEY_SCREEN_WIDTH "screen_width"
90 #define KEY_LAST_AUTHED_HOST "last_authed_host"
91 #define KEY_LAST_QUIT_HOST "last_quit_host"
92 #define KEY_EMAIL_ADDR "email_addr"
93 #define KEY_COOKIE "cookie"
94 #define KEY_COOKIE_DATA "data"
95 #define KEY_COOKIE_TYPE "type"
96 #define KEY_COOKIE_EXPIRES "expires"
97 #define KEY_ACTIVATION "activation"
98 #define KEY_PASSWORD_CHANGE "password change"
99 #define KEY_EMAIL_CHANGE "email change"
100 #define KEY_ALLOWAUTH "allowauth"
101 #define KEY_EPITHET "epithet"
102 #define KEY_TABLE_WIDTH "table_width"
103 #define KEY_MAXLOGINS "maxlogins"
104 #define KEY_FAKEHOST "fakehost"
105 #define KEY_FAKEIDENT "fakeident"
106 #define KEY_NOTES "notes"
107 #define KEY_NOTE_EXPIRES "expires"
108 #define KEY_NOTE_SET "set"
109 #define KEY_NOTE_SETTER "setter"
110 #define KEY_NOTE_NOTE "note"
111 #define KEY_KARMA "karma"
113 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
115 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
116 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
117 typedef OPTION_FUNC(option_func_t);
119 DEFINE_LIST(handle_info_list, struct handle_info*)
121 #define NICKSERV_MIN_PARMS(N) do { \
123 reply("MSG_MISSING_PARAMS", argv[0]); \
124 svccmd_send_help(user, nickserv, cmd); \
128 struct userNode *nickserv;
129 struct userList curr_helpers;
130 const char *handle_flags = HANDLE_FLAGS;
132 static struct module *nickserv_module;
133 static struct service *nickserv_service;
134 static struct log_type *NS_LOG;
135 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
136 static dict_t nickserv_id_dict; /* contains struct handle_info* */
137 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
138 static dict_t nickserv_opt_dict; /* contains option_func_t* */
139 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
140 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
141 static char handle_inverse_flags[256];
142 static unsigned int flag_access_levels[32];
143 static const struct message_entry msgtab[] = {
144 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
145 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
146 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
147 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
148 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
149 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
150 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
151 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
152 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
153 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
154 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
155 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
156 { "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." },
157 { "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." },
158 { "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." },
159 { "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." },
160 { "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." },
161 { "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." },
162 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
163 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
164 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
165 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
166 { "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." },
167 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
168 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
169 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
170 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
171 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
172 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
173 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
174 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
175 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
176 { "NSMSG_ATE_COOKIE_FOREIGN", "I ate the cookie for account $b%s$b. It may now have another." },
177 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
178 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
179 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
180 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
181 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
182 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
183 { "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)" },
184 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
185 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
186 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
187 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
188 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
189 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
190 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
191 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
192 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
193 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
194 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
195 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
196 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
197 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
198 { "NSMSG_FAKEIDENT_INVALID", "Fake idents must be shorter than %d characters." },
199 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
200 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
201 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
202 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
203 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
204 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
205 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
206 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
207 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
208 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
209 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
210 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
211 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
212 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
213 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
214 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
215 { "NSMSG_HANDLEINFO_FAKEIDENT", " Fake ident: %s" },
216 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
217 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
218 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
219 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
220 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
221 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
222 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
223 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
224 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
225 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
226 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
227 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
228 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
229 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
230 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
231 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
232 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
233 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
234 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
235 { "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)." },
236 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
237 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
238 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
239 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
240 { "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." },
241 { "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." },
242 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
243 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
244 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
245 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
246 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
247 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
248 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
249 { "NSMSG_PASS_SUCCESS", "Password changed." },
250 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
251 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
252 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
253 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
254 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
255 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
256 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
257 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
258 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
259 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
260 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
261 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
262 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
263 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
264 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
265 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
266 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
267 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
268 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
269 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
270 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
271 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
272 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
273 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
274 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
275 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
276 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
277 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
278 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
279 { "NSMSG_NO_ACCESS", "Access denied." },
280 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
281 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
282 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
283 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
284 { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
285 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
286 { "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." },
287 { "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." },
288 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
289 { "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." },
290 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
291 { "NSMSG_SEARCH_MATCH", "Match: %s" },
292 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
293 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
294 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
295 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
296 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
297 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
298 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
299 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
300 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
301 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
302 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
303 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
304 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
305 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
306 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
307 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
308 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
309 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
310 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
311 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
312 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
313 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
314 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
315 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
316 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
317 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
318 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
319 { "NSMSG_SET_FAKEIDENT", "$bFAKEIDENT: $b%s" },
320 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
321 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
322 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
323 { "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." },
324 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
325 { "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." },
326 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
327 { "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." },
328 { "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." },
329 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
330 { "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." },
331 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
332 { "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." },
333 { "CHECKPASS_YES", "Yes." },
334 { "CHECKPASS_NO", "No." },
335 { "CHECKEMAIL_NOT_SET", "No email set." },
336 { "CHECKEMAIL_YES", "Yes." },
337 { "CHECKEMAIL_NO", "No." },
341 enum reclaim_action {
347 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
348 static void nickserv_reclaim_p(void *data);
349 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
351 enum handle_ts_mode {
357 unsigned int disable_nicks : 1;
358 unsigned int valid_handle_regex_set : 1;
359 unsigned int valid_nick_regex_set : 1;
360 unsigned int autogag_enabled : 1;
361 unsigned int email_enabled : 1;
362 unsigned int email_required : 1;
363 unsigned int default_hostmask : 1;
364 unsigned int warn_nick_owned : 1;
365 unsigned int warn_clone_auth : 1;
366 unsigned long nicks_per_handle;
367 unsigned long password_min_length;
368 unsigned long password_min_digits;
369 unsigned long password_min_upper;
370 unsigned long password_min_lower;
371 unsigned long db_backup_frequency;
372 unsigned long handle_expire_frequency;
373 unsigned long autogag_duration;
374 unsigned long email_visible_level;
375 unsigned long cookie_timeout;
376 unsigned long handle_expire_delay;
377 unsigned long nochan_handle_expire_delay;
378 unsigned long modoper_level;
379 unsigned long set_epithet_level;
380 unsigned long set_title_level;
381 unsigned long set_fakehost_level;
382 unsigned long set_fakeident_level;
383 unsigned long handles_per_email;
384 unsigned long email_search_level;
385 const char *network_name;
386 const char *titlehost_suffix;
387 regex_t valid_handle_regex;
388 regex_t valid_nick_regex;
389 dict_t weak_password_dict;
390 struct policer_params *auth_policer_params;
391 enum reclaim_action reclaim_action;
392 enum reclaim_action auto_reclaim_action;
393 enum handle_ts_mode handle_ts_mode;
394 unsigned long auto_reclaim_delay;
395 unsigned char default_maxlogins;
396 unsigned char hard_maxlogins;
397 unsigned long ounregister_inactive;
398 unsigned long ounregister_flags;
401 /* We have 2^32 unique account IDs to use. */
402 unsigned long int highest_id = 0;
404 #define WALK_NOTES(HANDLE, PREV, NOTE) \
405 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
406 if (NOTE->expires && NOTE->expires < now) { \
407 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
409 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
413 canonicalize_hostmask(char *mask)
415 char *out = mask, *temp;
416 if ((temp = strchr(mask, '!'))) {
418 while (*temp) *out++ = *temp++;
424 static struct handle_info *
425 register_handle(const char *handle, const char *passwd, unsigned long id)
427 struct handle_info *hi;
429 char id_base64[IDLEN + 1];
432 /* Assign a unique account ID to the account; note that 0 is
433 an invalid account ID. 1 is therefore the first account ID. */
435 id = 1 + highest_id++;
437 /* Note: highest_id is and must always be the highest ID. */
438 if (id > highest_id) {
442 inttobase64(id_base64, id, IDLEN);
444 /* Make sure an account with the same ID doesn't exist. If a
445 duplicate is found, log some details and assign a new one.
446 This should be impossible, but it never hurts to expect it. */
447 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
448 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
453 hi = calloc(1, sizeof(*hi));
454 hi->userlist_style = HI_DEFAULT_STYLE;
455 hi->handle = strdup(handle);
456 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
458 dict_insert(nickserv_handle_dict, hi->handle, hi);
461 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
467 register_nick(const char *nick, struct handle_info *owner)
469 struct nick_info *ni;
470 ni = malloc(sizeof(struct nick_info));
471 safestrncpy(ni->nick, nick, sizeof(ni->nick));
473 ni->next = owner->nicks;
475 dict_insert(nickserv_nick_dict, ni->nick, ni);
479 delete_nick(struct nick_info *ni)
481 struct nick_info *last, *next;
482 struct userNode *user;
483 /* Check to see if we should mark a user as unregistered. */
484 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
485 user->modes &= ~FLAGS_REGNICK;
488 /* Remove ni from the nick_info linked list. */
489 if (ni == ni->owner->nicks) {
490 ni->owner->nicks = ni->next;
492 last = ni->owner->nicks;
498 last->next = next->next;
500 dict_remove(nickserv_nick_dict, ni->nick);
503 static unreg_func_t *unreg_func_list;
504 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
507 reg_unreg_func(unreg_func_t func)
509 if (unreg_func_used == unreg_func_size) {
510 if (unreg_func_size) {
511 unreg_func_size <<= 1;
512 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
515 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
518 unreg_func_list[unreg_func_used++] = func;
522 nickserv_free_cookie(void *data)
524 struct handle_cookie *cookie = data;
525 if (cookie->hi) cookie->hi->cookie = NULL;
526 if (cookie->data) free(cookie->data);
531 free_handle_info(void *vhi)
533 struct handle_info *hi = vhi;
536 inttobase64(id, hi->id, IDLEN);
537 dict_remove(nickserv_id_dict, id);
539 free_string_list(hi->masks);
543 delete_nick(hi->nicks);
549 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
550 nickserv_free_cookie(hi->cookie);
553 struct handle_note *note = hi->notes;
554 hi->notes = note->next;
557 if (hi->email_addr) {
558 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
559 handle_info_list_remove(hil, hi);
561 dict_remove(nickserv_email_dict, hi->email_addr);
566 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
569 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
573 for (n=0; n<unreg_func_used; n++)
574 unreg_func_list[n](notify, hi);
576 set_user_handle_info(hi->users, NULL, 0);
578 if (nickserv_conf.disable_nicks)
579 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
581 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
583 dict_remove(nickserv_handle_dict, hi->handle);
587 get_handle_info(const char *handle)
589 return dict_find(nickserv_handle_dict, handle, 0);
593 get_nick_info(const char *nick)
595 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
599 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
604 for (nn=0; nn<channel->members.used; ++nn) {
605 mn = channel->members.list[nn];
606 if ((mn->user != except) && (mn->user->handle_info == handle))
613 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
614 if (!user->handle_info) {
616 send_message(user, bot, "MSG_AUTHENTICATE");
620 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
622 send_message(user, bot, "NSMSG_NO_ACCESS");
626 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
628 send_message(user, bot, "MSG_OPER_SUSPENDED");
632 if (user->handle_info->opserv_level < min_level) {
634 send_message(user, bot, "NSMSG_NO_ACCESS");
642 is_valid_handle(const char *handle)
644 struct userNode *user;
645 /* cant register a juped nick/service nick as handle, to prevent confusion */
646 user = GetUserH(handle);
647 if (user && IsLocal(user))
649 /* check against maximum length */
650 if (strlen(handle) > NICKSERV_HANDLE_LEN)
652 /* for consistency, only allow account names that could be nicks */
653 if (!is_valid_nick(handle))
655 /* disallow account names that look like bad words */
656 if (opserv_bad_channel(handle))
658 /* test either regex or containing all valid chars */
659 if (nickserv_conf.valid_handle_regex_set) {
660 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
663 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
664 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
668 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
673 is_registerable_nick(const char *nick)
675 /* make sure it could be used as an account name */
676 if (!is_valid_handle(nick))
679 if (strlen(nick) > NICKLEN)
681 /* test either regex or as valid handle */
682 if (nickserv_conf.valid_nick_regex_set) {
683 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
686 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
687 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
695 is_valid_email_addr(const char *email)
697 return strchr(email, '@') != NULL;
701 visible_email_addr(struct userNode *user, struct handle_info *hi)
703 if (hi->email_addr) {
704 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
705 return hi->email_addr;
715 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
717 struct handle_info *hi;
718 struct userNode *target;
722 if (!(hi = get_handle_info(++name))) {
723 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
728 if (!(target = GetUserH(name))) {
729 send_message(user, service, "MSG_NICK_UNKNOWN", name);
732 if (IsLocal(target)) {
733 if (IsService(target))
734 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
736 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
739 if (!(hi = target->handle_info)) {
740 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
748 oper_outranks(struct userNode *user, struct handle_info *hi) {
749 if (user->handle_info->opserv_level > hi->opserv_level)
751 if (user->handle_info->opserv_level == hi->opserv_level) {
752 if ((user->handle_info->opserv_level == 1000)
753 || (user->handle_info == hi)
754 || ((user->handle_info->opserv_level == 0)
755 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
756 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
760 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
764 static struct handle_info *
765 get_victim_oper(struct userNode *user, const char *target)
767 struct handle_info *hi;
768 if (!(hi = smart_get_handle_info(nickserv, user, target)))
770 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
771 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
774 return oper_outranks(user, hi) ? hi : NULL;
778 valid_user_for(struct userNode *user, struct handle_info *hi)
782 /* If no hostmasks on the account, allow it. */
783 if (!hi->masks->used)
785 /* If any hostmask matches, allow it. */
786 for (ii=0; ii<hi->masks->used; ii++)
787 if (user_matches_glob(user, hi->masks->list[ii], 0))
789 /* If they are allowauthed to this account, allow it (removing the aa). */
790 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
791 dict_remove(nickserv_allow_auth_dict, user->nick);
794 /* The user is not allowed to use this account. */
799 is_secure_password(const char *handle, const char *pass, struct userNode *user)
802 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
806 if (len < nickserv_conf.password_min_length) {
808 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
811 if (!irccasecmp(pass, handle)) {
813 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
816 dict_find(nickserv_conf.weak_password_dict, pass, &p);
819 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
822 for (i=0; i<len; i++) {
823 if (isdigit(pass[i]))
825 if (isupper(pass[i]))
827 if (islower(pass[i]))
830 if ((cnt_lower < nickserv_conf.password_min_lower)
831 || (cnt_upper < nickserv_conf.password_min_upper)
832 || (cnt_digits < nickserv_conf.password_min_digits)) {
834 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
840 static auth_func_t *auth_func_list;
841 static unsigned int auth_func_size = 0, auth_func_used = 0;
844 reg_auth_func(auth_func_t func)
846 if (auth_func_used == auth_func_size) {
847 if (auth_func_size) {
848 auth_func_size <<= 1;
849 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
852 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
855 auth_func_list[auth_func_used++] = func;
858 static handle_rename_func_t *rf_list;
859 static unsigned int rf_list_size, rf_list_used;
862 reg_handle_rename_func(handle_rename_func_t func)
864 if (rf_list_used == rf_list_size) {
867 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
870 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
873 rf_list[rf_list_used++] = func;
877 generate_fakehost(struct handle_info *handle)
879 extern const char *hidden_host_suffix;
880 static char buffer[HOSTLEN+1];
882 if (!handle->fakehost) {
883 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
885 } else if (handle->fakehost[0] == '.') {
886 /* A leading dot indicates the stored value is actually a title. */
887 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
890 return handle->fakehost;
894 generate_fakeident(struct handle_info *handle, struct userNode *user)
896 static char buffer[USERLEN+1];
898 if (!handle->fakeident) {
901 safestrncpy(buffer, user->ident, sizeof(buffer));
904 return handle->fakeident;
908 apply_fakehost(struct handle_info *handle, struct userNode *user)
910 struct userNode *target;
911 char *fakehost, *fakeident;
916 fakehost = generate_fakehost(handle);
919 fakeident = generate_fakeident(handle, user);
920 assign_fakehost(user, fakehost, fakeident, 0, 1);
924 for (target = handle->users; target; target = target->next_authed) {
925 fakeident = generate_fakeident(handle, target);
926 assign_fakehost(target, fakehost, fakeident, 0, 1);
931 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
934 struct handle_info *old_info;
936 /* This can happen if somebody uses COOKIE while authed, or if
937 * they re-auth to their current handle (which is silly, but users
939 if (user->handle_info == hi)
942 if (user->handle_info) {
943 struct userNode *other;
946 userList_remove(&curr_helpers, user);
948 /* remove from next_authed linked list */
949 if (user->handle_info->users == user) {
950 user->handle_info->users = user->next_authed;
951 } else if (user->handle_info->users != NULL) {
952 for (other = user->handle_info->users;
953 other->next_authed != user;
954 other = other->next_authed) ;
955 other->next_authed = user->next_authed;
957 /* No users authed to the account - can happen if they get
958 * killed for authing. */
960 /* if nobody left on old handle, and they're not an oper, remove !god */
961 if (!user->handle_info->users && !user->handle_info->opserv_level)
962 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
963 /* record them as being last seen at this time */
964 user->handle_info->lastseen = now;
965 /* and record their hostmask */
966 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
968 old_info = user->handle_info;
969 user->handle_info = hi;
970 if (hi && !hi->users && !hi->opserv_level)
971 HANDLE_CLEAR_FLAG(hi, HELPING);
972 for (n=0; n<auth_func_used; n++) {
973 auth_func_list[n](user, old_info);
978 struct nick_info *ni;
980 HANDLE_CLEAR_FLAG(hi, FROZEN);
981 if (nickserv_conf.warn_clone_auth) {
982 struct userNode *other;
983 for (other = hi->users; other; other = other->next_authed)
984 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
986 user->next_authed = hi->users;
989 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
990 userList_append(&curr_helpers, user);
992 if (hi->fakehost || hi->fakeident || old_info)
993 apply_fakehost(hi, user);
996 if (!nickserv_conf.disable_nicks) {
997 struct nick_info *ni2;
998 for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
999 if (!irccasecmp(user->nick, ni2->nick)) {
1000 user->modes |= FLAGS_REGNICK;
1005 StampUser(user, hi->handle, hi->registered, hi->id);
1008 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
1009 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
1011 /* We cannot clear the user's account ID, unfortunately. */
1012 user->next_authed = NULL;
1016 static struct handle_info*
1017 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
1019 struct handle_info *hi;
1020 struct nick_info *ni;
1021 char crypted[MD5_CRYPT_LENGTH];
1023 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
1024 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
1028 if (!is_secure_password(handle, passwd, user))
1031 cryptpass(passwd, crypted);
1032 hi = register_handle(handle, crypted, 0);
1033 hi->masks = alloc_string_list(1);
1035 hi->language = lang_C;
1036 hi->registered = now;
1038 hi->flags = HI_DEFAULT_FLAGS;
1039 if (settee && !no_auth)
1040 set_user_handle_info(settee, hi, 1);
1043 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1044 else if (nickserv_conf.disable_nicks)
1045 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1046 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1047 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1049 register_nick(user->nick, hi);
1050 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1052 if (settee && (user != settee))
1053 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1058 nickserv_bake_cookie(struct handle_cookie *cookie)
1060 cookie->hi->cookie = cookie;
1061 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1065 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1067 struct handle_cookie *cookie;
1068 char subject[128], body[4096], *misc;
1069 const char *netname, *fmt;
1073 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1077 cookie = calloc(1, sizeof(*cookie));
1079 cookie->type = type;
1080 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1081 cookie->expires = now + nickserv_conf.cookie_timeout;
1082 inttobase64(cookie->cookie, rand(), 5);
1083 inttobase64(cookie->cookie+5, rand(), 5);
1085 netname = nickserv_conf.network_name;
1088 switch (cookie->type) {
1090 hi->passwd[0] = 0; /* invalidate password */
1091 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1092 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1093 snprintf(subject, sizeof(subject), fmt, netname);
1094 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1095 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1098 case PASSWORD_CHANGE:
1099 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1100 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1101 snprintf(subject, sizeof(subject), fmt, netname);
1102 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1103 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1106 misc = hi->email_addr;
1107 hi->email_addr = cookie->data;
1109 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1110 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1111 snprintf(subject, sizeof(subject), fmt, netname);
1112 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1113 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1114 mail_send(nickserv, hi, subject, body, 1);
1115 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1116 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1118 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1119 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1120 snprintf(subject, sizeof(subject), fmt, netname);
1121 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1122 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1123 mail_send(nickserv, hi, subject, body, 1);
1126 hi->email_addr = misc;
1129 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1130 snprintf(subject, sizeof(subject), fmt, netname);
1131 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1132 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1133 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1136 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1140 mail_send(nickserv, hi, subject, body, first_time);
1141 nickserv_bake_cookie(cookie);
1145 nickserv_eat_cookie(struct handle_cookie *cookie)
1147 cookie->hi->cookie = NULL;
1148 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1149 nickserv_free_cookie(cookie);
1153 nickserv_free_email_addr(void *data)
1155 handle_info_list_clean(data);
1160 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1162 struct handle_info_list *hil;
1163 /* Remove from old handle_info_list ... */
1164 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1165 handle_info_list_remove(hil, hi);
1166 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1167 hi->email_addr = NULL;
1169 /* Add to the new list.. */
1170 if (new_email_addr) {
1171 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1172 hil = calloc(1, sizeof(*hil));
1173 hil->tag = strdup(new_email_addr);
1174 handle_info_list_init(hil);
1175 dict_insert(nickserv_email_dict, hil->tag, hil);
1177 handle_info_list_append(hil, hi);
1178 hi->email_addr = hil->tag;
1182 static NICKSERV_FUNC(cmd_register)
1185 struct handle_info *hi;
1186 const char *email_addr, *password;
1189 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1190 /* Require the first handle registered to belong to someone +o. */
1191 reply("NSMSG_REQUIRE_OPER");
1195 if (user->handle_info) {
1196 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1200 if (IsRegistering(user)) {
1201 reply("NSMSG_ALREADY_REGISTERING");
1205 if (IsStamped(user)) {
1206 /* Unauthenticated users might still have been stamped
1207 previously and could therefore have a hidden host;
1208 do not allow them to register a new account. */
1209 reply("NSMSG_STAMPED_REGISTER");
1213 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1215 if (!is_valid_handle(argv[1])) {
1216 reply("NSMSG_BAD_HANDLE", argv[1]);
1220 if ((argc >= 4) && nickserv_conf.email_enabled) {
1221 struct handle_info_list *hil;
1224 /* Remember email address. */
1225 email_addr = argv[3];
1227 /* Check that the email address looks valid.. */
1228 if (!is_valid_email_addr(email_addr)) {
1229 reply("NSMSG_BAD_EMAIL_ADDR");
1233 /* .. and that we are allowed to send to it. */
1234 if ((str = mail_prohibited_address(email_addr))) {
1235 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1239 /* If we do email verify, make sure we don't spam the address. */
1240 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1242 for (nn=0; nn<hil->used; nn++) {
1243 if (hil->list[nn]->cookie) {
1244 reply("NSMSG_EMAIL_UNACTIVATED");
1248 if (hil->used >= nickserv_conf.handles_per_email) {
1249 reply("NSMSG_EMAIL_OVERUSED");
1262 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1264 /* Add any masks they should get. */
1265 if (nickserv_conf.default_hostmask) {
1266 string_list_append(hi->masks, strdup("*@*"));
1268 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1269 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1270 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1273 /* If they're the first to register, give them level 1000. */
1274 if (dict_size(nickserv_handle_dict) == 1) {
1275 hi->opserv_level = 1000;
1276 reply("NSMSG_ROOT_HANDLE", argv[1]);
1279 /* Set their email address. */
1281 nickserv_set_email_addr(hi, email_addr);
1283 /* If they need to do email verification, tell them. */
1285 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1287 /* Set registering flag.. */
1288 user->modes |= FLAGS_REGISTERING;
1293 static NICKSERV_FUNC(cmd_oregister)
1296 struct userNode *settee;
1297 struct handle_info *hi;
1299 NICKSERV_MIN_PARMS(3);
1301 if (!is_valid_handle(argv[1])) {
1302 reply("NSMSG_BAD_HANDLE", argv[1]);
1309 } else if (strchr(argv[3], '@')) {
1310 mask = canonicalize_hostmask(strdup(argv[3]));
1312 settee = GetUserH(argv[4]);
1314 reply("MSG_NICK_UNKNOWN", argv[4]);
1321 } else if ((settee = GetUserH(argv[3]))) {
1322 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1324 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1327 if (settee && settee->handle_info) {
1328 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1332 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1337 string_list_append(hi->masks, mask);
1341 static NICKSERV_FUNC(cmd_handleinfo)
1344 unsigned int i, pos=0, herelen;
1345 struct userNode *target, *next_un;
1346 struct handle_info *hi;
1347 const char *nsmsg_none;
1351 if (!(hi = user->handle_info)) {
1352 reply("NSMSG_MUST_AUTH");
1355 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1359 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1360 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1361 feh = hi->registered;
1362 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1365 intervalString(buff, now - hi->lastseen, user->handle_info);
1366 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1368 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1371 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1372 if (HANDLE_FLAGGED(hi, FROZEN))
1373 reply("NSMSG_HANDLEINFO_VACATION");
1375 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1376 struct do_not_register *dnr;
1377 if ((dnr = chanserv_is_dnr(NULL, hi)))
1378 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1379 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1381 } else if (hi != user->handle_info)
1385 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1387 if (nickserv_conf.email_enabled)
1388 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1392 switch (hi->cookie->type) {
1393 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1394 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1395 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1396 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1397 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1402 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1403 reply("NSMSG_HANDLEINFO_ID", hi->id);
1405 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1407 reply("NSMSG_HANDLEINFO_NO_NOTES");
1409 struct handle_note *prev, *note;
1411 WALK_NOTES(hi, prev, note) {
1412 char set_time[INTERVALLEN];
1413 intervalString(set_time, now - note->set, user->handle_info);
1414 if (note->expires) {
1415 char exp_time[INTERVALLEN];
1416 intervalString(exp_time, note->expires - now, user->handle_info);
1417 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1419 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1426 unsigned long flen = 1;
1427 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1429 for (i=0, flen=1; handle_flags[i]; i++)
1430 if (hi->flags & 1 << i)
1431 flags[flen++] = handle_flags[i];
1433 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1435 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1438 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1439 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1440 || (hi->opserv_level > 0)) {
1441 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1445 reply("NSMSG_HANDLEINFO_FAKEIDENT", (hi->fakeident ? hi->fakeident : handle_find_message(hi, "MSG_NONE")));
1448 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1450 if (hi->last_quit_host[0])
1451 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1453 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1455 if (nickserv_conf.disable_nicks) {
1456 /* nicks disabled; don't show anything about registered nicks */
1457 } else if (hi->nicks) {
1458 struct nick_info *ni, *next_ni;
1459 for (ni = hi->nicks; ni; ni = next_ni) {
1460 herelen = strlen(ni->nick);
1461 if (pos + herelen + 1 > ArrayLength(buff)) {
1463 goto print_nicks_buff;
1467 memcpy(buff+pos, ni->nick, herelen);
1468 pos += herelen; buff[pos++] = ' ';
1472 reply("NSMSG_HANDLEINFO_NICKS", buff);
1477 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1480 if (hi->masks->used) {
1481 for (i=0; i < hi->masks->used; i++) {
1482 herelen = strlen(hi->masks->list[i]);
1483 if (pos + herelen + 1 > ArrayLength(buff)) {
1485 goto print_mask_buff;
1487 memcpy(buff+pos, hi->masks->list[i], herelen);
1488 pos += herelen; buff[pos++] = ' ';
1489 if (i+1 == hi->masks->used) {
1492 reply("NSMSG_HANDLEINFO_MASKS", buff);
1497 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1501 struct userData *chan, *next;
1504 for (chan = hi->channels; chan; chan = next) {
1505 next = chan->u_next;
1506 name = chan->channel->channel->name;
1507 herelen = strlen(name);
1508 if (pos + herelen + 7 > ArrayLength(buff)) {
1510 goto print_chans_buff;
1512 if (IsUserSuspended(chan))
1514 pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
1518 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1523 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1526 for (target = hi->users; target; target = next_un) {
1527 herelen = strlen(target->nick);
1528 if (pos + herelen + 1 > ArrayLength(buff)) {
1530 goto print_cnick_buff;
1532 next_un = target->next_authed;
1534 memcpy(buff+pos, target->nick, herelen);
1535 pos += herelen; buff[pos++] = ' ';
1539 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1544 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1547 static NICKSERV_FUNC(cmd_userinfo)
1549 struct userNode *target;
1551 NICKSERV_MIN_PARMS(2);
1552 if (!(target = GetUserH(argv[1]))) {
1553 reply("MSG_NICK_UNKNOWN", argv[1]);
1556 if (target->handle_info)
1557 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1559 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1563 static NICKSERV_FUNC(cmd_nickinfo)
1565 struct nick_info *ni;
1567 NICKSERV_MIN_PARMS(2);
1568 if (!(ni = get_nick_info(argv[1]))) {
1569 reply("MSG_NICK_UNKNOWN", argv[1]);
1572 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1576 static NICKSERV_FUNC(cmd_notes)
1578 struct handle_info *hi;
1579 struct handle_note *prev, *note;
1582 NICKSERV_MIN_PARMS(2);
1583 if (!(hi = get_victim_oper(user, argv[1])))
1586 WALK_NOTES(hi, prev, note) {
1587 char set_time[INTERVALLEN];
1588 intervalString(set_time, now - note->set, user->handle_info);
1589 if (note->expires) {
1590 char exp_time[INTERVALLEN];
1591 intervalString(exp_time, note->expires - now, user->handle_info);
1592 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1594 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1598 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1602 static NICKSERV_FUNC(cmd_rename_handle)
1604 struct handle_info *hi;
1605 char msgbuf[MAXLEN], *old_handle;
1608 NICKSERV_MIN_PARMS(3);
1609 if (!(hi = get_victim_oper(user, argv[1])))
1611 if (!is_valid_handle(argv[2])) {
1612 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1615 if (get_handle_info(argv[2])) {
1616 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1620 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1621 hi->handle = strdup(argv[2]);
1622 dict_insert(nickserv_handle_dict, hi->handle, hi);
1623 for (nn=0; nn<rf_list_used; nn++)
1624 rf_list[nn](hi, old_handle);
1625 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1626 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1627 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1632 static failpw_func_t *failpw_func_list;
1633 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1636 reg_failpw_func(failpw_func_t func)
1638 if (failpw_func_used == failpw_func_size) {
1639 if (failpw_func_size) {
1640 failpw_func_size <<= 1;
1641 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1643 failpw_func_size = 8;
1644 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1647 failpw_func_list[failpw_func_used++] = func;
1650 static NICKSERV_FUNC(cmd_auth)
1652 int pw_arg, used, maxlogins;
1653 struct handle_info *hi;
1655 struct userNode *other;
1657 if (user->handle_info) {
1658 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1661 if (IsStamped(user)) {
1662 /* Unauthenticated users might still have been stamped
1663 previously and could therefore have a hidden host;
1664 do not allow them to authenticate. */
1665 reply("NSMSG_STAMPED_AUTH");
1669 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1671 } else if (argc == 2) {
1672 if (nickserv_conf.disable_nicks) {
1673 if (!(hi = get_handle_info(user->nick))) {
1674 reply("NSMSG_HANDLE_NOT_FOUND");
1678 /* try to look up their handle from their nick */
1679 struct nick_info *ni;
1680 ni = get_nick_info(user->nick);
1682 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1689 reply("MSG_MISSING_PARAMS", argv[0]);
1690 svccmd_send_help(user, nickserv, cmd);
1694 reply("NSMSG_HANDLE_NOT_FOUND");
1697 /* Responses from here on look up the language used by the handle they asked about. */
1698 passwd = argv[pw_arg];
1699 if (!valid_user_for(user, hi)) {
1700 if (hi->email_addr && nickserv_conf.email_enabled)
1701 send_message_type(4, user, cmd->parent->bot,
1702 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1705 send_message_type(4, user, cmd->parent->bot,
1706 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1708 argv[pw_arg] = "BADMASK";
1711 if (!checkpass(passwd, hi->passwd)) {
1713 send_message_type(4, user, cmd->parent->bot,
1714 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1715 argv[pw_arg] = "BADPASS";
1716 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1717 if (nickserv_conf.autogag_enabled) {
1718 if (!user->auth_policer.params) {
1719 user->auth_policer.last_req = now;
1720 user->auth_policer.params = nickserv_conf.auth_policer_params;
1722 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1724 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1725 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1726 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1728 argv[pw_arg] = "GAGGED";
1733 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1734 send_message_type(4, user, cmd->parent->bot,
1735 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1736 argv[pw_arg] = "SUSPENDED";
1739 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1740 for (used = 0, other = hi->users; other; other = other->next_authed) {
1741 if (++used >= maxlogins) {
1742 send_message_type(4, user, cmd->parent->bot,
1743 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1745 argv[pw_arg] = "MAXLOGINS";
1750 set_user_handle_info(user, hi, 1);
1751 if (nickserv_conf.email_required && !hi->email_addr)
1752 reply("NSMSG_PLEASE_SET_EMAIL");
1753 if (!is_secure_password(hi->handle, passwd, NULL))
1754 reply("NSMSG_WEAK_PASSWORD");
1755 if (hi->passwd[0] != '$')
1756 cryptpass(passwd, hi->passwd);
1757 if (!hi->masks->used) {
1759 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1760 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1761 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1763 argv[pw_arg] = "****";
1764 reply("NSMSG_AUTH_SUCCESS");
1768 static allowauth_func_t *allowauth_func_list;
1769 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1772 reg_allowauth_func(allowauth_func_t func)
1774 if (allowauth_func_used == allowauth_func_size) {
1775 if (allowauth_func_size) {
1776 allowauth_func_size <<= 1;
1777 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1779 allowauth_func_size = 8;
1780 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1783 allowauth_func_list[allowauth_func_used++] = func;
1786 static NICKSERV_FUNC(cmd_allowauth)
1788 struct userNode *target;
1789 struct handle_info *hi;
1792 NICKSERV_MIN_PARMS(2);
1793 if (!(target = GetUserH(argv[1]))) {
1794 reply("MSG_NICK_UNKNOWN", argv[1]);
1797 if (target->handle_info) {
1798 reply("NSMSG_USER_PREV_AUTH", target->nick);
1801 if (IsStamped(target)) {
1802 /* Unauthenticated users might still have been stamped
1803 previously and could therefore have a hidden host;
1804 do not allow them to authenticate to an account. */
1805 reply("NSMSG_USER_PREV_STAMP", target->nick);
1810 else if (!(hi = get_handle_info(argv[2]))) {
1811 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1815 if (hi->opserv_level > user->handle_info->opserv_level) {
1816 reply("MSG_USER_OUTRANKED", hi->handle);
1819 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1820 || (hi->opserv_level > 0))
1821 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1822 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1825 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1826 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1827 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1828 if (nickserv_conf.email_enabled)
1829 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1831 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1832 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1834 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1836 for (n=0; n<allowauth_func_used; n++)
1837 allowauth_func_list[n](user, target, hi);
1841 static NICKSERV_FUNC(cmd_authcookie)
1843 struct handle_info *hi;
1845 NICKSERV_MIN_PARMS(2);
1846 if (user->handle_info) {
1847 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1850 if (IsStamped(user)) {
1851 /* Unauthenticated users might still have been stamped
1852 previously and could therefore have a hidden host;
1853 do not allow them to authenticate to an account. */
1854 reply("NSMSG_STAMPED_AUTHCOOKIE");
1857 if (!(hi = get_handle_info(argv[1]))) {
1858 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1861 if (!hi->email_addr) {
1862 reply("MSG_SET_EMAIL_ADDR");
1865 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1869 static NICKSERV_FUNC(cmd_delcookie)
1871 struct handle_info *hi;
1873 hi = user->handle_info;
1875 reply("NSMSG_NO_COOKIE");
1878 switch (hi->cookie->type) {
1881 reply("NSMSG_MUST_TIME_OUT");
1884 nickserv_eat_cookie(hi->cookie);
1885 reply("NSMSG_ATE_COOKIE");
1891 static NICKSERV_FUNC(cmd_odelcookie)
1893 struct handle_info *hi;
1895 NICKSERV_MIN_PARMS(2);
1897 if (!(hi = get_victim_oper(user, argv[1])))
1901 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
1905 nickserv_eat_cookie(hi->cookie);
1906 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
1911 static NICKSERV_FUNC(cmd_resetpass)
1913 struct handle_info *hi;
1914 char crypted[MD5_CRYPT_LENGTH];
1916 NICKSERV_MIN_PARMS(3);
1917 if (user->handle_info) {
1918 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1921 if (IsStamped(user)) {
1922 /* Unauthenticated users might still have been stamped
1923 previously and could therefore have a hidden host;
1924 do not allow them to activate an account. */
1925 reply("NSMSG_STAMPED_RESETPASS");
1928 if (!(hi = get_handle_info(argv[1]))) {
1929 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1932 if (!hi->email_addr) {
1933 reply("MSG_SET_EMAIL_ADDR");
1936 cryptpass(argv[2], crypted);
1938 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1942 static NICKSERV_FUNC(cmd_cookie)
1944 struct handle_info *hi;
1947 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1950 NICKSERV_MIN_PARMS(3);
1951 if (!(hi = get_handle_info(argv[1]))) {
1952 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1958 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1959 reply("NSMSG_HANDLE_SUSPENDED");
1964 reply("NSMSG_NO_COOKIE");
1968 /* Check validity of operation before comparing cookie to
1969 * prohibit guessing by authed users. */
1970 if (user->handle_info
1971 && (hi->cookie->type != EMAIL_CHANGE)
1972 && (hi->cookie->type != PASSWORD_CHANGE)) {
1973 reply("NSMSG_CANNOT_COOKIE");
1977 if (strcmp(cookie, hi->cookie->cookie)) {
1978 reply("NSMSG_BAD_COOKIE");
1982 switch (hi->cookie->type) {
1984 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1985 set_user_handle_info(user, hi, 1);
1986 reply("NSMSG_HANDLE_ACTIVATED");
1988 case PASSWORD_CHANGE:
1989 set_user_handle_info(user, hi, 1);
1990 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1991 reply("NSMSG_PASSWORD_CHANGED");
1994 nickserv_set_email_addr(hi, hi->cookie->data);
1995 reply("NSMSG_EMAIL_CHANGED");
1998 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1999 set_user_handle_info(user, hi, 1);
2000 nickserv_addmask(user, hi, mask);
2001 reply("NSMSG_AUTH_SUCCESS");
2006 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2007 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2011 nickserv_eat_cookie(hi->cookie);
2016 static NICKSERV_FUNC(cmd_oregnick) {
2018 struct handle_info *target;
2019 struct nick_info *ni;
2021 NICKSERV_MIN_PARMS(3);
2022 if (!(target = modcmd_get_handle_info(user, argv[1])))
2025 if (!is_registerable_nick(nick)) {
2026 reply("NSMSG_BAD_NICK", nick);
2029 ni = dict_find(nickserv_nick_dict, nick, NULL);
2031 reply("NSMSG_NICK_EXISTS", nick);
2034 register_nick(nick, target);
2035 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2039 static NICKSERV_FUNC(cmd_regnick) {
2041 struct nick_info *ni;
2043 if (!is_registerable_nick(user->nick)) {
2044 reply("NSMSG_BAD_NICK", user->nick);
2047 /* count their nicks, see if it's too many */
2048 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2049 if (n >= nickserv_conf.nicks_per_handle) {
2050 reply("NSMSG_TOO_MANY_NICKS");
2053 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2055 reply("NSMSG_NICK_EXISTS", user->nick);
2058 register_nick(user->nick, user->handle_info);
2059 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2063 static NICKSERV_FUNC(cmd_pass)
2065 struct handle_info *hi;
2066 const char *old_pass, *new_pass;
2068 NICKSERV_MIN_PARMS(3);
2069 hi = user->handle_info;
2073 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2074 if (!checkpass(old_pass, hi->passwd)) {
2075 argv[1] = "BADPASS";
2076 reply("NSMSG_PASSWORD_INVALID");
2079 cryptpass(new_pass, hi->passwd);
2081 reply("NSMSG_PASS_SUCCESS");
2086 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2089 char *new_mask = canonicalize_hostmask(strdup(mask));
2090 for (i=0; i<hi->masks->used; i++) {
2091 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2092 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2097 string_list_append(hi->masks, new_mask);
2098 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2102 static NICKSERV_FUNC(cmd_addmask)
2105 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2106 int res = nickserv_addmask(user, user->handle_info, mask);
2110 if (!is_gline(argv[1])) {
2111 reply("NSMSG_MASK_INVALID", argv[1]);
2114 return nickserv_addmask(user, user->handle_info, argv[1]);
2118 static NICKSERV_FUNC(cmd_oaddmask)
2120 struct handle_info *hi;
2122 NICKSERV_MIN_PARMS(3);
2123 if (!(hi = get_victim_oper(user, argv[1])))
2125 return nickserv_addmask(user, hi, argv[2]);
2129 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2132 for (i=0; i<hi->masks->used; i++) {
2133 if (!strcmp(del_mask, hi->masks->list[i])) {
2134 char *old_mask = hi->masks->list[i];
2135 if (hi->masks->used == 1 && !force) {
2136 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2139 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2140 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2145 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2149 static NICKSERV_FUNC(cmd_delmask)
2151 NICKSERV_MIN_PARMS(2);
2152 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2155 static NICKSERV_FUNC(cmd_odelmask)
2157 struct handle_info *hi;
2158 NICKSERV_MIN_PARMS(3);
2159 if (!(hi = get_victim_oper(user, argv[1])))
2161 return nickserv_delmask(user, hi, argv[2], 1);
2165 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2166 unsigned int nn, add = 1, pos;
2167 unsigned long added, removed, flag;
2169 for (added=removed=nn=0; str[nn]; nn++) {
2171 case '+': add = 1; break;
2172 case '-': add = 0; break;
2174 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2175 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2178 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2179 /* cheesy avoidance of looking up the flag name.. */
2180 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2183 flag = 1 << (pos - 1);
2185 added |= flag, removed &= ~flag;
2187 removed |= flag, added &= ~flag;
2192 *premoved = removed;
2197 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2199 unsigned long before, after, added, removed;
2200 struct userNode *uNode;
2202 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2203 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2205 hi->flags = (hi->flags | added) & ~removed;
2206 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2208 /* Strip helping flag if they're only a support helper and not
2209 * currently in #support. */
2210 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2211 struct channelList *schannels;
2213 schannels = chanserv_support_channels();
2214 for (ii = 0; ii < schannels->used; ++ii)
2215 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2217 if (ii == schannels->used)
2218 HANDLE_CLEAR_FLAG(hi, HELPING);
2221 if (after && !before) {
2222 /* Add user to current helper list. */
2223 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2224 userList_append(&curr_helpers, uNode);
2225 } else if (!after && before) {
2226 /* Remove user from current helper list. */
2227 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2228 userList_remove(&curr_helpers, uNode);
2235 set_list(struct userNode *user, struct handle_info *hi, int override)
2239 char *set_display[] = {
2240 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2241 "EMAIL", "MAXLOGINS", "LANGUAGE"
2244 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2246 /* Do this so options are presented in a consistent order. */
2247 for (i = 0; i < ArrayLength(set_display); ++i)
2248 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2249 opt(user, hi, override, 0, NULL);
2252 static NICKSERV_FUNC(cmd_set)
2254 struct handle_info *hi;
2257 hi = user->handle_info;
2259 set_list(user, hi, 0);
2262 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2263 reply("NSMSG_INVALID_OPTION", argv[1]);
2266 return opt(user, hi, 0, argc-1, argv+1);
2269 static NICKSERV_FUNC(cmd_oset)
2271 struct handle_info *hi;
2272 struct svccmd *subcmd;
2274 char cmdname[MAXLEN];
2276 NICKSERV_MIN_PARMS(2);
2278 if (!(hi = get_victim_oper(user, argv[1])))
2282 set_list(user, hi, 0);
2286 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2287 reply("NSMSG_INVALID_OPTION", argv[2]);
2291 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2292 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2293 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2296 return opt(user, hi, 1, argc-2, argv+2);
2299 static OPTION_FUNC(opt_info)
2303 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2305 hi->infoline = NULL;
2307 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2311 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2312 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2316 static OPTION_FUNC(opt_width)
2319 hi->screen_width = strtoul(argv[1], NULL, 0);
2321 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2322 hi->screen_width = MIN_LINE_SIZE;
2323 else if (hi->screen_width > MAX_LINE_SIZE)
2324 hi->screen_width = MAX_LINE_SIZE;
2326 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2330 static OPTION_FUNC(opt_tablewidth)
2333 hi->table_width = strtoul(argv[1], NULL, 0);
2335 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2336 hi->table_width = MIN_LINE_SIZE;
2337 else if (hi->screen_width > MAX_LINE_SIZE)
2338 hi->table_width = MAX_LINE_SIZE;
2340 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2344 static OPTION_FUNC(opt_color)
2347 if (enabled_string(argv[1]))
2348 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2349 else if (disabled_string(argv[1]))
2350 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2352 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2357 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2361 static OPTION_FUNC(opt_privmsg)
2364 if (enabled_string(argv[1]))
2365 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2366 else if (disabled_string(argv[1]))
2367 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2369 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2374 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2378 static OPTION_FUNC(opt_style)
2383 if (!irccasecmp(argv[1], "Zoot"))
2384 hi->userlist_style = HI_STYLE_ZOOT;
2385 else if (!irccasecmp(argv[1], "def"))
2386 hi->userlist_style = HI_STYLE_DEF;
2389 switch (hi->userlist_style) {
2398 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2402 static OPTION_FUNC(opt_password)
2405 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2410 cryptpass(argv[1], hi->passwd);
2412 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2416 static OPTION_FUNC(opt_flags)
2419 unsigned int ii, flen;
2422 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2427 nickserv_apply_flags(user, hi, argv[1]);
2429 for (ii = flen = 0; handle_flags[ii]; ii++)
2430 if (hi->flags & (1 << ii))
2431 flags[flen++] = handle_flags[ii];
2434 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2436 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2440 static OPTION_FUNC(opt_email)
2444 if (!is_valid_email_addr(argv[1])) {
2445 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2448 if ((str = mail_prohibited_address(argv[1]))) {
2449 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2452 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2453 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2455 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2457 nickserv_set_email_addr(hi, argv[1]);
2459 nickserv_eat_cookie(hi->cookie);
2460 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2463 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2467 static OPTION_FUNC(opt_maxlogins)
2469 unsigned char maxlogins;
2471 maxlogins = strtoul(argv[1], NULL, 0);
2472 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2473 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2476 hi->maxlogins = maxlogins;
2478 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2479 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2483 static OPTION_FUNC(opt_language)
2485 struct language *lang;
2487 lang = language_find(argv[1]);
2488 if (irccasecmp(lang->name, argv[1]))
2489 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2490 hi->language = lang;
2492 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2496 static OPTION_FUNC(opt_karma)
2499 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2504 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2505 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2506 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2507 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2509 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2513 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2518 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2519 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2521 if ((user->handle_info->opserv_level < target->opserv_level)
2522 || ((user->handle_info->opserv_level == target->opserv_level)
2523 && (user->handle_info->opserv_level < 1000))) {
2524 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2527 if ((user->handle_info->opserv_level < new_level)
2528 || ((user->handle_info->opserv_level == new_level)
2529 && (user->handle_info->opserv_level < 1000))) {
2530 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2533 if (user->handle_info == target) {
2534 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2537 if (target->opserv_level == new_level)
2539 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2540 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2541 target->opserv_level = new_level;
2545 static OPTION_FUNC(opt_level)
2550 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2554 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2555 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2559 static OPTION_FUNC(opt_epithet)
2562 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2566 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2567 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2570 if ((epithet[0] == '*') && !epithet[1])
2573 hi->epithet = strdup(epithet);
2577 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2579 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2583 static OPTION_FUNC(opt_title)
2588 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2592 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2594 if (strchr(title, '.')) {
2595 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2598 if ((strlen(user->handle_info->handle) + strlen(title) +
2599 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2600 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2605 if (!strcmp(title, "*")) {
2606 hi->fakehost = NULL;
2608 hi->fakehost = malloc(strlen(title)+2);
2609 hi->fakehost[0] = '.';
2610 strcpy(hi->fakehost+1, title);
2612 apply_fakehost(hi, NULL);
2613 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2614 title = hi->fakehost + 1;
2618 title = user_find_message(user, "MSG_NONE");
2619 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2623 static OPTION_FUNC(opt_fakehost)
2625 char mask[USERLEN + HOSTLEN + 2];
2629 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2633 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2634 safestrncpy(mask, argv[1], sizeof(mask));
2636 if ((host = strrchr(mask, '@')) && host != mask &&
2637 oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2645 if ((strlen(host) > HOSTLEN) || (host[0] == '.')) {
2646 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2650 if (ident && strlen(ident) > USERLEN) {
2651 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2656 if (!strcmp(host, "*"))
2657 hi->fakehost = NULL;
2659 hi->fakehost = strdup(host);
2660 host = hi->fakehost;
2663 free(hi->fakeident);
2664 if (!strcmp(ident, "*"))
2665 hi->fakeident = NULL;
2667 hi->fakeident = strdup(ident);
2668 ident = hi->fakeident;
2671 apply_fakehost(hi, NULL);
2673 host = generate_fakehost(hi);
2674 ident = generate_fakeident(hi, NULL);
2677 host = (char *) user_find_message(user, "MSG_NONE");
2678 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
2680 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
2684 static OPTION_FUNC(opt_fakeident)
2689 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2693 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2695 if (strlen(ident) > USERLEN) {
2696 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2699 free(hi->fakeident);
2700 if (!strcmp(ident, "*"))
2701 hi->fakeident = NULL;
2703 hi->fakeident = strdup(ident);
2704 ident = hi->fakeident;
2705 apply_fakehost(hi, NULL);
2707 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
2709 ident = user_find_message(user, "MSG_NONE");
2710 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
2714 static NICKSERV_FUNC(cmd_reclaim)
2716 struct handle_info *hi;
2717 struct nick_info *ni;
2718 struct userNode *victim;
2720 NICKSERV_MIN_PARMS(2);
2721 hi = user->handle_info;
2722 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2724 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2727 if (ni->owner != user->handle_info) {
2728 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2731 victim = GetUserH(ni->nick);
2733 reply("MSG_NICK_UNKNOWN", ni->nick);
2736 if (victim == user) {
2737 reply("NSMSG_NICK_USER_YOU");
2740 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2741 switch (nickserv_conf.reclaim_action) {
2742 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2743 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2744 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2745 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2750 static NICKSERV_FUNC(cmd_unregnick)
2753 struct handle_info *hi;
2754 struct nick_info *ni;
2756 hi = user->handle_info;
2757 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2758 ni = dict_find(nickserv_nick_dict, nick, NULL);
2760 reply("NSMSG_UNKNOWN_NICK", nick);
2763 if (hi != ni->owner) {
2764 reply("NSMSG_NOT_YOUR_NICK", nick);
2767 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2772 static NICKSERV_FUNC(cmd_ounregnick)
2774 struct nick_info *ni;
2776 NICKSERV_MIN_PARMS(2);
2777 if (!(ni = get_nick_info(argv[1]))) {
2778 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2781 if (!oper_outranks(user, ni->owner))
2783 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2788 static NICKSERV_FUNC(cmd_unregister)
2790 struct handle_info *hi;
2793 NICKSERV_MIN_PARMS(2);
2794 hi = user->handle_info;
2797 if (checkpass(passwd, hi->passwd)) {
2798 nickserv_unregister_handle(hi, user);
2801 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2802 reply("NSMSG_PASSWORD_INVALID");
2807 static NICKSERV_FUNC(cmd_ounregister)
2809 struct handle_info *hi;
2810 char reason[MAXLEN];
2813 NICKSERV_MIN_PARMS(2);
2814 if (!(hi = get_victim_oper(user, argv[1])))
2817 if (HANDLE_FLAGGED(hi, NODELETE)) {
2818 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
2822 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
2824 ((hi->flags & nickserv_conf.ounregister_flags)
2826 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
2827 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
2831 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2832 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2833 nickserv_unregister_handle(hi, user);
2837 static NICKSERV_FUNC(cmd_status)
2839 if (nickserv_conf.disable_nicks) {
2840 reply("NSMSG_GLOBAL_STATS_NONICK",
2841 dict_size(nickserv_handle_dict));
2843 if (user->handle_info) {
2845 struct nick_info *ni;
2846 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2847 reply("NSMSG_HANDLE_STATS", cnt);
2849 reply("NSMSG_HANDLE_NONE");
2851 reply("NSMSG_GLOBAL_STATS",
2852 dict_size(nickserv_handle_dict),
2853 dict_size(nickserv_nick_dict));
2858 static NICKSERV_FUNC(cmd_ghost)
2860 struct userNode *target;
2861 char reason[MAXLEN];
2863 NICKSERV_MIN_PARMS(2);
2864 if (!(target = GetUserH(argv[1]))) {
2865 reply("MSG_NICK_UNKNOWN", argv[1]);
2868 if (target == user) {
2869 reply("NSMSG_CANNOT_GHOST_SELF");
2872 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2873 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2876 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2877 DelUser(target, nickserv, 1, reason);
2878 reply("NSMSG_GHOST_KILLED", argv[1]);
2882 static NICKSERV_FUNC(cmd_vacation)
2884 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2885 reply("NSMSG_ON_VACATION");
2889 static NICKSERV_FUNC(cmd_addnote)
2891 struct handle_info *hi;
2892 unsigned long duration;
2895 struct handle_note *prev;
2896 struct handle_note *note;
2898 /* Parse parameters and figure out values for note's fields. */
2899 NICKSERV_MIN_PARMS(4);
2900 hi = get_victim_oper(user, argv[1]);
2903 if(!strcmp(argv[2], "0"))
2905 else if(!(duration = ParseInterval(argv[2])))
2907 reply("MSG_INVALID_DURATION", argv[2]);
2910 if (duration > 2*365*86400) {
2911 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2914 unsplit_string(argv + 3, argc - 3, text);
2915 WALK_NOTES(hi, prev, note) {}
2916 id = prev ? (prev->id + 1) : 1;
2918 /* Create the new note structure. */
2919 note = calloc(1, sizeof(*note) + strlen(text));
2921 note->expires = duration ? (now + duration) : 0;
2924 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2925 strcpy(note->note, text);
2930 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2934 static NICKSERV_FUNC(cmd_delnote)
2936 struct handle_info *hi;
2937 struct handle_note *prev;
2938 struct handle_note *note;
2941 NICKSERV_MIN_PARMS(3);
2942 hi = get_victim_oper(user, argv[1]);
2945 id = strtoul(argv[2], NULL, 10);
2946 WALK_NOTES(hi, prev, note) {
2947 if (id == note->id) {
2949 prev->next = note->next;
2951 hi->notes = note->next;
2953 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2957 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2962 nickserv_saxdb_write(struct saxdb_context *ctx) {
2964 struct handle_info *hi;
2967 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2969 assert(hi->id != 0);
2970 saxdb_start_record(ctx, iter_key(it), 0);
2972 struct handle_cookie *cookie = hi->cookie;
2975 switch (cookie->type) {
2976 case ACTIVATION: type = KEY_ACTIVATION; break;
2977 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2978 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2979 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2980 default: type = NULL; break;
2983 saxdb_start_record(ctx, KEY_COOKIE, 0);
2984 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2985 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2987 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2988 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2989 saxdb_end_record(ctx);
2993 struct handle_note *prev, *note;
2994 saxdb_start_record(ctx, KEY_NOTES, 0);
2995 WALK_NOTES(hi, prev, note) {
2996 snprintf(flags, sizeof(flags), "%d", note->id);
2997 saxdb_start_record(ctx, flags, 0);
2999 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3000 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3001 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3002 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3003 saxdb_end_record(ctx);
3005 saxdb_end_record(ctx);
3008 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3010 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3012 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3014 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3018 for (ii=flen=0; handle_flags[ii]; ++ii)
3019 if (hi->flags & (1 << ii))
3020 flags[flen++] = handle_flags[ii];
3022 saxdb_write_string(ctx, KEY_FLAGS, flags);
3024 saxdb_write_int(ctx, KEY_ID, hi->id);
3026 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3027 if (hi->last_quit_host[0])
3028 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3029 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3031 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3032 if (hi->masks->used)
3033 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3035 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3037 struct string_list *slist;
3038 struct nick_info *ni;
3040 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3041 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3042 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3046 if (hi->opserv_level)
3047 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3048 if (hi->language != lang_C)
3049 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3050 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3051 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3052 if (hi->screen_width)
3053 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3054 if (hi->table_width)
3055 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3056 flags[0] = hi->userlist_style;
3058 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3059 saxdb_end_record(ctx);
3064 static handle_merge_func_t *handle_merge_func_list;
3065 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3068 reg_handle_merge_func(handle_merge_func_t func)
3070 if (handle_merge_func_used == handle_merge_func_size) {
3071 if (handle_merge_func_size) {
3072 handle_merge_func_size <<= 1;
3073 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3075 handle_merge_func_size = 8;
3076 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3079 handle_merge_func_list[handle_merge_func_used++] = func;
3082 static NICKSERV_FUNC(cmd_merge)
3084 struct handle_info *hi_from, *hi_to;
3085 struct userNode *last_user;
3086 struct userData *cList, *cListNext;
3087 unsigned int ii, jj, n;
3088 char buffer[MAXLEN];
3090 NICKSERV_MIN_PARMS(3);
3092 if (!(hi_from = get_victim_oper(user, argv[1])))
3094 if (!(hi_to = get_victim_oper(user, argv[2])))
3096 if (hi_to == hi_from) {
3097 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3101 for (n=0; n<handle_merge_func_used; n++)
3102 handle_merge_func_list[n](user, hi_to, hi_from);
3104 /* Append "from" handle's nicks to "to" handle's nick list. */
3106 struct nick_info *last_ni;
3107 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3108 last_ni->next = hi_from->nicks;
3110 while (hi_from->nicks) {
3111 hi_from->nicks->owner = hi_to;
3112 hi_from->nicks = hi_from->nicks->next;
3115 /* Merge the hostmasks. */
3116 for (ii=0; ii<hi_from->masks->used; ii++) {
3117 char *mask = hi_from->masks->list[ii];
3118 for (jj=0; jj<hi_to->masks->used; jj++)
3119 if (match_ircglobs(hi_to->masks->list[jj], mask))
3121 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3122 string_list_append(hi_to->masks, strdup(mask));
3125 /* Merge the lists of authed users. */
3127 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3128 last_user->next_authed = hi_from->users;
3130 hi_to->users = hi_from->users;
3132 /* Repoint the old "from" handle's users. */
3133 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3134 last_user->handle_info = hi_to;
3136 hi_from->users = NULL;
3138 /* Merge channel userlists. */
3139 for (cList=hi_from->channels; cList; cList=cListNext) {
3140 struct userData *cList2;
3141 cListNext = cList->u_next;
3142 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3143 if (cList->channel == cList2->channel)
3145 if (cList2 && (cList2->access >= cList->access)) {
3146 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);
3147 /* keep cList2 in hi_to; remove cList from hi_from */
3148 del_channel_user(cList, 1);
3151 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);
3152 /* remove the lower-ranking cList2 from hi_to */
3153 del_channel_user(cList2, 1);
3155 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3157 /* cList needs to be moved from hi_from to hi_to */
3158 cList->handle = hi_to;
3159 /* Remove from linked list for hi_from */
3160 assert(!cList->u_prev);
3161 hi_from->channels = cList->u_next;
3163 cList->u_next->u_prev = cList->u_prev;
3164 /* Add to linked list for hi_to */
3165 cList->u_prev = NULL;
3166 cList->u_next = hi_to->channels;
3167 if (hi_to->channels)
3168 hi_to->channels->u_prev = cList;
3169 hi_to->channels = cList;
3173 /* Do they get an OpServ level promotion? */
3174 if (hi_from->opserv_level > hi_to->opserv_level)
3175 hi_to->opserv_level = hi_from->opserv_level;
3177 /* What about last seen time? */
3178 if (hi_from->lastseen > hi_to->lastseen)
3179 hi_to->lastseen = hi_from->lastseen;
3181 /* New karma is the sum of the two original karmas. */
3182 hi_to->karma += hi_from->karma;
3184 /* Does a fakehost carry over? (This intentionally doesn't set it
3185 * for users previously attached to hi_to. They'll just have to
3188 if (hi_from->fakehost && !hi_to->fakehost)
3189 hi_to->fakehost = strdup(hi_from->fakehost);
3190 if (hi_from->fakeident && !hi_to->fakeident)
3191 hi_to->fakeident = strdup(hi_from->fakeident);
3193 /* Notify of success. */
3194 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3195 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3196 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3198 /* Unregister the "from" handle. */
3199 nickserv_unregister_handle(hi_from, NULL);
3204 struct nickserv_discrim {
3205 unsigned long flags_on, flags_off;
3206 unsigned long min_registered, max_registered;
3207 unsigned long lastseen;
3209 int min_level, max_level;
3210 int min_karma, max_karma;
3211 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3212 const char *nickmask;
3213 const char *hostmask;
3214 const char *fakehostmask;
3215 const char *fakeidentmask;
3216 const char *handlemask;
3217 const char *emailmask;
3220 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3222 struct discrim_apply_info {
3223 struct nickserv_discrim *discrim;
3224 discrim_search_func func;
3225 struct userNode *source;
3226 unsigned int matched;
3229 static struct nickserv_discrim *
3230 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3233 struct nickserv_discrim *discrim;
3235 discrim = malloc(sizeof(*discrim));
3236 memset(discrim, 0, sizeof(*discrim));
3237 discrim->min_level = 0;
3238 discrim->max_level = INT_MAX;
3239 discrim->limit = 50;
3240 discrim->min_registered = 0;
3241 discrim->max_registered = ULONG_MAX;
3242 discrim->lastseen = ULONG_MAX;
3243 discrim->min_karma = INT_MIN;
3244 discrim->max_karma = INT_MAX;
3246 for (i=0; i<argc; i++) {
3247 if (i == argc - 1) {
3248 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3251 if (!irccasecmp(argv[i], "limit")) {
3252 discrim->limit = strtoul(argv[++i], NULL, 0);
3253 } else if (!irccasecmp(argv[i], "flags")) {
3254 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3255 } else if (!irccasecmp(argv[i], "registered")) {
3256 const char *cmp = argv[++i];
3257 if (cmp[0] == '<') {
3258 if (cmp[1] == '=') {
3259 discrim->min_registered = now - ParseInterval(cmp+2);
3261 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3263 } else if (cmp[0] == '=') {
3264 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3265 } else if (cmp[0] == '>') {
3266 if (cmp[1] == '=') {
3267 discrim->max_registered = now - ParseInterval(cmp+2);
3269 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3272 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3274 } else if (!irccasecmp(argv[i], "seen")) {
3275 discrim->lastseen = now - ParseInterval(argv[++i]);
3276 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3277 discrim->nickmask = argv[++i];
3278 } else if (!irccasecmp(argv[i], "hostmask")) {
3280 if (!irccasecmp(argv[i], "exact")) {
3281 if (i == argc - 1) {
3282 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3285 discrim->hostmask_type = EXACT;
3286 } else if (!irccasecmp(argv[i], "subset")) {
3287 if (i == argc - 1) {
3288 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3291 discrim->hostmask_type = SUBSET;
3292 } else if (!irccasecmp(argv[i], "superset")) {
3293 if (i == argc - 1) {
3294 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3297 discrim->hostmask_type = SUPERSET;
3298 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3299 if (i == argc - 1) {
3300 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3303 discrim->hostmask_type = LASTQUIT;
3306 discrim->hostmask_type = SUPERSET;
3308 discrim->hostmask = argv[++i];
3309 } else if (!irccasecmp(argv[i], "fakehost")) {
3310 if (!irccasecmp(argv[++i], "*")) {
3311 discrim->fakehostmask = 0;
3313 discrim->fakehostmask = argv[i];
3315 } else if (!irccasecmp(argv[i], "fakeident")) {
3316 if (!irccasecmp(argv[++i], "*")) {
3317 discrim->fakeidentmask = 0;
3319 discrim->fakeidentmask = argv[i];
3321 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3322 if (!irccasecmp(argv[++i], "*")) {
3323 discrim->handlemask = 0;
3325 discrim->handlemask = argv[i];
3327 } else if (!irccasecmp(argv[i], "email")) {
3328 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3329 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3331 } else if (!irccasecmp(argv[++i], "*")) {
3332 discrim->emailmask = 0;
3334 discrim->emailmask = argv[i];
3336 } else if (!irccasecmp(argv[i], "access")) {
3337 const char *cmp = argv[++i];
3338 if (cmp[0] == '<') {
3339 if (discrim->min_level == 0) discrim->min_level = 1;
3340 if (cmp[1] == '=') {
3341 discrim->max_level = strtoul(cmp+2, NULL, 0);
3343 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3345 } else if (cmp[0] == '=') {
3346 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3347 } else if (cmp[0] == '>') {
3348 if (cmp[1] == '=') {
3349 discrim->min_level = strtoul(cmp+2, NULL, 0);
3351 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3354 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3356 } else if (!irccasecmp(argv[i], "karma")) {
3357 const char *cmp = argv[++i];
3358 if (cmp[0] == '<') {
3359 if (cmp[1] == '=') {
3360 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3362 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3364 } else if (cmp[0] == '=') {
3365 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3366 } else if (cmp[0] == '>') {
3367 if (cmp[1] == '=') {
3368 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3370 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3373 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3376 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3387 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3389 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3390 || (discrim->flags_off & hi->flags)
3391 || (discrim->min_registered > hi->registered)
3392 || (discrim->max_registered < hi->registered)
3393 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3394 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3395 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3396 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3397 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3398 || (discrim->min_level > hi->opserv_level)
3399 || (discrim->max_level < hi->opserv_level)
3400 || (discrim->min_karma > hi->karma)
3401 || (discrim->max_karma < hi->karma)
3405 if (discrim->hostmask) {
3407 for (i=0; i<hi->masks->used; i++) {
3408 const char *mask = hi->masks->list[i];
3409 if ((discrim->hostmask_type == SUBSET)
3410 && (match_ircglobs(discrim->hostmask, mask))) break;
3411 else if ((discrim->hostmask_type == EXACT)
3412 && !irccasecmp(discrim->hostmask, mask)) break;
3413 else if ((discrim->hostmask_type == SUPERSET)
3414 && (match_ircglobs(mask, discrim->hostmask))) break;
3415 else if ((discrim->hostmask_type == LASTQUIT)
3416 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3418 if (i==hi->masks->used) return 0;
3420 if (discrim->nickmask) {
3421 struct nick_info *nick = hi->nicks;
3423 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3426 if (!nick) return 0;
3432 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3434 dict_iterator_t it, next;
3435 unsigned int matched;
3437 for (it = dict_first(nickserv_handle_dict), matched = 0;
3438 it && (matched < discrim->limit);
3440 next = iter_next(it);
3441 if (nickserv_discrim_match(discrim, iter_data(it))) {
3442 dsf(source, iter_data(it));
3450 search_print_func(struct userNode *source, struct handle_info *match)
3452 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3456 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3461 search_unregister_func (struct userNode *source, struct handle_info *match)
3463 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3464 nickserv_unregister_handle(match, source);
3468 nickserv_sort_accounts_by_access(const void *a, const void *b)
3470 const struct handle_info *hi_a = *(const struct handle_info**)a;
3471 const struct handle_info *hi_b = *(const struct handle_info**)b;
3472 if (hi_a->opserv_level != hi_b->opserv_level)
3473 return hi_b->opserv_level - hi_a->opserv_level;
3474 return irccasecmp(hi_a->handle, hi_b->handle);
3478 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3480 struct handle_info_list hil;
3481 struct helpfile_table tbl;
3486 memset(&hil, 0, sizeof(hil));
3487 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3488 struct handle_info *hi = iter_data(it);
3489 if (hi->opserv_level)
3490 handle_info_list_append(&hil, hi);
3492 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3493 tbl.length = hil.used + 1;
3495 tbl.flags = TABLE_NO_FREE;
3496 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3497 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3500 for (ii = 0; ii < hil.used; ) {
3501 ary = malloc(tbl.width * sizeof(ary[0]));
3502 ary[0] = hil.list[ii]->handle;
3503 ary[1] = strtab(hil.list[ii]->opserv_level);
3504 tbl.contents[++ii] = ary;
3506 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3507 reply("MSG_MATCH_COUNT", hil.used);
3508 for (ii = 0; ii < hil.used; ii++)
3509 free(tbl.contents[ii]);
3514 static NICKSERV_FUNC(cmd_search)
3516 struct nickserv_discrim *discrim;
3517 discrim_search_func action;
3518 struct svccmd *subcmd;
3519 unsigned int matches;
3522 NICKSERV_MIN_PARMS(3);
3523 sprintf(buf, "search %s", argv[1]);
3524 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3525 if (!irccasecmp(argv[1], "print"))
3526 action = search_print_func;
3527 else if (!irccasecmp(argv[1], "count"))
3528 action = search_count_func;
3529 else if (!irccasecmp(argv[1], "unregister"))
3530 action = search_unregister_func;
3532 reply("NSMSG_INVALID_ACTION", argv[1]);
3536 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3539 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3543 if (action == search_print_func)
3544 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3545 else if (action == search_count_func)
3546 discrim->limit = INT_MAX;
3548 matches = nickserv_discrim_search(discrim, action, user);
3551 reply("MSG_MATCH_COUNT", matches);
3553 reply("MSG_NO_MATCHES");
3559 static MODCMD_FUNC(cmd_checkpass)
3561 struct handle_info *hi;
3563 NICKSERV_MIN_PARMS(3);
3564 if (!(hi = get_handle_info(argv[1]))) {
3565 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3568 if (checkpass(argv[2], hi->passwd))
3569 reply("CHECKPASS_YES");
3571 reply("CHECKPASS_NO");
3576 static MODCMD_FUNC(cmd_checkemail)
3578 struct handle_info *hi;
3580 NICKSERV_MIN_PARMS(3);
3581 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3584 if (!hi->email_addr)
3585 reply("CHECKEMAIL_NOT_SET");
3586 else if (!irccasecmp(argv[2], hi->email_addr))
3587 reply("CHECKEMAIL_YES");
3589 reply("CHECKEMAIL_NO");
3595 nickserv_db_read_handle(const char *handle, dict_t obj)
3598 struct string_list *masks, *slist;
3599 struct handle_info *hi;
3600 struct userNode *authed_users;
3601 struct userData *channel_list;
3606 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3607 id = str ? strtoul(str, NULL, 0) : 0;
3608 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3610 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3613 if ((hi = get_handle_info(handle))) {
3614 authed_users = hi->users;
3615 channel_list = hi->channels;
3617 hi->channels = NULL;
3618 dict_remove(nickserv_handle_dict, hi->handle);
3620 authed_users = NULL;
3621 channel_list = NULL;
3623 hi = register_handle(handle, str, id);
3625 hi->users = authed_users;
3626 while (authed_users) {
3627 authed_users->handle_info = hi;
3628 authed_users = authed_users->next_authed;
3631 hi->channels = channel_list;
3632 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3633 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3634 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3635 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3636 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3637 hi->language = language_find(str ? str : "C");
3638 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3639 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3640 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3642 hi->infoline = strdup(str);
3643 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3644 hi->registered = str ? strtoul(str, NULL, 0) : now;
3645 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3646 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3647 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3648 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3649 /* We want to read the nicks even if disable_nicks is set. This is so
3650 * that we don't lose the nick data entirely. */
3651 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3653 for (ii=0; ii<slist->used; ii++)
3654 register_nick(slist->list[ii], hi);
3656 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3658 for (ii=0; str[ii]; ii++)
3659 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3661 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3662 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3663 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3664 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3665 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3666 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3667 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3669 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3671 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3672 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3674 nickserv_set_email_addr(hi, str);
3675 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3677 hi->epithet = strdup(str);
3678 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3680 hi->fakehost = strdup(str);
3681 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
3683 hi->fakeident = strdup(str);
3684 /* Read the "cookie" sub-database (if it exists). */
3685 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3687 const char *data, *type, *expires, *cookie_str;
3688 struct handle_cookie *cookie;
3690 cookie = calloc(1, sizeof(*cookie));
3691 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3692 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3693 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3694 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3695 if (!type || !expires || !cookie_str) {
3696 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3699 if (!irccasecmp(type, KEY_ACTIVATION))
3700 cookie->type = ACTIVATION;
3701 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3702 cookie->type = PASSWORD_CHANGE;
3703 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3704 cookie->type = EMAIL_CHANGE;
3705 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3706 cookie->type = ALLOWAUTH;
3708 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3711 cookie->expires = strtoul(expires, NULL, 0);
3712 if (cookie->expires < now)
3715 cookie->data = strdup(data);
3716 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3720 nickserv_bake_cookie(cookie);
3722 nickserv_free_cookie(cookie);
3724 /* Read the "notes" sub-database (if it exists). */
3725 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3728 struct handle_note *last_note;
3729 struct handle_note *note;
3732 for (it = dict_first(subdb); it; it = iter_next(it)) {
3733 const char *expires;
3737 const char *note_id;
3740 note_id = iter_key(it);
3741 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3743 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
3746 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3747 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3748 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3749 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3750 if (!setter || !text || !set) {
3751 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
3754 note = calloc(1, sizeof(*note) + strlen(text));
3756 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3757 note->set = strtoul(set, NULL, 10);
3758 note->id = strtoul(note_id, NULL, 10);
3759 safestrncpy(note->setter, setter, sizeof(note->setter));
3760 strcpy(note->note, text);
3762 last_note->next = note;
3771 nickserv_saxdb_read(dict_t db) {
3773 struct record_data *rd;
3775 for (it=dict_first(db); it; it=iter_next(it)) {
3777 nickserv_db_read_handle(iter_key(it), rd->d.object);
3782 static NICKSERV_FUNC(cmd_mergedb)
3784 struct timeval start, stop;
3787 NICKSERV_MIN_PARMS(2);
3788 gettimeofday(&start, NULL);
3789 if (!(db = parse_database(argv[1]))) {
3790 reply("NSMSG_DB_UNREADABLE", argv[1]);
3793 nickserv_saxdb_read(db);
3795 gettimeofday(&stop, NULL);
3796 stop.tv_sec -= start.tv_sec;
3797 stop.tv_usec -= start.tv_usec;
3798 if (stop.tv_usec < 0) {
3800 stop.tv_usec += 1000000;
3802 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
3807 expire_handles(UNUSED_ARG(void *data))
3809 dict_iterator_t it, next;
3810 unsigned long expiry;
3811 struct handle_info *hi;
3813 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3814 next = iter_next(it);
3816 if ((hi->opserv_level > 0)
3818 || HANDLE_FLAGGED(hi, FROZEN)
3819 || HANDLE_FLAGGED(hi, NODELETE)) {
3822 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3823 if ((now - hi->lastseen) > expiry) {
3824 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3825 nickserv_unregister_handle(hi, NULL);
3829 if (nickserv_conf.handle_expire_frequency)
3830 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3834 nickserv_load_dict(const char *fname)
3838 if (!(file = fopen(fname, "r"))) {
3839 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3842 while (fgets(line, sizeof(line), file)) {
3845 if (line[strlen(line)-1] == '\n')
3846 line[strlen(line)-1] = 0;
3847 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3850 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3853 static enum reclaim_action
3854 reclaim_action_from_string(const char *str) {
3856 return RECLAIM_NONE;
3857 else if (!irccasecmp(str, "warn"))
3858 return RECLAIM_WARN;
3859 else if (!irccasecmp(str, "svsnick"))
3860 return RECLAIM_SVSNICK;
3861 else if (!irccasecmp(str, "kill"))
3862 return RECLAIM_KILL;
3864 return RECLAIM_NONE;
3868 nickserv_conf_read(void)
3870 dict_t conf_node, child;
3874 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3875 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3878 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3880 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3881 if (nickserv_conf.valid_handle_regex_set)
3882 regfree(&nickserv_conf.valid_handle_regex);
3884 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3885 nickserv_conf.valid_handle_regex_set = !err;
3886 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3888 nickserv_conf.valid_handle_regex_set = 0;
3890 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3891 if (nickserv_conf.valid_nick_regex_set)
3892 regfree(&nickserv_conf.valid_nick_regex);
3894 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3895 nickserv_conf.valid_nick_regex_set = !err;
3896 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3898 nickserv_conf.valid_nick_regex_set = 0;
3900 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3902 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3903 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3904 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3905 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3906 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3907 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3908 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3909 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3910 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3911 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3912 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3913 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3914 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3915 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3916 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3917 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3918 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3919 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3920 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3921 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3922 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3923 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3924 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3925 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3926 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
3927 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
3928 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3930 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3931 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3932 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3934 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3935 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3936 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3938 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3939 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3940 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3941 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3942 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3943 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3944 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3945 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3946 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
3947 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
3948 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
3951 nickserv_conf.ounregister_flags = 0;
3953 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
3956 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
3958 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
3960 nickserv_conf.handle_ts_mode = TS_IGNORE;
3961 else if (!irccasecmp(str, "ircu"))
3962 nickserv_conf.handle_ts_mode = TS_IRCU;
3964 nickserv_conf.handle_ts_mode = TS_IGNORE;
3965 if (!nickserv_conf.disable_nicks) {
3966 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3967 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3968 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3969 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3970 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3971 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3972 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3973 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3975 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3976 for (it=dict_first(child); it; it=iter_next(it)) {
3977 const char *key = iter_key(it), *value;
3981 if (!strncasecmp(key, "uc_", 3))
3982 flag = toupper(key[3]);
3983 else if (!strncasecmp(key, "lc_", 3))
3984 flag = tolower(key[3]);
3988 if ((pos = handle_inverse_flags[flag])) {
3989 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3990 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3993 if (nickserv_conf.weak_password_dict)
3994 dict_delete(nickserv_conf.weak_password_dict);
3995 nickserv_conf.weak_password_dict = dict_new();
3996 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3997 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3998 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3999 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4001 nickserv_load_dict(str);
4002 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4003 if (nickserv && str)
4004 NickChange(nickserv, str, 0);
4005 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4006 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4007 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4008 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4009 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4010 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4011 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4012 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4013 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4014 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4015 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4016 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4017 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4018 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4019 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4020 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4021 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4022 nickserv_conf.titlehost_suffix = str ? str : "example.net";
4023 str = conf_get_data("server/network", RECDB_QSTRING);
4024 nickserv_conf.network_name = str ? str : "some IRC network";
4025 if (!nickserv_conf.auth_policer_params) {
4026 nickserv_conf.auth_policer_params = policer_params_new();
4027 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4028 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4030 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4031 for (it=dict_first(child); it; it=iter_next(it))
4032 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4036 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4038 char newnick[NICKLEN+1];
4047 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4049 case RECLAIM_SVSNICK:
4051 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4052 } while (GetUserH(newnick));
4053 irc_svsnick(nickserv, user, newnick);
4056 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4057 DelUser(user, nickserv, 1, msg);
4063 nickserv_reclaim_p(void *data) {
4064 struct userNode *user = data;
4065 struct nick_info *ni = get_nick_info(user->nick);
4067 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4071 check_user_nick(struct userNode *user) {
4072 struct nick_info *ni;
4073 user->modes &= ~FLAGS_REGNICK;
4074 if (!(ni = get_nick_info(user->nick)))
4076 if (user->handle_info == ni->owner) {
4077 user->modes |= FLAGS_REGNICK;
4081 if (nickserv_conf.warn_nick_owned)
4082 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4083 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4085 if (nickserv_conf.auto_reclaim_delay)
4086 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4088 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4092 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4094 struct handle_info *hi = NULL;
4097 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4098 if ((hi == NULL) && (serial != 0)) {
4100 inttobase64(id, serial, IDLEN);
4101 hi = dict_find(nickserv_id_dict, id, NULL);
4105 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4106 && (timestamp != hi->registered)) {
4109 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4112 set_user_handle_info(user, hi, 0);
4114 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4119 handle_nick_change(struct userNode *user, const char *old_nick)
4121 struct handle_info *hi;
4123 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4124 dict_remove(nickserv_allow_auth_dict, old_nick);
4125 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4127 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4128 check_user_nick(user);
4132 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4134 dict_remove(nickserv_allow_auth_dict, user->nick);
4135 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4136 set_user_handle_info(user, NULL, 0);
4139 static struct modcmd *
4140 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4142 if (min_level > 0) {
4144 sprintf(buf, "%u", min_level);
4145 if (must_be_qualified) {
4146 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4148 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4150 } else if (min_level == 0) {
4151 if (must_be_qualified) {
4152 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4154 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4157 if (must_be_qualified) {
4158 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4160 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4166 nickserv_db_cleanup(void)
4168 unreg_del_user_func(nickserv_remove_user);
4169 userList_clean(&curr_helpers);
4170 policer_params_delete(nickserv_conf.auth_policer_params);
4171 dict_delete(nickserv_handle_dict);
4172 dict_delete(nickserv_nick_dict);
4173 dict_delete(nickserv_opt_dict);
4174 dict_delete(nickserv_allow_auth_dict);
4175 dict_delete(nickserv_email_dict);
4176 dict_delete(nickserv_id_dict);
4177 dict_delete(nickserv_conf.weak_password_dict);
4178 free(auth_func_list);
4179 free(unreg_func_list);
4181 free(allowauth_func_list);
4182 free(handle_merge_func_list);
4183 free(failpw_func_list);
4184 if (nickserv_conf.valid_handle_regex_set)
4185 regfree(&nickserv_conf.valid_handle_regex);
4186 if (nickserv_conf.valid_nick_regex_set)
4187 regfree(&nickserv_conf.valid_nick_regex);
4191 init_nickserv(const char *nick)
4194 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4195 reg_new_user_func(check_user_nick);
4196 reg_nick_change_func(handle_nick_change);
4197 reg_del_user_func(nickserv_remove_user);
4198 reg_account_func(handle_account);
4200 /* set up handle_inverse_flags */
4201 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4202 for (i=0; handle_flags[i]; i++) {
4203 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4204 flag_access_levels[i] = 0;
4207 conf_register_reload(nickserv_conf_read);
4208 nickserv_opt_dict = dict_new();
4209 nickserv_email_dict = dict_new();
4210 dict_set_free_keys(nickserv_email_dict, free);
4211 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4213 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4214 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4215 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4216 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4217 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4218 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4219 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4220 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4221 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4222 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4223 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4224 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4225 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4226 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4227 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4228 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4229 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4230 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4231 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4232 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4233 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4234 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4235 if (!nickserv_conf.disable_nicks) {
4236 /* nick management commands */
4237 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4238 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4239 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4240 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4241 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4242 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4244 if (nickserv_conf.email_enabled) {
4245 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4246 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4247 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4248 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4249 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4250 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4252 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4253 /* miscellaneous commands */
4254 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4255 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4256 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4257 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4258 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4259 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4261 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4262 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4263 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4264 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4265 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4266 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4267 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4268 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4269 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4270 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4271 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4272 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4273 if (nickserv_conf.titlehost_suffix) {
4274 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4275 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4276 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4278 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4279 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4280 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4281 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4283 nickserv_handle_dict = dict_new();
4284 dict_set_free_keys(nickserv_handle_dict, free);
4285 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4287 nickserv_id_dict = dict_new();
4288 dict_set_free_keys(nickserv_id_dict, free);
4290 nickserv_nick_dict = dict_new();
4291 dict_set_free_data(nickserv_nick_dict, free);
4293 nickserv_allow_auth_dict = dict_new();
4295 userList_init(&curr_helpers);
4298 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4299 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4300 nickserv_service = service_register(nickserv);
4302 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4303 reg_exit_func(nickserv_db_cleanup);
4304 if(nickserv_conf.handle_expire_frequency)
4305 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4306 message_register_table(msgtab);