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_DEVNULL "devnull"
89 #define KEY_WEBSITE "website"
90 #define KEY_USERLIST_STYLE "user_style"
91 #define KEY_SCREEN_WIDTH "screen_width"
92 #define KEY_LAST_AUTHED_HOST "last_authed_host"
93 #define KEY_LAST_QUIT_HOST "last_quit_host"
94 #define KEY_EMAIL_ADDR "email_addr"
95 #define KEY_COOKIE "cookie"
96 #define KEY_COOKIE_DATA "data"
97 #define KEY_COOKIE_TYPE "type"
98 #define KEY_COOKIE_EXPIRES "expires"
99 #define KEY_ACTIVATION "activation"
100 #define KEY_PASSWORD_CHANGE "password change"
101 #define KEY_EMAIL_CHANGE "email change"
102 #define KEY_ALLOWAUTH "allowauth"
103 #define KEY_EPITHET "epithet"
104 #define KEY_TABLE_WIDTH "table_width"
105 #define KEY_MAXLOGINS "maxlogins"
106 #define KEY_FAKEHOST "fakehost"
107 #define KEY_FAKEIDENT "fakeident"
108 #define KEY_NOTES "notes"
109 #define KEY_NOTE_EXPIRES "expires"
110 #define KEY_NOTE_SET "set"
111 #define KEY_NOTE_SETTER "setter"
112 #define KEY_NOTE_NOTE "note"
113 #define KEY_KARMA "karma"
115 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
117 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
118 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
119 typedef OPTION_FUNC(option_func_t);
121 DEFINE_LIST(handle_info_list, struct handle_info*)
123 #define NICKSERV_MIN_PARMS(N) do { \
125 reply("MSG_MISSING_PARAMS", argv[0]); \
126 svccmd_send_help(user, nickserv, cmd); \
130 struct userNode *nickserv;
131 struct userList curr_helpers;
132 const char *handle_flags = HANDLE_FLAGS;
134 static struct module *nickserv_module;
135 static struct service *nickserv_service;
136 static struct log_type *NS_LOG;
137 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
138 static dict_t nickserv_id_dict; /* contains struct handle_info* */
139 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
140 static dict_t nickserv_opt_dict; /* contains option_func_t* */
141 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
142 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
143 static char handle_inverse_flags[256];
144 static unsigned int flag_access_levels[32];
145 static const struct message_entry msgtab[] = {
146 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
147 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
148 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
149 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
150 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
151 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
152 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
153 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
154 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
155 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
156 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
157 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
158 { "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." },
159 { "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." },
160 { "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." },
161 { "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." },
162 { "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." },
163 { "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." },
164 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
165 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
166 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
167 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
168 { "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." },
169 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
170 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
171 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
172 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
173 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
174 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
175 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
176 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
177 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
178 { "NSMSG_ATE_COOKIE_FOREIGN", "I ate the cookie for account $b%s$b. It may now have another." },
179 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
180 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
181 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
182 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
183 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
184 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
185 { "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)" },
186 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
187 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
188 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
189 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
190 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
191 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
192 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
193 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
194 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
195 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
196 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
197 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
198 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
199 { "NSMSG_TITLE_TRUNCATED_RENAME", "That account name combined with the user's title would result in a truncated host; please choose a shorter account name." },
200 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
201 { "NSMSG_FAKEIDENT_INVALID", "Fake idents must be shorter than %d characters." },
202 { "NSMSG_FAKEMASK_INVALID", "Fake ident@hosts must be shorter than %d characters." },
203 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
204 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
205 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
206 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
207 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
208 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
209 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
210 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
211 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
212 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
213 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
214 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
215 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
216 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
217 { "NSMSG_HANDLEINFO_DEVNULL", " DevNull Class: %s" },
218 { "NSMSG_HANDLEINFO_WEBSITE", " Website: %s" },
219 { "NSMSG_HANDLEINFO_ACCESS", " Access: %i" },
220 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
221 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
222 { "NSMSG_HANDLEINFO_FAKEIDENT", " Fake ident: %s" },
223 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
224 { "NSMSG_HANDLEINFO_FAKEIDENTHOST", " Fake host: %s@%s" },
225 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
226 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
227 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
228 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
229 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
230 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
231 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
232 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
233 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
234 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
235 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
236 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
237 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
238 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
239 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
240 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
241 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
242 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
243 { "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)." },
244 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
245 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
246 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
247 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
248 { "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." },
249 { "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." },
250 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
251 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
252 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
253 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
254 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
255 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
256 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
257 { "NSMSG_PASS_SUCCESS", "Password changed." },
258 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
259 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
260 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
261 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
262 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
263 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
264 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
265 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
266 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
267 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
268 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
269 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
270 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
271 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
272 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
273 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
274 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
275 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
276 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
277 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
278 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
279 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
280 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
281 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
282 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
283 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
284 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
285 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
286 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
287 { "NSMSG_NO_ACCESS", "Access denied." },
288 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
289 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
290 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
291 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
292 { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
293 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
294 { "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." },
295 { "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." },
296 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
297 { "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." },
298 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
299 { "NSMSG_SEARCH_MATCH", "Match: %s" },
300 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
301 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
302 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
303 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
304 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
305 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
306 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
307 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
308 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
309 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
310 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
311 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
312 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
313 { "NSMSG_SET_DEVNULL", "$bDEVNULL: $b%s" },
314 { "NSMSG_SET_AUTOHIDE", "$bAUTOHIDE: $b%s" },
315 { "NSMSG_SET_WEBSITE", "$bWEBSITE: $b%s" },
316 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
317 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
318 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
319 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
320 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
321 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
322 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
323 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
324 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
325 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
326 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
327 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
328 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
329 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
330 { "NSMSG_SET_FAKEIDENT", "$bFAKEIDENT: $b%s" },
331 { "NSMSG_SET_FAKEIDENTHOST", "$bFAKEHOST: $b%s@%s" },
332 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
333 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
334 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
335 { "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." },
336 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
337 { "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." },
338 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
339 { "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." },
340 { "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." },
341 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
342 { "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." },
343 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
344 { "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." },
345 { "CHECKPASS_YES", "Yes." },
346 { "CHECKPASS_NO", "No." },
347 { "CHECKEMAIL_NOT_SET", "No email set." },
348 { "CHECKEMAIL_YES", "Yes." },
349 { "CHECKEMAIL_NO", "No." },
353 enum reclaim_action {
359 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
360 static void nickserv_reclaim_p(void *data);
361 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
363 enum handle_ts_mode {
369 unsigned int disable_nicks : 1;
370 unsigned int valid_handle_regex_set : 1;
371 unsigned int valid_nick_regex_set : 1;
372 unsigned int autogag_enabled : 1;
373 unsigned int email_enabled : 1;
374 unsigned int email_required : 1;
375 unsigned int default_hostmask : 1;
376 unsigned int warn_nick_owned : 1;
377 unsigned int warn_clone_auth : 1;
378 unsigned long nicks_per_handle;
379 unsigned long password_min_length;
380 unsigned long password_min_digits;
381 unsigned long password_min_upper;
382 unsigned long password_min_lower;
383 unsigned long db_backup_frequency;
384 unsigned long handle_expire_frequency;
385 unsigned long autogag_duration;
386 unsigned long email_visible_level;
387 unsigned long cookie_timeout;
388 unsigned long handle_expire_delay;
389 unsigned long nochan_handle_expire_delay;
390 unsigned long modoper_level;
391 unsigned long set_epithet_level;
392 unsigned long set_title_level;
393 unsigned long set_fakehost_level;
394 unsigned long set_fakeident_level;
395 unsigned long handles_per_email;
396 unsigned long email_search_level;
397 const char *network_name;
398 regex_t valid_handle_regex;
399 regex_t valid_nick_regex;
400 dict_t weak_password_dict;
401 struct policer_params *auth_policer_params;
402 enum reclaim_action reclaim_action;
403 enum reclaim_action auto_reclaim_action;
404 enum handle_ts_mode handle_ts_mode;
405 unsigned long auto_reclaim_delay;
406 unsigned char default_maxlogins;
407 unsigned char hard_maxlogins;
408 unsigned long ounregister_inactive;
409 unsigned long ounregister_flags;
412 const char *titlehost_suffix = NULL;
414 /* We have 2^32 unique account IDs to use. */
415 unsigned long int highest_id = 0;
417 #define WALK_NOTES(HANDLE, PREV, NOTE) \
418 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
419 if (NOTE->expires && NOTE->expires < now) { \
420 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
422 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
426 canonicalize_hostmask(char *mask)
428 char *out = mask, *temp;
429 if ((temp = strchr(mask, '!'))) {
431 while (*temp) *out++ = *temp++;
437 static struct handle_info *
438 register_handle(const char *handle, const char *passwd, unsigned long id)
440 struct handle_info *hi;
442 char id_base64[IDLEN + 1];
445 /* Assign a unique account ID to the account; note that 0 is
446 an invalid account ID. 1 is therefore the first account ID. */
448 id = 1 + highest_id++;
450 /* Note: highest_id is and must always be the highest ID. */
451 if (id > highest_id) {
455 inttobase64(id_base64, id, IDLEN);
457 /* Make sure an account with the same ID doesn't exist. If a
458 duplicate is found, log some details and assign a new one.
459 This should be impossible, but it never hurts to expect it. */
460 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
461 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
466 hi = calloc(1, sizeof(*hi));
467 hi->userlist_style = HI_DEFAULT_STYLE;
468 hi->handle = strdup(handle);
469 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
471 dict_insert(nickserv_handle_dict, hi->handle, hi);
476 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
482 register_nick(const char *nick, struct handle_info *owner)
484 struct nick_info *ni;
485 ni = malloc(sizeof(struct nick_info));
486 safestrncpy(ni->nick, nick, sizeof(ni->nick));
488 ni->next = owner->nicks;
490 dict_insert(nickserv_nick_dict, ni->nick, ni);
494 delete_nick(struct nick_info *ni)
496 struct nick_info *last, *next;
497 struct userNode *user;
498 /* Check to see if we should mark a user as unregistered. */
499 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
500 user->modes &= ~FLAGS_REGNICK;
503 /* Remove ni from the nick_info linked list. */
504 if (ni == ni->owner->nicks) {
505 ni->owner->nicks = ni->next;
507 last = ni->owner->nicks;
513 last->next = next->next;
515 dict_remove(nickserv_nick_dict, ni->nick);
518 static unreg_func_t *unreg_func_list;
519 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
522 reg_unreg_func(unreg_func_t func)
524 if (unreg_func_used == unreg_func_size) {
525 if (unreg_func_size) {
526 unreg_func_size <<= 1;
527 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
530 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
533 unreg_func_list[unreg_func_used++] = func;
537 nickserv_free_cookie(void *data)
539 struct handle_cookie *cookie = data;
540 if (cookie->hi) cookie->hi->cookie = NULL;
541 if (cookie->data) free(cookie->data);
546 free_handle_info(void *vhi)
548 struct handle_info *hi = vhi;
551 inttobase64(id, hi->id, IDLEN);
552 dict_remove(nickserv_id_dict, id);
554 free_string_list(hi->masks);
558 delete_nick(hi->nicks);
566 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
567 nickserv_free_cookie(hi->cookie);
570 struct handle_note *note = hi->notes;
571 hi->notes = note->next;
574 if (hi->email_addr) {
575 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
576 handle_info_list_remove(hil, hi);
578 dict_remove(nickserv_email_dict, hi->email_addr);
583 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
586 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
590 for (n=0; n<unreg_func_used; n++)
591 unreg_func_list[n](notify, hi);
593 set_user_handle_info(hi->users, NULL, 0);
595 if (nickserv_conf.disable_nicks)
596 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
598 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
600 dict_remove(nickserv_handle_dict, hi->handle);
604 get_handle_info(const char *handle)
606 return dict_find(nickserv_handle_dict, handle, 0);
610 get_nick_info(const char *nick)
612 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
616 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
621 for (nn=0; nn<channel->members.used; ++nn) {
622 mn = channel->members.list[nn];
623 if ((mn->user != except) && (mn->user->handle_info == handle))
630 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
631 if (!user->handle_info) {
633 send_message(user, bot, "MSG_AUTHENTICATE");
637 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
639 send_message(user, bot, "NSMSG_NO_ACCESS");
643 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
645 send_message(user, bot, "MSG_OPER_SUSPENDED");
649 if (user->handle_info->opserv_level < min_level) {
651 send_message(user, bot, "NSMSG_NO_ACCESS");
659 is_valid_handle(const char *handle)
661 struct userNode *user;
662 /* cant register a juped nick/service nick as handle, to prevent confusion */
663 user = GetUserH(handle);
664 if (user && IsLocal(user))
666 /* check against maximum length */
667 if (strlen(handle) > NICKSERV_HANDLE_LEN)
669 /* for consistency, only allow account names that could be nicks */
670 if (!is_valid_nick(handle))
672 /* disallow account names that look like bad words */
673 if (opserv_bad_channel(handle))
675 /* test either regex or containing all valid chars */
676 if (nickserv_conf.valid_handle_regex_set) {
677 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
680 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
681 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
685 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
690 is_registerable_nick(const char *nick)
692 /* make sure it could be used as an account name */
693 if (!is_valid_handle(nick))
696 if (strlen(nick) > NICKLEN)
698 /* test either regex or as valid handle */
699 if (nickserv_conf.valid_nick_regex_set) {
700 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
703 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
704 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
712 is_valid_email_addr(const char *org_email)
714 char email[strlen(org_email)+1];
715 strcpy(email, org_email);
716 //validate email address
717 //1st check: there need to be one @
718 char *p1 = strchr(email, '@');
719 if(!p1 || strchr(p1+1, '@')) return 0;
721 //2nd check: username (bevore @) must be at least 1 char long and out of part_chars
722 char *part_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._%+-";
724 if(p1 - email == 0) return 0;
725 for(i = 0; i < (p1 - email); i++) {
726 if(!strchr(part_chars, email[i])) return 0;
728 //3rd check: there need to be at least 1 dot in the domain part and all characters out of part_chars
729 part_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-";
738 } else if(!strchr(part_chars, *p1))
745 //4th check: TLD must be <= 5 chars, no special chars
760 visible_email_addr(struct userNode *user, struct handle_info *hi)
762 if (hi->email_addr) {
763 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
764 return hi->email_addr;
774 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
776 struct handle_info *hi;
777 struct userNode *target;
781 if (!(hi = get_handle_info(++name))) {
782 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
787 if (!(target = GetUserH(name))) {
788 send_message(user, service, "MSG_NICK_UNKNOWN", name);
791 if (IsLocal(target)) {
792 if (IsService(target))
793 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
795 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
798 if (!(hi = target->handle_info)) {
799 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
807 oper_outranks(struct userNode *user, struct handle_info *hi) {
808 if (user->handle_info->opserv_level > hi->opserv_level)
810 if (user->handle_info->opserv_level == hi->opserv_level) {
811 if ((user->handle_info->opserv_level == 1000)
812 || (user->handle_info == hi)
813 || ((user->handle_info->opserv_level == 0)
814 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
815 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
819 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
823 static struct handle_info *
824 get_victim_oper(struct userNode *user, const char *target)
826 struct handle_info *hi;
827 if (!(hi = smart_get_handle_info(nickserv, user, target)))
829 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
830 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
833 return oper_outranks(user, hi) ? hi : NULL;
837 valid_user_for(struct userNode *user, struct handle_info *hi)
841 /* If no hostmasks on the account, allow it. */
842 if (!hi->masks->used || IsDummy(user))
844 /* If any hostmask matches, allow it. */
845 for (ii=0; ii<hi->masks->used; ii++)
846 if (user_matches_glob(user, hi->masks->list[ii], 0))
848 /* If they are allowauthed to this account, allow it (removing the aa). */
849 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
850 dict_remove(nickserv_allow_auth_dict, user->nick);
853 /* The user is not allowed to use this account. */
858 is_secure_password(const char *handle, const char *pass, struct userNode *user)
861 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
865 if (len < nickserv_conf.password_min_length) {
867 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
870 if (!irccasecmp(pass, handle)) {
872 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
875 dict_find(nickserv_conf.weak_password_dict, pass, &p);
878 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
881 for (i=0; i<len; i++) {
882 if (isdigit(pass[i]))
884 if (isupper(pass[i]))
886 if (islower(pass[i]))
889 if ((cnt_lower < nickserv_conf.password_min_lower)
890 || (cnt_upper < nickserv_conf.password_min_upper)
891 || (cnt_digits < nickserv_conf.password_min_digits)) {
893 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
899 static auth_func_t *auth_func_list;
900 static unsigned int auth_func_size = 0, auth_func_used = 0;
903 reg_auth_func(auth_func_t func)
905 if (auth_func_used == auth_func_size) {
906 if (auth_func_size) {
907 auth_func_size <<= 1;
908 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
911 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
914 auth_func_list[auth_func_used++] = func;
917 static handle_rename_func_t *rf_list;
918 static unsigned int rf_list_size, rf_list_used;
921 reg_handle_rename_func(handle_rename_func_t func)
923 if (rf_list_used == rf_list_size) {
926 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
929 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
932 rf_list[rf_list_used++] = func;
936 generate_fakehost(struct handle_info *handle)
938 extern const char *hidden_host_suffix;
939 static char buffer[HOSTLEN+1];
941 if (!handle->fakehost) {
942 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
944 } else if (handle->fakehost[0] == '.') {
945 /* A leading dot indicates the stored value is actually a title. */
946 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, titlehost_suffix);
948 } else if (handle->fakehost[0] == '$') {
949 /* A leading $ indicates the stored value begins with the user handle. */
950 snprintf(buffer, sizeof(buffer), "%s%s", handle->handle, handle->fakehost+1);
953 return handle->fakehost;
957 generate_fakeident(struct handle_info *handle, struct userNode *user)
959 static char buffer[USERLEN+1];
961 if (!handle->fakeident) {
964 safestrncpy(buffer, user->ident, sizeof(buffer));
967 return handle->fakeident;
971 apply_fakehost(struct handle_info *handle, struct userNode *user)
973 struct userNode *target;
974 char *fakehost, *fakeident;
979 fakehost = generate_fakehost(handle);
982 fakeident = generate_fakeident(handle, user);
983 assign_fakehost(user, fakehost, fakeident, 0, 1);
987 for (target = handle->users; target; target = target->next_authed) {
988 fakeident = generate_fakeident(handle, target);
989 assign_fakehost(target, fakehost, fakeident, 0, 1);
994 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
997 struct handle_info *old_info;
999 /* This can happen if somebody uses COOKIE while authed, or if
1000 * they re-auth to their current handle (which is silly, but users
1001 * are like that). */
1002 if (user->handle_info == hi)
1005 if (user->handle_info) {
1006 struct userNode *other;
1009 userList_remove(&curr_helpers, user);
1011 /* remove from next_authed linked list */
1012 if (user->handle_info->users == user) {
1013 user->handle_info->users = user->next_authed;
1014 } else if (user->handle_info->users != NULL) {
1015 for (other = user->handle_info->users;
1016 other->next_authed != user;
1017 other = other->next_authed) ;
1018 other->next_authed = user->next_authed;
1020 /* No users authed to the account - can happen if they get
1021 * killed for authing. */
1023 /* if nobody left on old handle, and they're not an oper, remove !god */
1024 if (!user->handle_info->users && !user->handle_info->opserv_level)
1025 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
1026 /* record them as being last seen at this time */
1027 user->handle_info->lastseen = now;
1028 /* and record their hostmask */
1029 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
1031 old_info = user->handle_info;
1032 user->handle_info = hi;
1033 if (hi && !hi->users && !hi->opserv_level)
1034 HANDLE_CLEAR_FLAG(hi, HELPING);
1035 for (n=0; n<auth_func_used; n++) {
1036 auth_func_list[n](user, old_info);
1041 struct nick_info *ni;
1043 HANDLE_CLEAR_FLAG(hi, FROZEN);
1044 if (nickserv_conf.warn_clone_auth) {
1045 struct userNode *other;
1046 for (other = hi->users; other; other = other->next_authed)
1047 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
1049 user->next_authed = hi->users;
1052 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
1053 userList_append(&curr_helpers, user);
1055 if (hi->fakehost || hi->fakeident || old_info)
1056 apply_fakehost(hi, user);
1059 if (!nickserv_conf.disable_nicks) {
1060 struct nick_info *ni2;
1061 for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
1062 if (!irccasecmp(user->nick, ni2->nick)) {
1063 user->modes |= FLAGS_REGNICK;
1068 StampUser(user, hi->handle, hi->registered, hi->id);
1071 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
1072 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
1074 /* We cannot clear the user's account ID, unfortunately. */
1075 user->next_authed = NULL;
1079 static struct handle_info*
1080 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
1082 struct handle_info *hi;
1083 struct nick_info *ni;
1084 char crypted[MD5_CRYPT_LENGTH];
1086 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
1087 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
1091 if (!is_secure_password(handle, passwd, user))
1094 cryptpass(passwd, crypted);
1095 hi = register_handle(handle, crypted, 0);
1096 hi->masks = alloc_string_list(1);
1098 hi->language = lang_C;
1099 hi->registered = now;
1101 hi->flags = HI_DEFAULT_FLAGS;
1102 if (settee && !no_auth)
1103 set_user_handle_info(settee, hi, 1);
1106 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1107 else if (nickserv_conf.disable_nicks)
1108 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1109 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1110 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1112 register_nick(user->nick, hi);
1113 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1115 if (settee && (user != settee))
1116 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1121 nickserv_bake_cookie(struct handle_cookie *cookie)
1123 cookie->hi->cookie = cookie;
1124 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1128 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1130 struct handle_cookie *cookie;
1131 char subject[128], body[4096], *misc;
1132 const char *netname, *fmt;
1136 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1140 cookie = calloc(1, sizeof(*cookie));
1142 cookie->type = type;
1143 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1144 cookie->expires = now + nickserv_conf.cookie_timeout;
1145 inttobase64(cookie->cookie, rand(), 5);
1146 inttobase64(cookie->cookie+5, rand(), 5);
1148 netname = nickserv_conf.network_name;
1151 switch (cookie->type) {
1153 hi->passwd[0] = 0; /* invalidate password */
1154 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1155 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1156 snprintf(subject, sizeof(subject), fmt, netname);
1157 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1158 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1161 case PASSWORD_CHANGE:
1162 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1163 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1164 snprintf(subject, sizeof(subject), fmt, netname);
1165 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1166 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1169 misc = hi->email_addr;
1170 hi->email_addr = cookie->data;
1172 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1173 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1174 snprintf(subject, sizeof(subject), fmt, netname);
1175 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1176 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1177 mail_send(nickserv, hi, subject, body, 1);
1178 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1179 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1181 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1182 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1183 snprintf(subject, sizeof(subject), fmt, netname);
1184 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1185 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1186 mail_send(nickserv, hi, subject, body, 1);
1189 hi->email_addr = misc;
1192 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1193 snprintf(subject, sizeof(subject), fmt, netname);
1194 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1195 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1196 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1199 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1203 mail_send(nickserv, hi, subject, body, first_time);
1204 nickserv_bake_cookie(cookie);
1208 nickserv_eat_cookie(struct handle_cookie *cookie)
1210 cookie->hi->cookie = NULL;
1211 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1212 nickserv_free_cookie(cookie);
1216 nickserv_free_email_addr(void *data)
1218 handle_info_list_clean(data);
1223 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1225 struct handle_info_list *hil;
1226 /* Remove from old handle_info_list ... */
1227 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1228 handle_info_list_remove(hil, hi);
1229 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1230 hi->email_addr = NULL;
1232 /* Add to the new list.. */
1233 if (new_email_addr) {
1234 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1235 hil = calloc(1, sizeof(*hil));
1236 hil->tag = strdup(new_email_addr);
1237 handle_info_list_init(hil);
1238 dict_insert(nickserv_email_dict, hil->tag, hil);
1240 handle_info_list_append(hil, hi);
1241 hi->email_addr = hil->tag;
1245 static NICKSERV_FUNC(cmd_register)
1248 struct handle_info *hi;
1249 const char *email_addr, *password;
1252 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1253 /* Require the first handle registered to belong to someone +o. */
1254 reply("NSMSG_REQUIRE_OPER");
1258 if (user->handle_info) {
1259 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1263 if (IsRegistering(user)) {
1264 reply("NSMSG_ALREADY_REGISTERING");
1268 if (IsStamped(user)) {
1269 /* Unauthenticated users might still have been stamped
1270 previously and could therefore have a hidden host;
1271 do not allow them to register a new account. */
1272 reply("NSMSG_STAMPED_REGISTER");
1276 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1278 if (!is_valid_handle(argv[1])) {
1279 reply("NSMSG_BAD_HANDLE", argv[1]);
1283 if ((argc >= 4) && nickserv_conf.email_enabled) {
1284 struct handle_info_list *hil;
1287 /* Remember email address. */
1288 email_addr = argv[3];
1290 /* Check that the email address looks valid.. */
1291 if (!is_valid_email_addr(email_addr)) {
1292 reply("NSMSG_BAD_EMAIL_ADDR");
1296 /* .. and that we are allowed to send to it. */
1297 if ((str = mail_prohibited_address(email_addr))) {
1298 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1302 /* If we do email verify, make sure we don't spam the address. */
1303 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1305 for (nn=0; nn<hil->used; nn++) {
1306 if (hil->list[nn]->cookie) {
1307 reply("NSMSG_EMAIL_UNACTIVATED");
1311 if (hil->used >= nickserv_conf.handles_per_email) {
1312 reply("NSMSG_EMAIL_OVERUSED");
1325 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1327 /* Add any masks they should get. */
1328 if (nickserv_conf.default_hostmask) {
1329 string_list_append(hi->masks, strdup("*@*"));
1331 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1332 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1333 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1336 /* If they're the first to register, give them level 1000. */
1337 if (dict_size(nickserv_handle_dict) == 1) {
1338 hi->opserv_level = 1000;
1339 reply("NSMSG_ROOT_HANDLE", argv[1]);
1342 /* Set their email address. */
1344 nickserv_set_email_addr(hi, email_addr);
1346 /* If they need to do email verification, tell them. */
1348 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1350 /* Set registering flag.. */
1351 user->modes |= FLAGS_REGISTERING;
1356 static NICKSERV_FUNC(cmd_oregister)
1359 struct userNode *settee;
1360 struct handle_info *hi;
1361 const char *pass, *email;
1363 NICKSERV_MIN_PARMS(3);
1368 if (!is_valid_handle(argv[1])) {
1369 reply("NSMSG_BAD_HANDLE", argv[1]);
1373 if (argc < 5 || !nickserv_conf.email_enabled) {
1378 if (!is_valid_email_addr(email)) {
1379 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
1382 if ((str = mail_prohibited_address(email))) {
1383 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", email, str);
1388 if (argc < 4 || !strcmp(argv[3], "*")) {
1391 } else if (strchr(argv[3], '@')) {
1392 mask = canonicalize_hostmask(strdup(argv[3]));
1394 settee = GetUserH(argv[4]);
1396 reply("MSG_NICK_UNKNOWN", argv[4]);
1403 } else if ((settee = GetUserH(argv[3]))) {
1404 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1406 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1409 if (settee && settee->handle_info) {
1410 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1414 if (!(hi = nickserv_register(user, settee, argv[1], pass, 0))) {
1419 string_list_append(hi->masks, mask);
1421 nickserv_set_email_addr(hi, email);
1425 static NICKSERV_FUNC(cmd_handleinfo)
1428 unsigned int i, pos=0, herelen;
1429 struct userNode *target, *next_un;
1430 struct handle_info *hi;
1431 const char *nsmsg_none;
1435 if (!(hi = user->handle_info)) {
1436 reply("NSMSG_MUST_AUTH");
1439 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1443 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1444 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1445 feh = hi->registered;
1446 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1449 intervalString(buff, now - hi->lastseen, user->handle_info);
1450 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1452 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1455 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1456 if ((oper_has_access(user, cmd->parent->bot, 200, 1)) || IsNetworkHelper(user))
1457 reply("NSMSG_HANDLEINFO_DEVNULL", (hi->devnull ? hi->devnull : nsmsg_none));
1458 if (user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1459 reply("NSMSG_HANDLEINFO_WEBSITE", (hi->website ? hi->website : nsmsg_none));
1460 if(hi->opserv_level > 0 && user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1461 reply("NSMSG_HANDLEINFO_ACCESS", hi->opserv_level);
1462 if (HANDLE_FLAGGED(hi, FROZEN))
1463 reply("NSMSG_HANDLEINFO_VACATION");
1465 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1466 struct do_not_register *dnr;
1467 if ((dnr = chanserv_is_dnr(NULL, hi)))
1468 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1469 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1471 } else if (hi != user->handle_info)
1475 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1477 if (nickserv_conf.email_enabled)
1478 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1482 switch (hi->cookie->type) {
1483 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1484 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1485 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1486 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1487 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1492 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1493 reply("NSMSG_HANDLEINFO_ID", hi->id);
1495 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1497 reply("NSMSG_HANDLEINFO_NO_NOTES");
1499 struct handle_note *prev, *note;
1501 WALK_NOTES(hi, prev, note) {
1502 char set_time[INTERVALLEN];
1503 intervalString(set_time, now - note->set, user->handle_info);
1504 if (note->expires) {
1505 char exp_time[INTERVALLEN];
1506 intervalString(exp_time, note->expires - now, user->handle_info);
1507 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1509 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1516 unsigned long flen = 1;
1517 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1519 for (i=0, flen=1; handle_flags[i]; i++)
1520 if (hi->flags & 1 << i)
1521 flags[flen++] = handle_flags[i];
1523 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1525 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1528 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1529 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1530 || (hi->opserv_level > 0)) {
1531 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1534 if (hi->fakeident && hi->fakehost)
1535 reply("NSMSG_HANDLEINFO_FAKEIDENTHOST", hi->fakeident, hi->fakehost);
1536 else if (hi->fakeident)
1537 reply("NSMSG_HANDLEINFO_FAKEIDENT", hi->fakeident);
1538 else if (hi->fakehost)
1539 reply("NSMSG_HANDLEINFO_FAKEHOST", hi->fakehost);
1541 if (hi->last_quit_host[0])
1542 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1544 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1546 if (nickserv_conf.disable_nicks) {
1547 /* nicks disabled; don't show anything about registered nicks */
1548 } else if (hi->nicks) {
1549 struct nick_info *ni, *next_ni;
1550 for (ni = hi->nicks; ni; ni = next_ni) {
1551 herelen = strlen(ni->nick);
1552 if (pos + herelen + 1 > ArrayLength(buff)) {
1554 goto print_nicks_buff;
1558 memcpy(buff+pos, ni->nick, herelen);
1559 pos += herelen; buff[pos++] = ' ';
1563 reply("NSMSG_HANDLEINFO_NICKS", buff);
1568 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1571 if (hi->masks->used) {
1572 for (i=0; i < hi->masks->used; i++) {
1573 herelen = strlen(hi->masks->list[i]);
1574 if (pos + herelen + 1 > ArrayLength(buff)) {
1576 goto print_mask_buff;
1578 memcpy(buff+pos, hi->masks->list[i], herelen);
1579 pos += herelen; buff[pos++] = ' ';
1580 if (i+1 == hi->masks->used) {
1583 reply("NSMSG_HANDLEINFO_MASKS", buff);
1588 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1592 struct userData *chan, *next;
1595 for (chan = hi->channels; chan; chan = next) {
1596 next = chan->u_next;
1597 name = chan->channel->channel->name;
1598 herelen = strlen(name);
1599 if (pos + herelen + 7 > ArrayLength(buff)) {
1601 goto print_chans_buff;
1603 if (IsUserSuspended(chan))
1605 pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
1609 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1614 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1617 for (target = hi->users; target; target = next_un) {
1618 herelen = strlen(target->nick);
1619 if (pos + herelen + 1 > ArrayLength(buff)) {
1621 goto print_cnick_buff;
1623 next_un = target->next_authed;
1625 memcpy(buff+pos, target->nick, herelen);
1626 pos += herelen; buff[pos++] = ' ';
1630 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1635 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1638 static NICKSERV_FUNC(cmd_userinfo)
1640 struct userNode *target;
1642 NICKSERV_MIN_PARMS(2);
1643 if (!(target = GetUserH(argv[1]))) {
1644 reply("MSG_NICK_UNKNOWN", argv[1]);
1647 if (target->handle_info)
1648 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1650 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1654 static NICKSERV_FUNC(cmd_nickinfo)
1656 struct nick_info *ni;
1658 NICKSERV_MIN_PARMS(2);
1659 if (!(ni = get_nick_info(argv[1]))) {
1660 reply("MSG_NICK_UNKNOWN", argv[1]);
1663 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1667 static NICKSERV_FUNC(cmd_notes)
1669 struct handle_info *hi;
1670 struct handle_note *prev, *note;
1673 NICKSERV_MIN_PARMS(2);
1674 if (!(hi = get_victim_oper(user, argv[1])))
1677 WALK_NOTES(hi, prev, note) {
1678 char set_time[INTERVALLEN];
1679 intervalString(set_time, now - note->set, user->handle_info);
1680 if (note->expires) {
1681 char exp_time[INTERVALLEN];
1682 intervalString(exp_time, note->expires - now, user->handle_info);
1683 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1685 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1689 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1693 static NICKSERV_FUNC(cmd_rename_handle)
1695 struct handle_info *hi;
1696 char msgbuf[MAXLEN], *old_handle;
1699 NICKSERV_MIN_PARMS(3);
1700 if (!(hi = get_victim_oper(user, argv[1])))
1702 if (!is_valid_handle(argv[2])) {
1703 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1706 if (get_handle_info(argv[2])) {
1707 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1710 if (hi->fakehost && hi->fakehost[0] == '.' &&
1711 (strlen(argv[2]) + strlen(hi->fakehost+1) +
1712 strlen(titlehost_suffix) + 2) > HOSTLEN) {
1713 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED_RENAME");
1717 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1718 hi->handle = strdup(argv[2]);
1719 dict_insert(nickserv_handle_dict, hi->handle, hi);
1720 for (nn=0; nn<rf_list_used; nn++)
1721 rf_list[nn](hi, old_handle);
1722 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1723 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1724 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1726 apply_fakehost(hi, NULL);
1730 static failpw_func_t *failpw_func_list;
1731 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1734 reg_failpw_func(failpw_func_t func)
1736 if (failpw_func_used == failpw_func_size) {
1737 if (failpw_func_size) {
1738 failpw_func_size <<= 1;
1739 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1741 failpw_func_size = 8;
1742 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1745 failpw_func_list[failpw_func_used++] = func;
1748 static NICKSERV_FUNC(cmd_auth)
1750 int pw_arg, used, maxlogins;
1751 struct handle_info *hi;
1753 struct userNode *other;
1755 if (user->handle_info) {
1756 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1759 if (IsStamped(user)) {
1760 /* Unauthenticated users might still have been stamped
1761 previously and could therefore have a hidden host;
1762 do not allow them to authenticate. */
1763 reply("NSMSG_STAMPED_AUTH");
1767 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1769 } else if (argc == 2) {
1770 if (nickserv_conf.disable_nicks) {
1771 if (!(hi = get_handle_info(user->nick))) {
1772 reply("NSMSG_HANDLE_NOT_FOUND");
1776 /* try to look up their handle from their nick */
1777 struct nick_info *ni;
1778 ni = get_nick_info(user->nick);
1780 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1787 reply("MSG_MISSING_PARAMS", argv[0]);
1788 svccmd_send_help(user, nickserv, cmd);
1792 reply("NSMSG_HANDLE_NOT_FOUND");
1795 /* Responses from here on look up the language used by the handle they asked about. */
1796 passwd = argv[pw_arg];
1797 if (!valid_user_for(user, hi)) {
1798 if (hi->email_addr && nickserv_conf.email_enabled)
1799 send_message_type(4, user, cmd->parent->bot,
1800 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1803 send_message_type(4, user, cmd->parent->bot,
1804 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1806 argv[pw_arg] = "BADMASK";
1809 if (!checkpass(passwd, hi->passwd)) {
1811 send_message_type(4, user, cmd->parent->bot,
1812 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1813 argv[pw_arg] = "BADPASS";
1814 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1815 if (nickserv_conf.autogag_enabled) {
1816 if (!user->auth_policer.params) {
1817 user->auth_policer.last_req = now;
1818 user->auth_policer.params = nickserv_conf.auth_policer_params;
1820 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1822 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1823 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1824 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1826 argv[pw_arg] = "GAGGED";
1831 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1832 send_message_type(4, user, cmd->parent->bot,
1833 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1834 argv[pw_arg] = "SUSPENDED";
1837 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1838 for (used = 0, other = hi->users; other; other = other->next_authed) {
1839 if (++used >= maxlogins) {
1840 send_message_type(4, user, cmd->parent->bot,
1841 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1843 argv[pw_arg] = "MAXLOGINS";
1847 if (HANDLE_FLAGGED(hi, AUTOHIDE)) {
1848 //ok we have a fakehost set... but we need to set mode +x
1849 irc_svsmode(nickserv,user,"+x");
1852 set_user_handle_info(user, hi, 1);
1853 if (nickserv_conf.email_required && !hi->email_addr)
1854 reply("NSMSG_PLEASE_SET_EMAIL");
1855 if (!is_secure_password(hi->handle, passwd, NULL))
1856 reply("NSMSG_WEAK_PASSWORD");
1857 if (hi->passwd[0] != '$')
1858 cryptpass(passwd, hi->passwd);
1859 if (!hi->masks->used) {
1861 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1862 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1863 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1865 argv[pw_arg] = "****";
1866 reply("NSMSG_AUTH_SUCCESS");
1870 struct handle_info *checklogin(const char *user, const char *pass, const char *numeric, const char *hostmask, const char *ipmask)
1872 struct handle_info *hi;
1873 unsigned int match = 0, ii = 0;
1874 hi = dict_find(nickserv_handle_dict, user, NULL);
1877 /* If no hostmasks on the account, allow it. */
1878 if (hi->masks->used) {
1879 /* If any hostmask matches, allow it. */
1880 for (ii=0; ii<hi->masks->used; ii++)
1881 if (match_ircglob(hostmask, hi->masks->list[ii]) || match_ircglob(ipmask, hi->masks->list[ii])) {
1888 if(!checkpass(pass, hi->passwd))
1890 if (HANDLE_FLAGGED(hi, SUSPENDED))
1892 /** following in one of the next commits
1893 struct last_login *login,*clogin,*old;
1894 unsigned int ii = 0;
1895 login = calloc(1, sizeof(*login));
1896 login->last_login = hi->last_login;
1897 login->hostmask = strdup(hostmask);
1898 login->authtime = now;
1899 login->quittime = 0;
1902 login->loc_pending = strdup(numeric);
1903 for (clogin = hi->last_login; clogin != NULL && ii < 9; clogin = clogin->last_login) {
1904 if(ii == 8 && clogin->last_login) {
1905 old = clogin->last_login;
1906 clogin->last_login = NULL;
1907 free(old->hostmask);
1910 if(old->loc_pending)
1911 free(old->loc_pending);
1915 hi->last_login = login;
1920 char *getfakehost(const char *user)
1922 struct handle_info *hi;
1923 hi = dict_find(nickserv_handle_dict, user, NULL);
1926 return generate_fakehost(hi);
1929 static allowauth_func_t *allowauth_func_list;
1930 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1933 reg_allowauth_func(allowauth_func_t func)
1935 if (allowauth_func_used == allowauth_func_size) {
1936 if (allowauth_func_size) {
1937 allowauth_func_size <<= 1;
1938 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1940 allowauth_func_size = 8;
1941 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1944 allowauth_func_list[allowauth_func_used++] = func;
1947 static NICKSERV_FUNC(cmd_allowauth)
1949 struct userNode *target;
1950 struct handle_info *hi;
1953 NICKSERV_MIN_PARMS(2);
1954 if (!(target = GetUserH(argv[1]))) {
1955 reply("MSG_NICK_UNKNOWN", argv[1]);
1958 if (target->handle_info) {
1959 reply("NSMSG_USER_PREV_AUTH", target->nick);
1962 if (IsStamped(target)) {
1963 /* Unauthenticated users might still have been stamped
1964 previously and could therefore have a hidden host;
1965 do not allow them to authenticate to an account. */
1966 reply("NSMSG_USER_PREV_STAMP", target->nick);
1971 else if (!(hi = get_handle_info(argv[2]))) {
1972 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1976 if (hi->opserv_level > user->handle_info->opserv_level) {
1977 reply("MSG_USER_OUTRANKED", hi->handle);
1980 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1981 || (hi->opserv_level > 0))
1982 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1983 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1986 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1987 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1988 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1989 if (nickserv_conf.email_enabled)
1990 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1992 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1993 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1995 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1997 for (n=0; n<allowauth_func_used; n++)
1998 allowauth_func_list[n](user, target, hi);
2002 static NICKSERV_FUNC(cmd_authcookie)
2004 struct handle_info *hi;
2006 NICKSERV_MIN_PARMS(2);
2007 if (user->handle_info) {
2008 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2011 if (IsStamped(user)) {
2012 /* Unauthenticated users might still have been stamped
2013 previously and could therefore have a hidden host;
2014 do not allow them to authenticate to an account. */
2015 reply("NSMSG_STAMPED_AUTHCOOKIE");
2018 if (!(hi = get_handle_info(argv[1]))) {
2019 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2022 if (!hi->email_addr) {
2023 reply("MSG_SET_EMAIL_ADDR");
2026 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
2030 static NICKSERV_FUNC(cmd_delcookie)
2032 struct handle_info *hi;
2034 hi = user->handle_info;
2036 reply("NSMSG_NO_COOKIE");
2039 switch (hi->cookie->type) {
2042 reply("NSMSG_MUST_TIME_OUT");
2045 nickserv_eat_cookie(hi->cookie);
2046 reply("NSMSG_ATE_COOKIE");
2052 static NICKSERV_FUNC(cmd_odelcookie)
2054 struct handle_info *hi;
2056 NICKSERV_MIN_PARMS(2);
2058 if (!(hi = get_victim_oper(user, argv[1])))
2062 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
2066 nickserv_eat_cookie(hi->cookie);
2067 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
2072 static NICKSERV_FUNC(cmd_resetpass)
2074 struct handle_info *hi;
2075 char crypted[MD5_CRYPT_LENGTH];
2077 NICKSERV_MIN_PARMS(3);
2078 if (user->handle_info) {
2079 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2082 if (IsStamped(user)) {
2083 /* Unauthenticated users might still have been stamped
2084 previously and could therefore have a hidden host;
2085 do not allow them to activate an account. */
2086 reply("NSMSG_STAMPED_RESETPASS");
2089 if (!(hi = get_handle_info(argv[1]))) {
2090 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2093 if (!hi->email_addr) {
2094 reply("MSG_SET_EMAIL_ADDR");
2097 cryptpass(argv[2], crypted);
2099 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
2103 static NICKSERV_FUNC(cmd_cookie)
2105 struct handle_info *hi;
2108 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
2111 NICKSERV_MIN_PARMS(3);
2112 if (!(hi = get_handle_info(argv[1]))) {
2113 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2119 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2120 reply("NSMSG_HANDLE_SUSPENDED");
2125 reply("NSMSG_NO_COOKIE");
2129 /* Check validity of operation before comparing cookie to
2130 * prohibit guessing by authed users. */
2131 if (user->handle_info
2132 && (hi->cookie->type != EMAIL_CHANGE)
2133 && (hi->cookie->type != PASSWORD_CHANGE)) {
2134 reply("NSMSG_CANNOT_COOKIE");
2138 if (strcmp(cookie, hi->cookie->cookie)) {
2139 reply("NSMSG_BAD_COOKIE");
2143 switch (hi->cookie->type) {
2145 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2146 set_user_handle_info(user, hi, 1);
2147 reply("NSMSG_HANDLE_ACTIVATED");
2149 case PASSWORD_CHANGE:
2150 set_user_handle_info(user, hi, 1);
2151 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2152 reply("NSMSG_PASSWORD_CHANGED");
2155 nickserv_set_email_addr(hi, hi->cookie->data);
2156 reply("NSMSG_EMAIL_CHANGED");
2159 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2160 set_user_handle_info(user, hi, 1);
2161 nickserv_addmask(user, hi, mask);
2162 reply("NSMSG_AUTH_SUCCESS");
2167 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2168 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2172 nickserv_eat_cookie(hi->cookie);
2177 static NICKSERV_FUNC(cmd_oregnick) {
2179 struct handle_info *target;
2180 struct nick_info *ni;
2182 NICKSERV_MIN_PARMS(3);
2183 if (!(target = modcmd_get_handle_info(user, argv[1])))
2186 if (!is_registerable_nick(nick)) {
2187 reply("NSMSG_BAD_NICK", nick);
2190 ni = dict_find(nickserv_nick_dict, nick, NULL);
2192 reply("NSMSG_NICK_EXISTS", nick);
2195 register_nick(nick, target);
2196 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2200 static NICKSERV_FUNC(cmd_regnick) {
2202 struct nick_info *ni;
2204 if (!is_registerable_nick(user->nick)) {
2205 reply("NSMSG_BAD_NICK", user->nick);
2208 /* count their nicks, see if it's too many */
2209 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2210 if (n >= nickserv_conf.nicks_per_handle) {
2211 reply("NSMSG_TOO_MANY_NICKS");
2214 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2216 reply("NSMSG_NICK_EXISTS", user->nick);
2219 register_nick(user->nick, user->handle_info);
2220 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2224 static NICKSERV_FUNC(cmd_pass)
2226 struct handle_info *hi;
2227 const char *old_pass, *new_pass;
2229 NICKSERV_MIN_PARMS(3);
2230 hi = user->handle_info;
2234 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2235 if (!checkpass(old_pass, hi->passwd)) {
2236 argv[1] = "BADPASS";
2237 reply("NSMSG_PASSWORD_INVALID");
2240 cryptpass(new_pass, hi->passwd);
2242 reply("NSMSG_PASS_SUCCESS");
2247 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2250 char *new_mask = canonicalize_hostmask(strdup(mask));
2251 for (i=0; i<hi->masks->used; i++) {
2252 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2253 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2258 string_list_append(hi->masks, new_mask);
2259 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2263 static NICKSERV_FUNC(cmd_addmask)
2266 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2267 int res = nickserv_addmask(user, user->handle_info, mask);
2271 if (!is_gline(argv[1])) {
2272 reply("NSMSG_MASK_INVALID", argv[1]);
2275 return nickserv_addmask(user, user->handle_info, argv[1]);
2279 static NICKSERV_FUNC(cmd_oaddmask)
2281 struct handle_info *hi;
2283 NICKSERV_MIN_PARMS(3);
2284 if (!(hi = get_victim_oper(user, argv[1])))
2286 return nickserv_addmask(user, hi, argv[2]);
2290 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2293 for (i=0; i<hi->masks->used; i++) {
2294 if (!strcmp(del_mask, hi->masks->list[i])) {
2295 char *old_mask = hi->masks->list[i];
2296 if (hi->masks->used == 1 && !force) {
2297 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2300 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2301 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2306 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2310 static NICKSERV_FUNC(cmd_delmask)
2312 NICKSERV_MIN_PARMS(2);
2313 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2316 static NICKSERV_FUNC(cmd_odelmask)
2318 struct handle_info *hi;
2319 NICKSERV_MIN_PARMS(3);
2320 if (!(hi = get_victim_oper(user, argv[1])))
2322 return nickserv_delmask(user, hi, argv[2], 1);
2326 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2327 unsigned int nn, add = 1, pos;
2328 unsigned long added, removed, flag;
2330 for (added=removed=nn=0; str[nn]; nn++) {
2332 case '+': add = 1; break;
2333 case '-': add = 0; break;
2335 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2336 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2339 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2340 /* cheesy avoidance of looking up the flag name.. */
2341 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2344 flag = 1 << (pos - 1);
2346 added |= flag, removed &= ~flag;
2348 removed |= flag, added &= ~flag;
2353 *premoved = removed;
2358 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2360 unsigned long before, after, added, removed;
2361 struct userNode *uNode;
2363 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2364 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2366 hi->flags = (hi->flags | added) & ~removed;
2367 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2369 /* Strip helping flag if they're only a support helper and not
2370 * currently in #support. */
2371 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2372 struct channelList *schannels;
2374 schannels = chanserv_support_channels();
2375 for (ii = 0; ii < schannels->used; ++ii)
2376 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2378 if (ii == schannels->used)
2379 HANDLE_CLEAR_FLAG(hi, HELPING);
2382 if (after && !before) {
2383 /* Add user to current helper list. */
2384 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2385 userList_append(&curr_helpers, uNode);
2386 } else if (!after && before) {
2387 /* Remove user from current helper list. */
2388 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2389 userList_remove(&curr_helpers, uNode);
2396 set_list(struct userNode *user, struct handle_info *hi, int override)
2400 char *set_display[] = {
2401 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2402 "EMAIL", "MAXLOGINS", "LANGUAGE", "DEVNULL"
2405 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2407 /* Do this so options are presented in a consistent order. */
2408 for (i = 0; i < ArrayLength(set_display); ++i)
2409 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2410 opt(user, hi, override, 0, NULL);
2413 static NICKSERV_FUNC(cmd_set)
2415 struct handle_info *hi;
2418 hi = user->handle_info;
2420 set_list(user, hi, 0);
2423 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2424 reply("NSMSG_INVALID_OPTION", argv[1]);
2427 return opt(user, hi, 0, argc-1, argv+1);
2430 static NICKSERV_FUNC(cmd_oset)
2432 struct handle_info *hi;
2433 struct svccmd *subcmd;
2435 char cmdname[MAXLEN];
2437 NICKSERV_MIN_PARMS(2);
2439 if (!(hi = get_victim_oper(user, argv[1])))
2443 set_list(user, hi, 0);
2447 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2448 reply("NSMSG_INVALID_OPTION", argv[2]);
2452 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2453 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2454 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2457 return opt(user, hi, 1, argc-2, argv+2);
2460 static OPTION_FUNC(opt_info)
2464 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2466 hi->infoline = NULL;
2468 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2472 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2473 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2477 static OPTION_FUNC(opt_devnull)
2479 const char *devnull;
2483 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2486 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2490 devnull = unsplit_string(argv+1, argc-1, NULL);
2491 if(devnull_check(devnull) == 1) {
2494 hi->devnull = strdup(devnull);
2499 devnull = hi->devnull ? hi->devnull : user_find_message(user, "MSG_NONE");
2500 send_message(user, nickserv, "NSMSG_SET_DEVNULL", devnull);
2504 void nickserv_devnull_delete(char *name) {
2506 struct handle_info *hi;
2508 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2510 if (hi->devnull && !irccasecmp(name, hi->devnull)) {
2517 void nickserv_devnull_rename(char *oldname, char *newname) {
2519 struct handle_info *hi;
2521 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2523 if (hi->devnull && !irccasecmp(oldname, hi->devnull)) {
2524 hi->devnull = strdup(newname);
2529 static OPTION_FUNC(opt_website)
2531 const char *website;
2534 if (!HANDLE_FLAGGED(user->handle_info, BOT)) {
2535 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2538 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2542 website = unsplit_string(argv+1, argc-1, NULL);
2543 hi->website = strdup(website);
2546 if (HANDLE_FLAGGED(user->handle_info, BOT)) {
2547 website = hi->website ? hi->website : user_find_message(user, "MSG_NONE");
2548 send_message(user, nickserv, "NSMSG_SET_WEBSITE", website);
2553 static OPTION_FUNC(opt_width)
2556 hi->screen_width = strtoul(argv[1], NULL, 0);
2558 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2559 hi->screen_width = MIN_LINE_SIZE;
2560 else if (hi->screen_width > MAX_LINE_SIZE)
2561 hi->screen_width = MAX_LINE_SIZE;
2563 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2567 static OPTION_FUNC(opt_tablewidth)
2570 hi->table_width = strtoul(argv[1], NULL, 0);
2572 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2573 hi->table_width = MIN_LINE_SIZE;
2574 else if (hi->screen_width > MAX_LINE_SIZE)
2575 hi->table_width = MAX_LINE_SIZE;
2577 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2581 static OPTION_FUNC(opt_color)
2584 if (enabled_string(argv[1]))
2585 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2586 else if (disabled_string(argv[1]))
2587 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2589 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2594 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2598 static OPTION_FUNC(opt_privmsg)
2601 if (enabled_string(argv[1]))
2602 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2603 else if (disabled_string(argv[1]))
2604 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2606 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2611 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2615 static OPTION_FUNC(opt_autohide)
2618 if (enabled_string(argv[1]))
2619 HANDLE_SET_FLAG(hi, AUTOHIDE);
2620 else if (disabled_string(argv[1]))
2621 HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
2623 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2628 send_message(user, nickserv, "NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
2632 static OPTION_FUNC(opt_style)
2637 if (!irccasecmp(argv[1], "Zoot"))
2638 hi->userlist_style = HI_STYLE_ZOOT;
2639 else if (!irccasecmp(argv[1], "def"))
2640 hi->userlist_style = HI_STYLE_DEF;
2643 switch (hi->userlist_style) {
2652 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2656 static OPTION_FUNC(opt_password)
2659 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2664 cryptpass(argv[1], hi->passwd);
2666 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2670 static OPTION_FUNC(opt_flags)
2673 unsigned int ii, flen;
2676 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2681 nickserv_apply_flags(user, hi, argv[1]);
2683 for (ii = flen = 0; handle_flags[ii]; ii++)
2684 if (hi->flags & (1 << ii))
2685 flags[flen++] = handle_flags[ii];
2688 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2690 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2694 static OPTION_FUNC(opt_email)
2698 if (!is_valid_email_addr(argv[1])) {
2699 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2702 if ((str = mail_prohibited_address(argv[1]))) {
2703 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2706 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2707 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2709 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2711 nickserv_set_email_addr(hi, argv[1]);
2713 nickserv_eat_cookie(hi->cookie);
2714 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2717 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2721 static OPTION_FUNC(opt_maxlogins)
2723 unsigned char maxlogins;
2725 maxlogins = strtoul(argv[1], NULL, 0);
2726 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2727 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2730 hi->maxlogins = maxlogins;
2732 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2733 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2737 static OPTION_FUNC(opt_language)
2739 struct language *lang;
2741 lang = language_find(argv[1]);
2742 if (irccasecmp(lang->name, argv[1]))
2743 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2744 hi->language = lang;
2746 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2750 static OPTION_FUNC(opt_karma)
2753 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2758 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2759 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2760 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2761 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2763 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2767 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2772 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2773 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2775 if ((user->handle_info->opserv_level < target->opserv_level)
2776 || ((user->handle_info->opserv_level == target->opserv_level)
2777 && (user->handle_info->opserv_level < 1000))) {
2778 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2781 if ((user->handle_info->opserv_level < new_level)
2782 || ((user->handle_info->opserv_level == new_level)
2783 && (user->handle_info->opserv_level < 1000))) {
2784 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2787 if (user->handle_info == target) {
2788 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2791 if (target->opserv_level == new_level)
2793 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2794 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2795 target->opserv_level = new_level;
2799 static OPTION_FUNC(opt_level)
2804 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2808 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2809 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2813 static OPTION_FUNC(opt_epithet)
2816 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2820 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2821 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2824 if ((epithet[0] == '*') && !epithet[1])
2827 hi->epithet = strdup(epithet);
2831 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2833 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2837 static OPTION_FUNC(opt_title)
2842 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2846 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2848 if (strchr(title, '.')) {
2849 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2852 if ((strlen(user->handle_info->handle) + strlen(title) +
2853 strlen(titlehost_suffix) + 2) > HOSTLEN) {
2854 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2859 if (!strcmp(title, "*")) {
2860 hi->fakehost = NULL;
2862 hi->fakehost = malloc(strlen(title)+2);
2863 hi->fakehost[0] = '.';
2864 strcpy(hi->fakehost+1, title);
2866 apply_fakehost(hi, NULL);
2867 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2868 title = hi->fakehost + 1;
2872 title = user_find_message(user, "MSG_NONE");
2873 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2877 static OPTION_FUNC(opt_fakehost)
2879 char mask[USERLEN + HOSTLEN + 2];
2883 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2887 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2888 if(strlen(argv[1]) >= sizeof(mask)) {
2889 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
2893 safestrncpy(mask, argv[1], sizeof(mask));
2895 if ((host = strrchr(mask, '@')) && host != mask) {
2896 /* If ident@host was used and the user doesn't have access to set idents, do not change anything. */
2897 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2909 if (ident && strlen(ident) > USERLEN) {
2910 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2914 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
2915 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2919 if (host && host[0]) {
2921 if (!strcmp(host, "*"))
2922 hi->fakehost = NULL;
2924 hi->fakehost = strdup(host);
2925 host = hi->fakehost;
2928 host = generate_fakehost(hi);
2931 free(hi->fakeident);
2932 if (!strcmp(ident, "*"))
2933 hi->fakeident = NULL;
2935 hi->fakeident = strdup(ident);
2936 ident = hi->fakeident;
2939 ident = generate_fakeident(hi, NULL);
2941 apply_fakehost(hi, NULL);
2943 host = generate_fakehost(hi);
2944 ident = generate_fakeident(hi, NULL);
2947 host = (char *) user_find_message(user, "MSG_NONE");
2949 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
2951 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
2955 static OPTION_FUNC(opt_fakeident)
2960 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2964 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2966 if (strlen(ident) > USERLEN) {
2967 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2970 free(hi->fakeident);
2971 if (!strcmp(ident, "*"))
2972 hi->fakeident = NULL;
2974 hi->fakeident = strdup(ident);
2975 ident = hi->fakeident;
2976 apply_fakehost(hi, NULL);
2978 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
2980 ident = user_find_message(user, "MSG_NONE");
2981 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
2985 static NICKSERV_FUNC(cmd_reclaim)
2987 struct nick_info *ni;
2988 struct userNode *victim;
2990 NICKSERV_MIN_PARMS(2);
2991 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2993 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2996 if (ni->owner != user->handle_info) {
2997 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
3000 victim = GetUserH(ni->nick);
3002 reply("MSG_NICK_UNKNOWN", ni->nick);
3005 if (victim == user) {
3006 reply("NSMSG_NICK_USER_YOU");
3009 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
3010 switch (nickserv_conf.reclaim_action) {
3011 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
3012 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
3013 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
3014 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
3019 static NICKSERV_FUNC(cmd_unregnick)
3022 struct handle_info *hi;
3023 struct nick_info *ni;
3025 hi = user->handle_info;
3026 nick = (argc < 2) ? user->nick : (const char*)argv[1];
3027 ni = dict_find(nickserv_nick_dict, nick, NULL);
3029 reply("NSMSG_UNKNOWN_NICK", nick);
3032 if (hi != ni->owner) {
3033 reply("NSMSG_NOT_YOUR_NICK", nick);
3036 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3041 static NICKSERV_FUNC(cmd_ounregnick)
3043 struct nick_info *ni;
3045 NICKSERV_MIN_PARMS(2);
3046 if (!(ni = get_nick_info(argv[1]))) {
3047 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
3050 if (!oper_outranks(user, ni->owner))
3052 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3057 static NICKSERV_FUNC(cmd_unregister)
3059 struct handle_info *hi;
3062 NICKSERV_MIN_PARMS(2);
3063 hi = user->handle_info;
3066 if (checkpass(passwd, hi->passwd)) {
3067 nickserv_unregister_handle(hi, user);
3070 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3071 reply("NSMSG_PASSWORD_INVALID");
3076 static NICKSERV_FUNC(cmd_ounregister)
3078 struct handle_info *hi;
3079 char reason[MAXLEN];
3082 NICKSERV_MIN_PARMS(2);
3083 if (!(hi = get_victim_oper(user, argv[1])))
3086 if (HANDLE_FLAGGED(hi, NODELETE)) {
3087 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
3091 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
3093 ((hi->flags & nickserv_conf.ounregister_flags)
3095 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
3096 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
3100 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
3101 global_message(MESSAGE_RECIPIENT_STAFF, reason);
3102 nickserv_unregister_handle(hi, user);
3106 static NICKSERV_FUNC(cmd_status)
3108 if (nickserv_conf.disable_nicks) {
3109 reply("NSMSG_GLOBAL_STATS_NONICK",
3110 dict_size(nickserv_handle_dict));
3112 if (user->handle_info) {
3114 struct nick_info *ni;
3115 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
3116 reply("NSMSG_HANDLE_STATS", cnt);
3118 reply("NSMSG_HANDLE_NONE");
3120 reply("NSMSG_GLOBAL_STATS",
3121 dict_size(nickserv_handle_dict),
3122 dict_size(nickserv_nick_dict));
3127 static NICKSERV_FUNC(cmd_ghost)
3129 struct userNode *target;
3130 char reason[MAXLEN];
3132 NICKSERV_MIN_PARMS(2);
3133 if (!(target = GetUserH(argv[1]))) {
3134 reply("MSG_NICK_UNKNOWN", argv[1]);
3137 if (target == user) {
3138 reply("NSMSG_CANNOT_GHOST_SELF");
3141 if (!target->handle_info || (target->handle_info != user->handle_info)) {
3142 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
3145 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
3146 DelUser(target, nickserv, 1, reason);
3147 reply("NSMSG_GHOST_KILLED", argv[1]);
3151 static NICKSERV_FUNC(cmd_vacation)
3153 HANDLE_SET_FLAG(user->handle_info, FROZEN);
3154 reply("NSMSG_ON_VACATION");
3158 static NICKSERV_FUNC(cmd_addnote)
3160 struct handle_info *hi;
3161 unsigned long duration;
3164 struct handle_note *prev;
3165 struct handle_note *note;
3167 /* Parse parameters and figure out values for note's fields. */
3168 NICKSERV_MIN_PARMS(4);
3169 hi = get_victim_oper(user, argv[1]);
3172 if(!strcmp(argv[2], "0"))
3174 else if(!(duration = ParseInterval(argv[2])))
3176 reply("MSG_INVALID_DURATION", argv[2]);
3179 if (duration > 2*365*86400) {
3180 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
3183 unsplit_string(argv + 3, argc - 3, text);
3184 WALK_NOTES(hi, prev, note) {}
3185 id = prev ? (prev->id + 1) : 1;
3187 /* Create the new note structure. */
3188 note = calloc(1, sizeof(*note) + strlen(text));
3190 note->expires = duration ? (now + duration) : 0;
3193 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
3194 strcpy(note->note, text);
3199 reply("NSMSG_NOTE_ADDED", id, hi->handle);
3203 static NICKSERV_FUNC(cmd_delnote)
3205 struct handle_info *hi;
3206 struct handle_note *prev;
3207 struct handle_note *note;
3210 NICKSERV_MIN_PARMS(3);
3211 hi = get_victim_oper(user, argv[1]);
3214 id = strtoul(argv[2], NULL, 10);
3215 WALK_NOTES(hi, prev, note) {
3216 if (id == note->id) {
3218 prev->next = note->next;
3220 hi->notes = note->next;
3222 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3226 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3231 nickserv_saxdb_write(struct saxdb_context *ctx) {
3233 struct handle_info *hi;
3236 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3238 assert(hi->id != 0);
3239 saxdb_start_record(ctx, iter_key(it), 0);
3241 struct handle_cookie *cookie = hi->cookie;
3244 switch (cookie->type) {
3245 case ACTIVATION: type = KEY_ACTIVATION; break;
3246 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3247 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3248 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3249 default: type = NULL; break;
3252 saxdb_start_record(ctx, KEY_COOKIE, 0);
3253 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3254 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3256 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3257 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3258 saxdb_end_record(ctx);
3262 struct handle_note *prev, *note;
3263 saxdb_start_record(ctx, KEY_NOTES, 0);
3264 WALK_NOTES(hi, prev, note) {
3265 snprintf(flags, sizeof(flags), "%d", note->id);
3266 saxdb_start_record(ctx, flags, 0);
3268 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3269 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3270 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3271 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3272 saxdb_end_record(ctx);
3274 saxdb_end_record(ctx);
3277 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3279 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3281 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3283 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3287 for (ii=flen=0; handle_flags[ii]; ++ii)
3288 if (hi->flags & (1 << ii))
3289 flags[flen++] = handle_flags[ii];
3291 saxdb_write_string(ctx, KEY_FLAGS, flags);
3293 saxdb_write_int(ctx, KEY_ID, hi->id);
3295 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3297 saxdb_write_string(ctx, KEY_DEVNULL, hi->devnull);
3299 saxdb_write_string(ctx, KEY_WEBSITE, hi->website);
3300 if (hi->last_quit_host[0])
3301 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3302 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3304 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3305 if (hi->masks->used)
3306 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3308 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3310 struct string_list *slist;
3311 struct nick_info *ni;
3313 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3314 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3315 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3319 if (hi->opserv_level)
3320 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3321 if (hi->language != lang_C)
3322 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3323 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3324 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3325 if (hi->screen_width)
3326 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3327 if (hi->table_width)
3328 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3329 flags[0] = hi->userlist_style;
3331 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3332 saxdb_end_record(ctx);
3337 static handle_merge_func_t *handle_merge_func_list;
3338 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3341 reg_handle_merge_func(handle_merge_func_t func)
3343 if (handle_merge_func_used == handle_merge_func_size) {
3344 if (handle_merge_func_size) {
3345 handle_merge_func_size <<= 1;
3346 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3348 handle_merge_func_size = 8;
3349 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3352 handle_merge_func_list[handle_merge_func_used++] = func;
3355 static NICKSERV_FUNC(cmd_merge)
3357 struct handle_info *hi_from, *hi_to;
3358 struct userNode *last_user;
3359 struct userData *cList, *cListNext;
3360 unsigned int ii, jj, n;
3361 char buffer[MAXLEN];
3363 NICKSERV_MIN_PARMS(3);
3365 if (!(hi_from = get_victim_oper(user, argv[1])))
3367 if (!(hi_to = get_victim_oper(user, argv[2])))
3369 if (hi_to == hi_from) {
3370 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3374 for (n=0; n<handle_merge_func_used; n++)
3375 handle_merge_func_list[n](user, hi_to, hi_from);
3377 /* Append "from" handle's nicks to "to" handle's nick list. */
3379 struct nick_info *last_ni;
3380 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3381 last_ni->next = hi_from->nicks;
3383 while (hi_from->nicks) {
3384 hi_from->nicks->owner = hi_to;
3385 hi_from->nicks = hi_from->nicks->next;
3388 /* Merge the hostmasks. */
3389 for (ii=0; ii<hi_from->masks->used; ii++) {
3390 char *mask = hi_from->masks->list[ii];
3391 for (jj=0; jj<hi_to->masks->used; jj++)
3392 if (match_ircglobs(hi_to->masks->list[jj], mask))
3394 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3395 string_list_append(hi_to->masks, strdup(mask));
3398 /* Merge the lists of authed users. */
3400 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3401 last_user->next_authed = hi_from->users;
3403 hi_to->users = hi_from->users;
3405 /* Repoint the old "from" handle's users. */
3406 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3407 last_user->handle_info = hi_to;
3409 hi_from->users = NULL;
3411 /* Merge channel userlists. */
3412 for (cList=hi_from->channels; cList; cList=cListNext) {
3413 struct userData *cList2;
3414 cListNext = cList->u_next;
3415 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3416 if (cList->channel == cList2->channel)
3418 if (cList2 && (cList2->access >= cList->access)) {
3419 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);
3420 /* keep cList2 in hi_to; remove cList from hi_from */
3421 del_channel_user(cList, 1);
3424 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);
3425 /* remove the lower-ranking cList2 from hi_to */
3426 del_channel_user(cList2, 1);
3428 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3430 /* cList needs to be moved from hi_from to hi_to */
3431 cList->handle = hi_to;
3432 /* Remove from linked list for hi_from */
3433 assert(!cList->u_prev);
3434 hi_from->channels = cList->u_next;
3436 cList->u_next->u_prev = cList->u_prev;
3437 /* Add to linked list for hi_to */
3438 cList->u_prev = NULL;
3439 cList->u_next = hi_to->channels;
3440 if (hi_to->channels)
3441 hi_to->channels->u_prev = cList;
3442 hi_to->channels = cList;
3446 /* Do they get an OpServ level promotion? */
3447 if (hi_from->opserv_level > hi_to->opserv_level)
3448 hi_to->opserv_level = hi_from->opserv_level;
3450 /* What about last seen time? */
3451 if (hi_from->lastseen > hi_to->lastseen)
3452 hi_to->lastseen = hi_from->lastseen;
3454 /* New karma is the sum of the two original karmas. */
3455 hi_to->karma += hi_from->karma;
3457 /* Does a fakehost carry over? (This intentionally doesn't set it
3458 * for users previously attached to hi_to. They'll just have to
3461 if (hi_from->fakehost && !hi_to->fakehost)
3462 hi_to->fakehost = strdup(hi_from->fakehost);
3463 if (hi_from->fakeident && !hi_to->fakeident)
3464 hi_to->fakeident = strdup(hi_from->fakeident);
3466 /* Notify of success. */
3467 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3468 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3469 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3471 /* Unregister the "from" handle. */
3472 nickserv_unregister_handle(hi_from, NULL);
3477 #define NICKSERV_DISCRIM_FIELDS_AUTH 0x01
3478 #define NICKSERV_DISCRIM_FIELDS_EMAIL 0x02
3479 #define NICKSERV_DISCRIM_FIELDS_SEEN 0x04
3480 #define NICKSERV_DISCRIM_FIELDS_ACCESS 0x08
3481 #define NICKSERV_DISCRIM_FIELDS_FAKEHOST 0x10
3482 #define NICKSERV_DISCRIM_FIELDS_WEBSITE 0x20
3483 #define NICKSERV_DISCRIM_FIELDS_DEVNULL 0x40
3485 #define NICKSERV_DISCRIM_FIELD_COUNT 7
3487 struct nickserv_discrim {
3488 unsigned int show_fields;
3489 struct helpfile_table *output_table;
3490 int output_table_pos;
3491 unsigned int output_table_free_fields;
3493 unsigned long flags_on, flags_off;
3494 unsigned long min_registered, max_registered;
3495 unsigned long lastseen;
3497 int min_level, max_level;
3498 int min_karma, max_karma;
3499 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3500 const char *nickmask;
3501 const char *hostmask;
3502 const char *fakehostmask;
3503 const char *fakeidentmask;
3504 const char *website;
3505 const char *devnullclass;
3506 const char *handlemask;
3507 const char *emailmask;
3510 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi, struct nickserv_discrim *discrim);
3512 struct discrim_apply_info {
3513 struct nickserv_discrim *discrim;
3514 discrim_search_func func;
3515 struct userNode *source;
3516 unsigned int matched;
3519 static struct nickserv_discrim *
3520 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3523 struct nickserv_discrim *discrim;
3525 discrim = malloc(sizeof(*discrim));
3526 memset(discrim, 0, sizeof(*discrim));
3527 discrim->min_level = 0;
3528 discrim->max_level = INT_MAX;
3529 discrim->limit = 50;
3530 discrim->min_registered = 0;
3531 discrim->max_registered = ULONG_MAX;
3532 discrim->lastseen = ULONG_MAX;
3533 discrim->min_karma = INT_MIN;
3534 discrim->max_karma = INT_MAX;
3536 for (i=0; i<argc; i++) {
3537 if (i == argc - 1) {
3538 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3541 if (!irccasecmp(argv[i], "limit")) {
3542 discrim->limit = strtoul(argv[++i], NULL, 0);
3543 } else if (!irccasecmp(argv[i], "flags")) {
3544 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3545 } else if (!irccasecmp(argv[i], "fields")) {
3546 char *fields = argv[++i];
3547 char *delimiter = strstr(fields, ",");
3551 if(!irccasecmp(fields, "auth"))
3552 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_AUTH;
3553 else if(!irccasecmp(fields, "email"))
3554 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_EMAIL;
3555 else if(!irccasecmp(fields, "seen"))
3556 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_SEEN;
3557 else if(!irccasecmp(fields, "access"))
3558 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_ACCESS;
3559 else if(!irccasecmp(fields, "fakehost"))
3560 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_FAKEHOST;
3561 else if(!irccasecmp(fields, "website") && IsBot(user))
3562 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_WEBSITE;
3563 else if(!irccasecmp(fields, "devnull"))
3564 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_DEVNULL;
3566 send_message(user, nickserv, "MSG_INVALID_FIELD", fields);
3571 fields = delimiter+1;
3573 delimiter = strstr(fields, ",");
3579 } else if (!irccasecmp(argv[i], "registered")) {
3580 const char *cmp = argv[++i];
3581 if (cmp[0] == '<') {
3582 if (cmp[1] == '=') {
3583 discrim->min_registered = now - ParseInterval(cmp+2);
3585 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3587 } else if (cmp[0] == '=') {
3588 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3589 } else if (cmp[0] == '>') {
3590 if (cmp[1] == '=') {
3591 discrim->max_registered = now - ParseInterval(cmp+2);
3593 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3596 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3598 } else if (!irccasecmp(argv[i], "seen")) {
3599 discrim->lastseen = now - ParseInterval(argv[++i]);
3600 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3601 discrim->nickmask = argv[++i];
3602 } else if (!irccasecmp(argv[i], "hostmask")) {
3604 if (!irccasecmp(argv[i], "exact")) {
3605 if (i == argc - 1) {
3606 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3609 discrim->hostmask_type = EXACT;
3610 } else if (!irccasecmp(argv[i], "subset")) {
3611 if (i == argc - 1) {
3612 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3615 discrim->hostmask_type = SUBSET;
3616 } else if (!irccasecmp(argv[i], "superset")) {
3617 if (i == argc - 1) {
3618 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3621 discrim->hostmask_type = SUPERSET;
3622 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3623 if (i == argc - 1) {
3624 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3627 discrim->hostmask_type = LASTQUIT;
3630 discrim->hostmask_type = SUPERSET;
3632 discrim->hostmask = argv[++i];
3633 } else if (!irccasecmp(argv[i], "fakehost")) {
3634 if (!irccasecmp(argv[++i], "*")) {
3635 discrim->fakehostmask = 0;
3637 discrim->fakehostmask = argv[i];
3639 } else if (!irccasecmp(argv[i], "fakeident")) {
3640 if (!irccasecmp(argv[++i], "*")) {
3641 discrim->fakeidentmask = 0;
3643 discrim->fakeidentmask = argv[i];
3645 } else if (!irccasecmp(argv[i], "website")) {
3646 if (!irccasecmp(argv[++i], "*")) {
3647 discrim->website = 0;
3649 discrim->website = argv[i];
3651 } else if (!irccasecmp(argv[i], "devnull")) {
3652 if (!irccasecmp(argv[++i], "*")) {
3653 discrim->devnullclass = 0;
3655 discrim->devnullclass = argv[i];
3657 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3658 if (!irccasecmp(argv[++i], "*")) {
3659 discrim->handlemask = 0;
3661 discrim->handlemask = argv[i];
3663 } else if (!irccasecmp(argv[i], "email")) {
3664 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3665 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3667 } else if (!irccasecmp(argv[++i], "*")) {
3668 discrim->emailmask = 0;
3670 discrim->emailmask = argv[i];
3672 } else if (!irccasecmp(argv[i], "access")) {
3673 const char *cmp = argv[++i];
3674 if (cmp[0] == '<') {
3675 if (discrim->min_level == 0) discrim->min_level = 1;
3676 if (cmp[1] == '=') {
3677 discrim->max_level = strtoul(cmp+2, NULL, 0);
3679 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3681 } else if (cmp[0] == '=') {
3682 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3683 } else if (cmp[0] == '>') {
3684 if (cmp[1] == '=') {
3685 discrim->min_level = strtoul(cmp+2, NULL, 0);
3687 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3690 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3692 } else if (!irccasecmp(argv[i], "karma")) {
3693 const char *cmp = argv[++i];
3694 if (cmp[0] == '<') {
3695 if (cmp[1] == '=') {
3696 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3698 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3700 } else if (cmp[0] == '=') {
3701 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3702 } else if (cmp[0] == '>') {
3703 if (cmp[1] == '=') {
3704 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3706 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3709 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3712 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3723 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3725 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3726 || (discrim->flags_off & hi->flags)
3727 || (discrim->min_registered > hi->registered)
3728 || (discrim->max_registered < hi->registered)
3729 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3730 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3731 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3732 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3733 || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website)))
3734 || (discrim->devnullclass && (!hi->devnull || !match_ircglob(hi->devnull, discrim->devnullclass)))
3735 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3736 || (discrim->min_level > hi->opserv_level)
3737 || (discrim->max_level < hi->opserv_level)
3738 || (discrim->min_karma > hi->karma)
3739 || (discrim->max_karma < hi->karma)
3743 if (discrim->hostmask) {
3745 for (i=0; i<hi->masks->used; i++) {
3746 const char *mask = hi->masks->list[i];
3747 if ((discrim->hostmask_type == SUBSET)
3748 && (match_ircglobs(discrim->hostmask, mask))) break;
3749 else if ((discrim->hostmask_type == EXACT)
3750 && !irccasecmp(discrim->hostmask, mask)) break;
3751 else if ((discrim->hostmask_type == SUPERSET)
3752 && (match_ircglobs(mask, discrim->hostmask))) break;
3753 else if ((discrim->hostmask_type == LASTQUIT)
3754 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3756 if (i==hi->masks->used) return 0;
3758 if (discrim->nickmask) {
3759 struct nick_info *nick = hi->nicks;
3761 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3764 if (!nick) return 0;
3770 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3772 dict_iterator_t it, next;
3773 unsigned int matched;
3775 for (it = dict_first(nickserv_handle_dict), matched = 0;
3776 it && (matched < discrim->limit);
3778 next = iter_next(it);
3779 if (nickserv_discrim_match(discrim, iter_data(it))) {
3780 dsf(source, iter_data(it), discrim);
3788 search_print_func(struct userNode *source, struct handle_info *match, struct nickserv_discrim *discrim)
3790 if(discrim->show_fields) {
3792 if(discrim->output_table) {
3793 discrim->output_table->contents[++discrim->output_table_pos] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
3795 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
3796 discrim->output_table->contents[discrim->output_table_pos][i++] = match->handle;
3797 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
3798 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->email_addr ? match->email_addr : "*");
3799 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN) {
3801 char seenBuf[INTERVALLEN];
3804 } else if(match->lastseen == 0) {
3807 seen = intervalString(seenBuf, now - match->lastseen, source->handle_info);
3809 discrim->output_table_free_fields |= 1 << i;
3810 discrim->output_table->contents[discrim->output_table_pos][i++] = strdup(seen);
3812 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
3813 discrim->output_table->contents[discrim->output_table_pos][i++] = strtab(match->opserv_level);
3814 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
3815 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->fakehost ? match->fakehost : "*");
3816 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
3817 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->website ? match->website : "*");
3818 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
3819 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->devnull ? match->devnull : "*");
3823 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3827 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match), UNUSED_ARG(struct nickserv_discrim *discrim))
3832 search_unregister_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
3834 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3835 nickserv_unregister_handle(match, source);
3839 nickserv_sort_accounts_by_access(const void *a, const void *b)
3841 const struct handle_info *hi_a = *(const struct handle_info**)a;
3842 const struct handle_info *hi_b = *(const struct handle_info**)b;
3843 if (hi_a->opserv_level != hi_b->opserv_level)
3844 return hi_b->opserv_level - hi_a->opserv_level;
3845 return irccasecmp(hi_a->handle, hi_b->handle);
3849 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3851 struct handle_info_list hil;
3852 struct helpfile_table tbl;
3857 memset(&hil, 0, sizeof(hil));
3858 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3859 struct handle_info *hi = iter_data(it);
3860 if (hi->opserv_level)
3861 handle_info_list_append(&hil, hi);
3863 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3864 tbl.length = hil.used + 1;
3866 tbl.flags = TABLE_NO_FREE;
3867 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3868 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3871 for (ii = 0; ii < hil.used; ) {
3872 ary = malloc(tbl.width * sizeof(ary[0]));
3873 ary[0] = hil.list[ii]->handle;
3874 ary[1] = strtab(hil.list[ii]->opserv_level);
3875 tbl.contents[++ii] = ary;
3877 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3878 reply("MSG_MATCH_COUNT", hil.used);
3879 for (ii = 0; ii < hil.used; ii++)
3880 free(tbl.contents[ii]);
3885 static NICKSERV_FUNC(cmd_search)
3887 struct nickserv_discrim *discrim;
3888 discrim_search_func action;
3889 struct svccmd *subcmd;
3890 unsigned int matches;
3893 NICKSERV_MIN_PARMS(3);
3894 sprintf(buf, "search %s", argv[1]);
3895 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3896 if (!irccasecmp(argv[1], "print"))
3897 action = search_print_func;
3898 else if (!irccasecmp(argv[1], "count"))
3899 action = search_count_func;
3900 else if (!irccasecmp(argv[1], "unregister"))
3901 action = search_unregister_func;
3903 reply("NSMSG_INVALID_ACTION", argv[1]);
3907 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3910 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3914 if (action == search_print_func)
3915 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3916 else if (action == search_count_func)
3917 discrim->limit = INT_MAX;
3919 matches = nickserv_discrim_search(discrim, action, user);
3921 if(discrim->show_fields) {
3924 for(ii = 0; ii < NICKSERV_DISCRIM_FIELD_COUNT; ii++) {
3925 if(discrim->show_fields & (1 << ii)) width++;
3927 discrim->output_table = malloc(sizeof(discrim->output_table[0]));
3928 discrim->output_table->length = matches+1;
3929 discrim->output_table->width = width;
3930 discrim->output_table->flags = TABLE_NO_FREE;
3931 discrim->output_table->contents = malloc(discrim->output_table->length * sizeof(discrim->output_table->contents[0]));
3932 discrim->output_table->contents[0] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
3935 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
3936 discrim->output_table->contents[0][ii++] = "Auth";
3937 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
3938 discrim->output_table->contents[0][ii++] = "EMail";
3939 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN)
3940 discrim->output_table->contents[0][ii++] = "Seen";
3941 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
3942 discrim->output_table->contents[0][ii++] = "Access";
3943 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
3944 discrim->output_table->contents[0][ii++] = "Fakehost";
3945 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
3946 discrim->output_table->contents[0][ii++] = "Website";
3947 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
3948 discrim->output_table->contents[0][ii++] = "DevNull";
3950 nickserv_discrim_search(discrim, action, user);
3952 table_send(nickserv, user->nick, 0, NULL, *discrim->output_table);
3954 for(ii = 1; ii < discrim->output_table->length; ++ii) {
3956 for(ij = 0; ij < NICKSERV_DISCRIM_FIELD_COUNT; ij++) {
3957 if(discrim->output_table_free_fields & (1 << ij))
3958 free((char*)discrim->output_table->contents[ii][ij]);
3960 free(discrim->output_table->contents[ii]);
3962 free(discrim->output_table->contents[0]);
3963 free(discrim->output_table->contents);
3964 free(discrim->output_table);
3967 reply("MSG_MATCH_COUNT", matches);
3969 reply("MSG_NO_MATCHES");
3976 static MODCMD_FUNC(cmd_checkpass)
3978 struct handle_info *hi;
3980 NICKSERV_MIN_PARMS(3);
3981 if (!(hi = get_handle_info(argv[1]))) {
3982 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3985 if (checkpass(argv[2], hi->passwd))
3986 reply("CHECKPASS_YES");
3988 reply("CHECKPASS_NO");
3993 static MODCMD_FUNC(cmd_checkemail)
3995 struct handle_info *hi;
3997 NICKSERV_MIN_PARMS(3);
3998 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
4001 if (!hi->email_addr)
4002 reply("CHECKEMAIL_NOT_SET");
4003 else if (!irccasecmp(argv[2], hi->email_addr))
4004 reply("CHECKEMAIL_YES");
4006 reply("CHECKEMAIL_NO");
4012 nickserv_db_read_handle(const char *handle, dict_t obj)
4015 struct string_list *masks, *slist;
4016 struct handle_info *hi;
4017 struct userNode *authed_users;
4018 struct userData *channel_list;
4023 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
4024 id = str ? strtoul(str, NULL, 0) : 0;
4025 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
4027 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
4030 if ((hi = get_handle_info(handle))) {
4031 authed_users = hi->users;
4032 channel_list = hi->channels;
4034 hi->channels = NULL;
4035 dict_remove(nickserv_handle_dict, hi->handle);
4037 authed_users = NULL;
4038 channel_list = NULL;
4040 hi = register_handle(handle, str, id);
4042 hi->users = authed_users;
4043 while (authed_users) {
4044 authed_users->handle_info = hi;
4045 authed_users = authed_users->next_authed;
4048 hi->channels = channel_list;
4049 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
4050 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
4051 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
4052 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
4053 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
4054 hi->language = language_find(str ? str : "C");
4055 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
4056 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
4057 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
4059 hi->infoline = strdup(str);
4060 str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING);
4062 hi->website = strdup(str);
4063 str = database_get_data(obj, KEY_DEVNULL, RECDB_QSTRING);
4065 hi->devnull = strdup(str);
4066 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
4067 hi->registered = str ? strtoul(str, NULL, 0) : now;
4068 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
4069 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
4070 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
4071 hi->karma = str ? strtoul(str, NULL, 0) : 0;
4072 /* We want to read the nicks even if disable_nicks is set. This is so
4073 * that we don't lose the nick data entirely. */
4074 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
4076 for (ii=0; ii<slist->used; ii++)
4077 register_nick(slist->list[ii], hi);
4079 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
4081 for (ii=0; str[ii]; ii++)
4082 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
4084 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
4085 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
4086 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
4087 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
4088 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
4089 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
4090 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
4092 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
4094 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
4095 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
4097 nickserv_set_email_addr(hi, str);
4098 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
4100 hi->epithet = strdup(str);
4101 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
4103 hi->fakehost = strdup(str);
4104 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
4106 hi->fakeident = strdup(str);
4107 /* Read the "cookie" sub-database (if it exists). */
4108 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
4110 const char *data, *type, *expires, *cookie_str;
4111 struct handle_cookie *cookie;
4113 cookie = calloc(1, sizeof(*cookie));
4114 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
4115 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
4116 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
4117 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
4118 if (!type || !expires || !cookie_str) {
4119 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
4122 if (!irccasecmp(type, KEY_ACTIVATION))
4123 cookie->type = ACTIVATION;
4124 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
4125 cookie->type = PASSWORD_CHANGE;
4126 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
4127 cookie->type = EMAIL_CHANGE;
4128 else if (!irccasecmp(type, KEY_ALLOWAUTH))
4129 cookie->type = ALLOWAUTH;
4131 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
4134 cookie->expires = strtoul(expires, NULL, 0);
4135 if (cookie->expires < now)
4138 cookie->data = strdup(data);
4139 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
4143 nickserv_bake_cookie(cookie);
4145 nickserv_free_cookie(cookie);
4147 /* Read the "notes" sub-database (if it exists). */
4148 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
4151 struct handle_note *last_note;
4152 struct handle_note *note;
4155 for (it = dict_first(subdb); it; it = iter_next(it)) {
4156 const char *expires;
4160 const char *note_id;
4163 note_id = iter_key(it);
4164 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
4166 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
4169 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
4170 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
4171 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
4172 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
4173 if (!setter || !text || !set) {
4174 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
4177 note = calloc(1, sizeof(*note) + strlen(text));
4179 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
4180 note->set = strtoul(set, NULL, 10);
4181 note->id = strtoul(note_id, NULL, 10);
4182 safestrncpy(note->setter, setter, sizeof(note->setter));
4183 strcpy(note->note, text);
4185 last_note->next = note;
4194 nickserv_saxdb_read(dict_t db) {
4196 struct record_data *rd;
4198 for (it=dict_first(db); it; it=iter_next(it)) {
4200 nickserv_db_read_handle(iter_key(it), rd->d.object);
4205 static NICKSERV_FUNC(cmd_mergedb)
4207 struct timeval start, stop;
4210 NICKSERV_MIN_PARMS(2);
4211 gettimeofday(&start, NULL);
4212 if (!(db = parse_database(argv[1]))) {
4213 reply("NSMSG_DB_UNREADABLE", argv[1]);
4216 nickserv_saxdb_read(db);
4218 gettimeofday(&stop, NULL);
4219 stop.tv_sec -= start.tv_sec;
4220 stop.tv_usec -= start.tv_usec;
4221 if (stop.tv_usec < 0) {
4223 stop.tv_usec += 1000000;
4225 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
4230 expire_handles(UNUSED_ARG(void *data))
4232 dict_iterator_t it, next;
4233 unsigned long expiry;
4234 struct handle_info *hi;
4236 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4237 next = iter_next(it);
4239 if ((hi->opserv_level > 0)
4241 || HANDLE_FLAGGED(hi, FROZEN)
4242 || HANDLE_FLAGGED(hi, NODELETE)) {
4245 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4246 if ((now - hi->lastseen) > expiry) {
4247 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
4248 nickserv_unregister_handle(hi, NULL);
4252 if (nickserv_conf.handle_expire_frequency)
4253 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4257 nickserv_load_dict(const char *fname)
4261 if (!(file = fopen(fname, "r"))) {
4262 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4265 while (fgets(line, sizeof(line), file)) {
4268 if (line[strlen(line)-1] == '\n')
4269 line[strlen(line)-1] = 0;
4270 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4273 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4276 static enum reclaim_action
4277 reclaim_action_from_string(const char *str) {
4279 return RECLAIM_NONE;
4280 else if (!irccasecmp(str, "warn"))
4281 return RECLAIM_WARN;
4282 else if (!irccasecmp(str, "svsnick"))
4283 return RECLAIM_SVSNICK;
4284 else if (!irccasecmp(str, "kill"))
4285 return RECLAIM_KILL;
4287 return RECLAIM_NONE;
4291 nickserv_conf_read(void)
4293 dict_t conf_node, child;
4297 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4298 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4301 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4303 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4304 if (nickserv_conf.valid_handle_regex_set)
4305 regfree(&nickserv_conf.valid_handle_regex);
4307 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4308 nickserv_conf.valid_handle_regex_set = !err;
4309 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4311 nickserv_conf.valid_handle_regex_set = 0;
4313 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4314 if (nickserv_conf.valid_nick_regex_set)
4315 regfree(&nickserv_conf.valid_nick_regex);
4317 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4318 nickserv_conf.valid_nick_regex_set = !err;
4319 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4321 nickserv_conf.valid_nick_regex_set = 0;
4323 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4325 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4326 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4327 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4328 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4329 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4330 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4331 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4332 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4333 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4334 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4335 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4336 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4337 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4338 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4339 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4340 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4341 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4342 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4343 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4344 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4345 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4346 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4347 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4348 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4349 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
4350 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
4351 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4353 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4354 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4355 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4357 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4358 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4359 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4361 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4362 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4363 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4364 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4365 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4366 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4367 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4368 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4369 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4370 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4371 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4374 nickserv_conf.ounregister_flags = 0;
4376 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4379 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4381 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4383 nickserv_conf.handle_ts_mode = TS_IGNORE;
4384 else if (!irccasecmp(str, "ircu"))
4385 nickserv_conf.handle_ts_mode = TS_IRCU;
4387 nickserv_conf.handle_ts_mode = TS_IGNORE;
4388 if (!nickserv_conf.disable_nicks) {
4389 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4390 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4391 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4392 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4393 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4394 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4395 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4396 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4398 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4399 for (it=dict_first(child); it; it=iter_next(it)) {
4400 const char *key = iter_key(it), *value;
4404 if (!strncasecmp(key, "uc_", 3))
4405 flag = toupper(key[3]);
4406 else if (!strncasecmp(key, "lc_", 3))
4407 flag = tolower(key[3]);
4411 if ((pos = handle_inverse_flags[flag])) {
4412 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4413 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4416 if (nickserv_conf.weak_password_dict)
4417 dict_delete(nickserv_conf.weak_password_dict);
4418 nickserv_conf.weak_password_dict = dict_new();
4419 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4420 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4421 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4422 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4424 nickserv_load_dict(str);
4425 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4426 if (nickserv && str)
4427 NickChange(nickserv, str, 0);
4428 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4429 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4430 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4431 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4432 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4433 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4434 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4435 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4436 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4437 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4438 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4439 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4440 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4441 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4442 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4443 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4444 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4445 titlehost_suffix = str ? str : "example.net";
4446 str = conf_get_data("server/network", RECDB_QSTRING);
4447 nickserv_conf.network_name = str ? str : "some IRC network";
4448 if (!nickserv_conf.auth_policer_params) {
4449 nickserv_conf.auth_policer_params = policer_params_new();
4450 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4451 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4453 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4454 for (it=dict_first(child); it; it=iter_next(it))
4455 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4459 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4461 char newnick[NICKLEN+1];
4470 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4472 case RECLAIM_SVSNICK:
4474 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4475 } while (GetUserH(newnick));
4476 irc_svsnick(nickserv, user, newnick);
4479 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4480 DelUser(user, nickserv, 1, msg);
4486 nickserv_reclaim_p(void *data) {
4487 struct userNode *user = data;
4488 struct nick_info *ni = get_nick_info(user->nick);
4490 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4494 check_user_nick(struct userNode *user) {
4495 struct nick_info *ni;
4496 user->modes &= ~FLAGS_REGNICK;
4497 if (!(ni = get_nick_info(user->nick)))
4499 if (user->handle_info == ni->owner) {
4500 user->modes |= FLAGS_REGNICK;
4504 if (nickserv_conf.warn_nick_owned)
4505 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4506 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4508 if (nickserv_conf.auto_reclaim_delay)
4509 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4511 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4515 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4517 struct handle_info *hi = NULL;
4520 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4521 if ((hi == NULL) && (serial != 0)) {
4523 inttobase64(id, serial, IDLEN);
4524 hi = dict_find(nickserv_id_dict, id, NULL);
4528 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4529 && (timestamp != hi->registered)) {
4532 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4535 set_user_handle_info(user, hi, 0);
4537 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4542 handle_nick_change(struct userNode *user, const char *old_nick)
4544 struct handle_info *hi;
4546 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4547 dict_remove(nickserv_allow_auth_dict, old_nick);
4548 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4550 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4551 check_user_nick(user);
4555 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4557 dict_remove(nickserv_allow_auth_dict, user->nick);
4558 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4559 set_user_handle_info(user, NULL, 0);
4562 static struct modcmd *
4563 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4565 if (min_level > 0) {
4567 sprintf(buf, "%u", min_level);
4568 if (must_be_qualified) {
4569 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4571 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4573 } else if (min_level == 0) {
4574 if (must_be_qualified) {
4575 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4577 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4580 if (must_be_qualified) {
4581 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4583 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4589 nickserv_db_cleanup(void)
4591 unreg_del_user_func(nickserv_remove_user);
4592 userList_clean(&curr_helpers);
4593 policer_params_delete(nickserv_conf.auth_policer_params);
4594 dict_delete(nickserv_handle_dict);
4595 dict_delete(nickserv_nick_dict);
4596 dict_delete(nickserv_opt_dict);
4597 dict_delete(nickserv_allow_auth_dict);
4598 dict_delete(nickserv_email_dict);
4599 dict_delete(nickserv_id_dict);
4600 dict_delete(nickserv_conf.weak_password_dict);
4601 free(auth_func_list);
4602 free(unreg_func_list);
4604 free(allowauth_func_list);
4605 free(handle_merge_func_list);
4606 free(failpw_func_list);
4607 if (nickserv_conf.valid_handle_regex_set)
4608 regfree(&nickserv_conf.valid_handle_regex);
4609 if (nickserv_conf.valid_nick_regex_set)
4610 regfree(&nickserv_conf.valid_nick_regex);
4614 init_nickserv(const char *nick)
4617 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4618 reg_new_user_func(check_user_nick);
4619 reg_nick_change_func(handle_nick_change);
4620 reg_del_user_func(nickserv_remove_user);
4621 reg_account_func(handle_account);
4623 /* set up handle_inverse_flags */
4624 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4625 for (i=0; handle_flags[i]; i++) {
4626 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4627 flag_access_levels[i] = 0;
4630 conf_register_reload(nickserv_conf_read);
4631 nickserv_opt_dict = dict_new();
4632 nickserv_email_dict = dict_new();
4633 dict_set_free_keys(nickserv_email_dict, free);
4634 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4636 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4637 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4638 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4639 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4640 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4641 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4642 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4643 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4644 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4645 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4646 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4647 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4648 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4649 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4650 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4651 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4652 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4653 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4654 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4655 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4656 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4657 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4658 if (!nickserv_conf.disable_nicks) {
4659 /* nick management commands */
4660 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4661 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4662 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4663 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4664 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4665 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4667 if (nickserv_conf.email_enabled) {
4668 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4669 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4670 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4671 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4672 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4673 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4675 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4676 /* miscellaneous commands */
4677 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4678 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4679 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4680 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4681 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4682 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4684 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4685 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4686 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4687 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4688 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4689 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4690 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
4691 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4692 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4693 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4694 dict_insert(nickserv_opt_dict, "WEBSITE", opt_website);
4695 dict_insert(nickserv_opt_dict, "DEVNULL", opt_devnull);
4696 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4697 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4698 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4699 if (titlehost_suffix) {
4700 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4701 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4702 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4704 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4705 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4706 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4707 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4709 nickserv_handle_dict = dict_new();
4710 dict_set_free_keys(nickserv_handle_dict, free);
4711 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4713 nickserv_id_dict = dict_new();
4714 dict_set_free_keys(nickserv_id_dict, free);
4716 nickserv_nick_dict = dict_new();
4717 dict_set_free_data(nickserv_nick_dict, free);
4719 nickserv_allow_auth_dict = dict_new();
4721 userList_init(&curr_helpers);
4724 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4725 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4726 nickserv_service = service_register(nickserv);
4728 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4729 reg_exit_func(nickserv_db_cleanup);
4730 if(nickserv_conf.handle_expire_frequency)
4731 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4732 message_register_table(msgtab);