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_MODSTAFF_LEVEL "modstaff_level"
52 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
53 #define KEY_SET_TITLE_LEVEL "set_title_level"
54 #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
55 #define KEY_SET_FAKEIDENT_LEVEL "set_fakeident_level"
56 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
57 #define KEY_FLAG_LEVELS "flag_levels"
58 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
59 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
60 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
61 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
62 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
63 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
64 #define KEY_DICT_FILE "dict_file"
65 #define KEY_NICK "nick"
66 #define KEY_LANGUAGE "language"
67 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
68 #define KEY_AUTOGAG_DURATION "autogag_duration"
69 #define KEY_AUTH_POLICER "auth_policer"
70 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
71 #define KEY_EMAIL_ENABLED "email_enabled"
72 #define KEY_EMAIL_REQUIRED "email_required"
73 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
74 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
75 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
76 #define KEY_OUNREGISTER_INACTIVE "ounregister_inactive"
77 #define KEY_OUNREGISTER_FLAGS "ounregister_flags"
78 #define KEY_HANDLE_TS_MODE "account_timestamp_mode"
79 #define KEY_MAX_AUTHLOG_LEN "max_authlog_len"
82 #define KEY_PASSWD "passwd"
83 #define KEY_NICKS "nicks"
84 #define KEY_MASKS "masks"
85 #define KEY_OPSERV_LEVEL "opserv_level"
86 #define KEY_STAFF_LEVEL "staff_level"
87 #define KEY_FLAGS "flags"
88 #define KEY_REGISTER_ON "register"
89 #define KEY_LAST_SEEN "lastseen"
90 #define KEY_INFO "info"
91 #define KEY_DEVNULL "devnull"
92 #define KEY_WEBSITE "website"
93 #define KEY_USERLIST_STYLE "user_style"
94 #define KEY_SCREEN_WIDTH "screen_width"
95 #define KEY_LAST_AUTHED_HOST "last_authed_host"
96 #define KEY_LAST_QUIT_HOST "last_quit_host"
97 #define KEY_EMAIL_ADDR "email_addr"
98 #define KEY_COOKIE "cookie"
99 #define KEY_COOKIE_DATA "data"
100 #define KEY_COOKIE_TYPE "type"
101 #define KEY_COOKIE_EXPIRES "expires"
102 #define KEY_ACTIVATION "activation"
103 #define KEY_PASSWORD_CHANGE "password change"
104 #define KEY_EMAIL_CHANGE "email change"
105 #define KEY_ALLOWAUTH "allowauth"
106 #define KEY_EPITHET "epithet"
107 #define KEY_TABLE_WIDTH "table_width"
108 #define KEY_MAXLOGINS "maxlogins"
109 #define KEY_FAKEHOST "fakehost"
110 #define KEY_FAKEIDENT "fakeident"
111 #define KEY_NOTES "notes"
112 #define KEY_NOTE_EXPIRES "expires"
113 #define KEY_NOTE_SET "set"
114 #define KEY_NOTE_SETTER "setter"
115 #define KEY_NOTE_NOTE "note"
116 #define KEY_KARMA "karma"
117 #define KEY_AUTHLOG "authlog"
118 #define KEY_AUTHLOG_LOGIN_TIME "login_time"
119 #define KEY_AUTHLOG_LOGOUT_TIME "logout_time"
120 #define KEY_AUTHLOG_HOSTMASK "hostmask"
121 #define KEY_AUTHLOG_QUIT_REASON "quit_reason"
123 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
125 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
126 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
127 typedef OPTION_FUNC(option_func_t);
129 DEFINE_LIST(handle_info_list, struct handle_info*)
131 #define NICKSERV_MIN_PARMS(N) do { \
133 reply("MSG_MISSING_PARAMS", argv[0]); \
134 svccmd_send_help(user, nickserv, cmd); \
138 struct userNode *nickserv;
139 struct userList curr_helpers;
140 const char *handle_flags = HANDLE_FLAGS;
142 static struct module *nickserv_module;
143 static struct service *nickserv_service;
144 static struct log_type *NS_LOG;
145 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
146 static dict_t nickserv_id_dict; /* contains struct handle_info* */
147 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
148 static dict_t nickserv_opt_dict; /* contains option_func_t* */
149 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
150 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
151 static char handle_inverse_flags[256];
152 static unsigned int flag_access_levels[32];
153 static const struct message_entry msgtab[] = {
154 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
155 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
156 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
157 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
158 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
159 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
160 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
161 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
162 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
163 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
164 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
165 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
166 { "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." },
167 { "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." },
168 { "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." },
169 { "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." },
170 { "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." },
171 { "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." },
172 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
173 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
174 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
175 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
176 { "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." },
177 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
178 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
179 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
180 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
181 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
182 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
183 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
184 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
185 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
186 { "NSMSG_ATE_COOKIE_FOREIGN", "I ate the cookie for account $b%s$b. It may now have another." },
187 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
188 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
189 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
190 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
191 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
192 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
193 { "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)" },
194 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
195 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
196 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
197 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
198 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
199 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
200 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
201 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
202 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
203 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
204 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
205 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
206 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
207 { "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." },
208 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
209 { "NSMSG_FAKEIDENT_INVALID", "Fake idents must be shorter than %d characters." },
210 { "NSMSG_FAKEMASK_INVALID", "Fake ident@hosts must be shorter than %d characters." },
211 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
212 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
213 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
214 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
215 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
216 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
217 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
218 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
219 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
220 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
221 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
222 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
223 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
224 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
225 { "NSMSG_HANDLEINFO_DEVNULL", " DevNull Class: %s" },
226 { "NSMSG_HANDLEINFO_WEBSITE", " Website: %s" },
227 { "NSMSG_HANDLEINFO_ACCESS", " Access: %i" },
228 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
229 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
230 { "NSMSG_HANDLEINFO_FAKEIDENT", " Fake ident: %s" },
231 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
232 { "NSMSG_HANDLEINFO_FAKEIDENTHOST", " Fake host: %s@%s" },
233 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
234 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
235 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
236 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
237 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
238 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
239 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
240 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
241 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
242 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
243 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
244 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
245 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
246 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
247 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
248 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
249 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
250 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
251 { "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)." },
252 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
253 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
254 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
255 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
256 { "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." },
257 { "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." },
258 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
259 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
260 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
261 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
262 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
263 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
264 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
265 { "NSMSG_PASS_SUCCESS", "Password changed." },
266 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
267 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
268 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
269 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
270 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
271 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
272 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
273 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
274 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
275 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
276 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
277 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
278 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
279 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
280 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
281 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
282 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
283 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
284 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
285 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
286 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
287 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
288 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
289 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
290 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
291 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
292 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
293 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
294 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
295 { "NSMSG_NO_ACCESS", "Access denied." },
296 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
297 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
298 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
299 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
300 { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
301 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
302 { "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." },
303 { "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." },
304 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
305 { "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." },
306 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
307 { "NSMSG_SEARCH_MATCH", "Match: %s" },
308 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
309 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
310 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
311 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
312 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
313 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
314 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
315 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
316 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
317 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
318 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
319 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
320 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
321 { "NSMSG_SET_DEVNULL", "$bDEVNULL: $b%s" },
322 { "NSMSG_SET_AUTOHIDE", "$bAUTOHIDE: $b%s" },
323 { "NSMSG_SET_WEBSITE", "$bWEBSITE: $b%s" },
324 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
325 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
326 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
327 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
328 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
329 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
330 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
331 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
332 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
333 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
334 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
335 { "NSMSG_SET_STAFFLEVEL", "$bSTAFF_LEVEL: $b%d" },
336 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
337 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
338 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
339 { "NSMSG_SET_FAKEIDENT", "$bFAKEIDENT: $b%s" },
340 { "NSMSG_SET_FAKEIDENTHOST", "$bFAKEHOST: $b%s@%s" },
341 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
342 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
343 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
344 { "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." },
345 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
346 { "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." },
347 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
348 { "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." },
349 { "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." },
350 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
351 { "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." },
352 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
353 { "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." },
354 { "CHECKPASS_YES", "Yes." },
355 { "CHECKPASS_NO", "No." },
356 { "CHECKEMAIL_NOT_SET", "No email set." },
357 { "CHECKEMAIL_YES", "Yes." },
358 { "CHECKEMAIL_NO", "No." },
362 enum reclaim_action {
368 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
369 static void nickserv_reclaim_p(void *data);
370 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
372 enum handle_ts_mode {
378 unsigned int disable_nicks : 1;
379 unsigned int valid_handle_regex_set : 1;
380 unsigned int valid_nick_regex_set : 1;
381 unsigned int autogag_enabled : 1;
382 unsigned int email_enabled : 1;
383 unsigned int email_required : 1;
384 unsigned int default_hostmask : 1;
385 unsigned int warn_nick_owned : 1;
386 unsigned int warn_clone_auth : 1;
387 unsigned long nicks_per_handle;
388 unsigned long password_min_length;
389 unsigned long password_min_digits;
390 unsigned long password_min_upper;
391 unsigned long password_min_lower;
392 unsigned long db_backup_frequency;
393 unsigned long handle_expire_frequency;
394 unsigned long autogag_duration;
395 unsigned long email_visible_level;
396 unsigned long cookie_timeout;
397 unsigned long handle_expire_delay;
398 unsigned long nochan_handle_expire_delay;
399 unsigned long modoper_level;
400 unsigned long modstaff_level;
401 unsigned long set_epithet_level;
402 unsigned long set_title_level;
403 unsigned long set_fakehost_level;
404 unsigned long set_fakeident_level;
405 unsigned long handles_per_email;
406 unsigned long email_search_level;
407 const char *network_name;
408 regex_t valid_handle_regex;
409 regex_t valid_nick_regex;
410 dict_t weak_password_dict;
411 struct policer_params *auth_policer_params;
412 enum reclaim_action reclaim_action;
413 enum reclaim_action auto_reclaim_action;
414 enum handle_ts_mode handle_ts_mode;
415 unsigned long auto_reclaim_delay;
416 unsigned char default_maxlogins;
417 unsigned char hard_maxlogins;
418 unsigned long ounregister_inactive;
419 unsigned long ounregister_flags;
420 unsigned int max_authlog_len;
423 struct pendingLOCUser {
424 struct handle_info *handle_info;
426 struct authlogEntry *authlog;
427 struct pendingLOCUser *next;
430 const char *titlehost_suffix = NULL;
431 static struct pendingLOCUser *pendingLOCUsers = NULL;
433 /* We have 2^32 unique account IDs to use. */
434 unsigned long int highest_id = 0;
436 #define WALK_NOTES(HANDLE, PREV, NOTE) \
437 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
438 if (NOTE->expires && NOTE->expires < now) { \
439 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
441 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
445 canonicalize_hostmask(char *mask)
447 char *out = mask, *temp;
448 if ((temp = strchr(mask, '!'))) {
450 while (*temp) *out++ = *temp++;
456 static struct handle_info *
457 register_handle(const char *handle, const char *passwd, unsigned long id)
459 struct handle_info *hi;
461 char id_base64[IDLEN + 1];
464 /* Assign a unique account ID to the account; note that 0 is
465 an invalid account ID. 1 is therefore the first account ID. */
467 id = 1 + highest_id++;
469 /* Note: highest_id is and must always be the highest ID. */
470 if (id > highest_id) {
474 inttobase64(id_base64, id, IDLEN);
476 /* Make sure an account with the same ID doesn't exist. If a
477 duplicate is found, log some details and assign a new one.
478 This should be impossible, but it never hurts to expect it. */
479 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
480 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
485 hi = calloc(1, sizeof(*hi));
486 hi->userlist_style = HI_DEFAULT_STYLE;
487 hi->handle = strdup(handle);
488 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
490 dict_insert(nickserv_handle_dict, hi->handle, hi);
495 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
501 register_nick(const char *nick, struct handle_info *owner)
503 struct nick_info *ni;
504 ni = malloc(sizeof(struct nick_info));
505 safestrncpy(ni->nick, nick, sizeof(ni->nick));
507 ni->next = owner->nicks;
509 dict_insert(nickserv_nick_dict, ni->nick, ni);
513 delete_nick(struct nick_info *ni)
515 struct nick_info *last, *next;
516 struct userNode *user;
517 /* Check to see if we should mark a user as unregistered. */
518 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
519 user->modes &= ~FLAGS_REGNICK;
522 /* Remove ni from the nick_info linked list. */
523 if (ni == ni->owner->nicks) {
524 ni->owner->nicks = ni->next;
526 last = ni->owner->nicks;
532 last->next = next->next;
534 dict_remove(nickserv_nick_dict, ni->nick);
537 static unreg_func_t *unreg_func_list;
538 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
541 reg_unreg_func(unreg_func_t func)
543 if (unreg_func_used == unreg_func_size) {
544 if (unreg_func_size) {
545 unreg_func_size <<= 1;
546 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
549 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
552 unreg_func_list[unreg_func_used++] = func;
556 nickserv_free_cookie(void *data)
558 struct handle_cookie *cookie = data;
559 if (cookie->hi) cookie->hi->cookie = NULL;
560 if (cookie->data) free(cookie->data);
565 free_handle_info(void *vhi)
567 struct handle_info *hi = vhi;
570 inttobase64(id, hi->id, IDLEN);
571 dict_remove(nickserv_id_dict, id);
573 free_string_list(hi->masks);
577 delete_nick(hi->nicks);
585 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
586 nickserv_free_cookie(hi->cookie);
589 struct handle_note *note = hi->notes;
590 hi->notes = note->next;
593 if (hi->email_addr) {
594 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
595 handle_info_list_remove(hil, hi);
597 dict_remove(nickserv_email_dict, hi->email_addr);
599 struct authlogEntry *authlog, *next;
600 for(authlog = hi->authlog; authlog; authlog = next) {
601 next = authlog->next;
602 struct pendingLOCUser *pending, *prev_pending = NULL;
603 for(pending = pendingLOCUsers; pending; pending = pending->next) {
604 if(pending->authlog == authlog) {
606 prev_pending->next = pending->next;
608 pendingLOCUsers = pending->next;
612 prev_pending = pending;
614 free((char *) authlog->hostmask);
615 if(authlog->quit_reason)
616 free((char *) authlog->quit_reason);
622 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
625 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
629 for (n=0; n<unreg_func_used; n++)
630 unreg_func_list[n](notify, hi);
632 set_user_handle_info(hi->users, NULL, 0);
634 if (nickserv_conf.disable_nicks)
635 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
637 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
639 dict_remove(nickserv_handle_dict, hi->handle);
643 get_handle_info(const char *handle)
645 return dict_find(nickserv_handle_dict, handle, 0);
649 get_nick_info(const char *nick)
651 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
655 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
660 for (nn=0; nn<channel->members.used; ++nn) {
661 mn = channel->members.list[nn];
662 if ((mn->user != except) && (mn->user->handle_info == handle))
669 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
670 if (!user->handle_info) {
672 send_message(user, bot, "MSG_AUTHENTICATE");
676 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
678 send_message(user, bot, "NSMSG_NO_ACCESS");
682 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
684 send_message(user, bot, "MSG_OPER_SUSPENDED");
688 if (user->handle_info->opserv_level < min_level) {
690 send_message(user, bot, "NSMSG_NO_ACCESS");
698 staff_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
699 if (!user->handle_info) {
701 send_message(user, bot, "MSG_AUTHENTICATE");
705 if (user->handle_info->staff_level < min_level) {
707 send_message(user, bot, "NSMSG_NO_ACCESS");
715 is_valid_handle(const char *handle)
717 struct userNode *user;
718 /* cant register a juped nick/service nick as handle, to prevent confusion */
719 user = GetUserH(handle);
720 if (user && IsLocal(user))
722 /* check against maximum length */
723 if (strlen(handle) > NICKSERV_HANDLE_LEN)
725 /* for consistency, only allow account names that could be nicks */
726 if (!is_valid_nick(handle))
728 /* disallow account names that look like bad words */
729 if (opserv_bad_channel(handle))
731 /* test either regex or containing all valid chars */
732 if (nickserv_conf.valid_handle_regex_set) {
733 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
736 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
737 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
741 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
746 is_registerable_nick(const char *nick)
748 /* make sure it could be used as an account name */
749 if (!is_valid_handle(nick))
752 if (strlen(nick) > NICKLEN)
754 /* test either regex or as valid handle */
755 if (nickserv_conf.valid_nick_regex_set) {
756 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
759 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
760 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
768 is_valid_email_addr(const char *org_email)
770 char email[strlen(org_email)+1];
771 strcpy(email, org_email);
772 //validate email address
773 //1st check: there need to be one @
774 char *p1 = strchr(email, '@');
775 if(!p1 || strchr(p1+1, '@')) return 0;
777 //2nd check: username (bevore @) must be at least 1 char long and out of part_chars
778 char *part_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._%+-";
780 if(p1 - email == 0) return 0;
781 for(i = 0; i < (p1 - email); i++) {
782 if(!strchr(part_chars, email[i])) return 0;
784 //3rd check: there need to be at least 1 dot in the domain part and all characters out of part_chars
785 part_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-";
794 } else if(!strchr(part_chars, *p1))
801 //4th check: TLD must be <= 5 chars, no special chars
816 visible_email_addr(struct userNode *user, struct handle_info *hi)
818 if (hi->email_addr) {
819 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
820 return hi->email_addr;
830 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
832 struct handle_info *hi;
833 struct userNode *target;
837 if (!(hi = get_handle_info(++name))) {
838 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
843 if (!(target = GetUserH(name))) {
844 send_message(user, service, "MSG_NICK_UNKNOWN", name);
847 if (IsLocal(target)) {
848 if (IsService(target))
849 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
851 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
854 if (!(hi = target->handle_info)) {
855 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
863 oper_outranks(struct userNode *user, struct handle_info *hi) {
864 if (user->handle_info->opserv_level > hi->opserv_level)
866 if (user->handle_info->opserv_level == hi->opserv_level) {
867 if ((user->handle_info->opserv_level == 1000)
868 || (user->handle_info == hi)
869 || ((user->handle_info->opserv_level == 0)
870 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
871 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
875 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
879 static struct handle_info *
880 get_victim_oper(struct userNode *user, const char *target)
882 struct handle_info *hi;
883 if (!(hi = smart_get_handle_info(nickserv, user, target)))
885 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
886 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
889 return oper_outranks(user, hi) ? hi : NULL;
893 valid_user_for(struct userNode *user, struct handle_info *hi)
897 /* If no hostmasks on the account, allow it. */
898 if (!hi->masks->used || IsDummy(user))
900 /* If any hostmask matches, allow it. */
901 for (ii=0; ii<hi->masks->used; ii++)
902 if (user_matches_glob(user, hi->masks->list[ii], 0))
904 /* If they are allowauthed to this account, allow it (removing the aa). */
905 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
906 dict_remove(nickserv_allow_auth_dict, user->nick);
909 /* The user is not allowed to use this account. */
914 is_secure_password(const char *handle, const char *pass, struct userNode *user)
917 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
921 if (len < nickserv_conf.password_min_length) {
923 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
926 if (!irccasecmp(pass, handle)) {
928 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
931 dict_find(nickserv_conf.weak_password_dict, pass, &p);
934 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
937 for (i=0; i<len; i++) {
938 if (isdigit(pass[i]))
940 if (isupper(pass[i]))
942 if (islower(pass[i]))
945 if ((cnt_lower < nickserv_conf.password_min_lower)
946 || (cnt_upper < nickserv_conf.password_min_upper)
947 || (cnt_digits < nickserv_conf.password_min_digits)) {
949 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
955 static auth_func_t *auth_func_list;
956 static unsigned int auth_func_size = 0, auth_func_used = 0;
959 reg_auth_func(auth_func_t func)
961 if (auth_func_used == auth_func_size) {
962 if (auth_func_size) {
963 auth_func_size <<= 1;
964 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
967 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
970 auth_func_list[auth_func_used++] = func;
973 static handle_rename_func_t *rf_list;
974 static unsigned int rf_list_size, rf_list_used;
977 reg_handle_rename_func(handle_rename_func_t func)
979 if (rf_list_used == rf_list_size) {
982 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
985 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
988 rf_list[rf_list_used++] = func;
992 generate_fakehost(struct handle_info *handle)
994 extern const char *hidden_host_suffix;
995 static char buffer[HOSTLEN+1];
997 if (!handle->fakehost) {
998 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
1000 } else if (handle->fakehost[0] == '.') {
1001 /* A leading dot indicates the stored value is actually a title. */
1002 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, titlehost_suffix);
1004 } else if (handle->fakehost[0] == '$') {
1005 /* A leading $ indicates the stored value begins with the user handle. */
1006 snprintf(buffer, sizeof(buffer), "%s%s", handle->handle, handle->fakehost+1);
1009 return handle->fakehost;
1013 generate_fakeident(struct handle_info *handle, struct userNode *user)
1015 static char buffer[USERLEN+1];
1017 if (!handle->fakeident) {
1020 safestrncpy(buffer, user->ident, sizeof(buffer));
1023 return handle->fakeident;
1027 apply_fakehost(struct handle_info *handle, struct userNode *user)
1029 struct userNode *target;
1030 char *fakehost, *fakeident;
1035 fakehost = generate_fakehost(handle);
1038 fakeident = generate_fakeident(handle, user);
1039 assign_fakehost(user, fakehost, fakeident, 0, 1);
1043 for (target = handle->users; target; target = target->next_authed) {
1044 fakeident = generate_fakeident(handle, target);
1045 assign_fakehost(target, fakehost, fakeident, 0, 1);
1050 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
1053 struct handle_info *old_info;
1055 /* This can happen if somebody uses COOKIE while authed, or if
1056 * they re-auth to their current handle (which is silly, but users
1057 * are like that). */
1058 if (user->handle_info == hi)
1061 if (user->handle_info) {
1062 struct userNode *other;
1065 userList_remove(&curr_helpers, user);
1067 /* remove from next_authed linked list */
1068 if (user->handle_info->users == user) {
1069 user->handle_info->users = user->next_authed;
1070 } else if (user->handle_info->users != NULL) {
1071 for (other = user->handle_info->users;
1072 other->next_authed != user;
1073 other = other->next_authed) ;
1074 other->next_authed = user->next_authed;
1076 /* No users authed to the account - can happen if they get
1077 * killed for authing. */
1079 /* if nobody left on old handle, and they're not an oper, remove !god */
1080 if (!user->handle_info->users && !user->handle_info->opserv_level)
1081 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
1082 /* record them as being last seen at this time */
1083 user->handle_info->lastseen = now;
1084 /* and record their hostmask */
1085 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
1087 old_info = user->handle_info;
1088 user->handle_info = hi;
1089 if (hi && !hi->users && !hi->opserv_level)
1090 HANDLE_CLEAR_FLAG(hi, HELPING);
1091 for (n=0; n<auth_func_used; n++) {
1092 auth_func_list[n](user, old_info);
1097 struct nick_info *ni;
1099 HANDLE_CLEAR_FLAG(hi, FROZEN);
1100 if (nickserv_conf.warn_clone_auth) {
1101 struct userNode *other;
1102 for (other = hi->users; other; other = other->next_authed)
1103 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
1105 user->next_authed = hi->users;
1108 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
1109 userList_append(&curr_helpers, user);
1111 if (hi->fakehost || hi->fakeident || old_info)
1112 apply_fakehost(hi, user);
1115 if (!nickserv_conf.disable_nicks) {
1116 struct nick_info *ni2;
1117 for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
1118 if (!irccasecmp(user->nick, ni2->nick)) {
1119 user->modes |= FLAGS_REGNICK;
1124 StampUser(user, hi->handle, hi->registered, hi->id);
1127 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
1128 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
1130 /* We cannot clear the user's account ID, unfortunately. */
1131 user->next_authed = NULL;
1135 static struct handle_info*
1136 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
1138 struct handle_info *hi;
1139 struct nick_info *ni;
1140 char crypted[MD5_CRYPT_LENGTH];
1142 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
1143 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
1147 if (!is_secure_password(handle, passwd, user))
1150 cryptpass(passwd, crypted);
1151 hi = register_handle(handle, crypted, 0);
1152 hi->masks = alloc_string_list(1);
1154 hi->language = lang_C;
1155 hi->registered = now;
1157 hi->flags = HI_DEFAULT_FLAGS;
1158 if (settee && !no_auth)
1159 set_user_handle_info(settee, hi, 1);
1162 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1163 else if (nickserv_conf.disable_nicks)
1164 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1165 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1166 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1168 register_nick(user->nick, hi);
1169 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1171 if (settee && (user != settee))
1172 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1177 nickserv_bake_cookie(struct handle_cookie *cookie)
1179 cookie->hi->cookie = cookie;
1180 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1184 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1186 struct handle_cookie *cookie;
1187 char subject[128], body[4096], *misc;
1188 const char *netname, *fmt;
1192 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1196 cookie = calloc(1, sizeof(*cookie));
1198 cookie->type = type;
1199 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1200 cookie->expires = now + nickserv_conf.cookie_timeout;
1201 inttobase64(cookie->cookie, rand(), 5);
1202 inttobase64(cookie->cookie+5, rand(), 5);
1204 netname = nickserv_conf.network_name;
1207 switch (cookie->type) {
1209 hi->passwd[0] = 0; /* invalidate password */
1210 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1211 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1212 snprintf(subject, sizeof(subject), fmt, netname);
1213 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1214 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1217 case PASSWORD_CHANGE:
1218 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1219 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1220 snprintf(subject, sizeof(subject), fmt, netname);
1221 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1222 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1225 misc = hi->email_addr;
1226 hi->email_addr = cookie->data;
1228 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1229 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1230 snprintf(subject, sizeof(subject), fmt, netname);
1231 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1232 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1233 mail_send(nickserv, hi, subject, body, 1);
1234 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1235 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1237 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1238 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1239 snprintf(subject, sizeof(subject), fmt, netname);
1240 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1241 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1242 mail_send(nickserv, hi, subject, body, 1);
1245 hi->email_addr = misc;
1248 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1249 snprintf(subject, sizeof(subject), fmt, netname);
1250 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1251 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1252 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1255 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1259 mail_send(nickserv, hi, subject, body, first_time);
1260 nickserv_bake_cookie(cookie);
1264 nickserv_eat_cookie(struct handle_cookie *cookie)
1266 cookie->hi->cookie = NULL;
1267 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1268 nickserv_free_cookie(cookie);
1272 nickserv_free_email_addr(void *data)
1274 handle_info_list_clean(data);
1279 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1281 struct handle_info_list *hil;
1282 /* Remove from old handle_info_list ... */
1283 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1284 handle_info_list_remove(hil, hi);
1285 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1286 hi->email_addr = NULL;
1288 /* Add to the new list.. */
1289 if (new_email_addr) {
1290 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1291 hil = calloc(1, sizeof(*hil));
1292 hil->tag = strdup(new_email_addr);
1293 handle_info_list_init(hil);
1294 dict_insert(nickserv_email_dict, hil->tag, hil);
1296 handle_info_list_append(hil, hi);
1297 hi->email_addr = hil->tag;
1301 static NICKSERV_FUNC(cmd_register)
1304 struct handle_info *hi;
1305 const char *email_addr, *password;
1308 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1309 /* Require the first handle registered to belong to someone +o. */
1310 reply("NSMSG_REQUIRE_OPER");
1314 if (user->handle_info) {
1315 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1319 if (IsRegistering(user)) {
1320 reply("NSMSG_ALREADY_REGISTERING");
1324 if (IsStamped(user)) {
1325 /* Unauthenticated users might still have been stamped
1326 previously and could therefore have a hidden host;
1327 do not allow them to register a new account. */
1328 reply("NSMSG_STAMPED_REGISTER");
1332 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1334 if (!is_valid_handle(argv[1])) {
1335 reply("NSMSG_BAD_HANDLE", argv[1]);
1339 if ((argc >= 4) && nickserv_conf.email_enabled) {
1340 struct handle_info_list *hil;
1343 /* Remember email address. */
1344 email_addr = argv[3];
1346 /* Check that the email address looks valid.. */
1347 if (!is_valid_email_addr(email_addr)) {
1348 reply("NSMSG_BAD_EMAIL_ADDR");
1352 /* .. and that we are allowed to send to it. */
1353 if ((str = mail_prohibited_address(email_addr))) {
1354 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1358 /* If we do email verify, make sure we don't spam the address. */
1359 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1361 for (nn=0; nn<hil->used; nn++) {
1362 if (hil->list[nn]->cookie) {
1363 reply("NSMSG_EMAIL_UNACTIVATED");
1367 if (hil->used >= nickserv_conf.handles_per_email) {
1368 reply("NSMSG_EMAIL_OVERUSED");
1381 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1383 /* Add any masks they should get. */
1384 if (nickserv_conf.default_hostmask) {
1385 nickserv_addmask(NULL, hi, strdup("*@*"));
1387 nickserv_addmask(NULL, hi, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1388 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1389 nickserv_addmask(NULL, hi, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1392 /* If they're the first to register, give them level 1000. */
1393 if (dict_size(nickserv_handle_dict) == 1) {
1394 hi->opserv_level = 1000;
1395 hi->staff_level = 1000;
1396 reply("NSMSG_ROOT_HANDLE", argv[1]);
1399 /* Set their email address. */
1401 nickserv_set_email_addr(hi, email_addr);
1403 /* If they need to do email verification, tell them. */
1405 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1407 /* Set registering flag.. */
1408 user->modes |= FLAGS_REGISTERING;
1413 static NICKSERV_FUNC(cmd_oregister)
1416 struct userNode *settee;
1417 struct handle_info *hi;
1418 const char *pass, *email;
1420 NICKSERV_MIN_PARMS(3);
1425 if (!is_valid_handle(argv[1])) {
1426 reply("NSMSG_BAD_HANDLE", argv[1]);
1430 if (argc < 5 || !nickserv_conf.email_enabled) {
1435 if (!is_valid_email_addr(email)) {
1436 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
1439 if ((str = mail_prohibited_address(email))) {
1440 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", email, str);
1445 if (argc < 4 || !strcmp(argv[3], "*")) {
1448 } else if (strchr(argv[3], '@')) {
1449 mask = canonicalize_hostmask(strdup(argv[3]));
1451 settee = GetUserH(argv[4]);
1453 reply("MSG_NICK_UNKNOWN", argv[4]);
1460 } else if ((settee = GetUserH(argv[3]))) {
1461 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1463 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1466 if (settee && settee->handle_info) {
1467 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1471 if (!(hi = nickserv_register(user, settee, argv[1], pass, 0))) {
1476 string_list_append(hi->masks, mask);
1478 nickserv_set_email_addr(hi, email);
1482 static NICKSERV_FUNC(cmd_handleinfo)
1485 unsigned int i, pos=0, herelen;
1486 struct userNode *target, *next_un;
1487 struct handle_info *hi;
1488 const char *nsmsg_none;
1492 if (!(hi = user->handle_info)) {
1493 reply("NSMSG_MUST_AUTH");
1496 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1500 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1501 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1502 feh = hi->registered;
1503 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1506 intervalString(buff, now - hi->lastseen, user->handle_info);
1507 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1509 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1512 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1513 if ((oper_has_access(user, cmd->parent->bot, 200, 1)) || IsNetworkHelper(user))
1514 reply("NSMSG_HANDLEINFO_DEVNULL", (hi->devnull ? hi->devnull : nsmsg_none));
1515 if (user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1516 reply("NSMSG_HANDLEINFO_WEBSITE", (hi->website ? hi->website : nsmsg_none));
1517 if(hi->opserv_level > 0 && user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1518 reply("NSMSG_HANDLEINFO_ACCESS", hi->opserv_level);
1519 if (HANDLE_FLAGGED(hi, FROZEN))
1520 reply("NSMSG_HANDLEINFO_VACATION");
1522 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1523 struct do_not_register *dnr;
1524 if ((dnr = chanserv_is_dnr(NULL, hi)))
1525 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1526 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1528 } else if (hi != user->handle_info)
1532 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1534 if (nickserv_conf.email_enabled)
1535 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1539 switch (hi->cookie->type) {
1540 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1541 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1542 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1543 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1544 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1549 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1550 reply("NSMSG_HANDLEINFO_ID", hi->id);
1552 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1554 reply("NSMSG_HANDLEINFO_NO_NOTES");
1556 struct handle_note *prev, *note;
1558 WALK_NOTES(hi, prev, note) {
1559 char set_time[INTERVALLEN];
1560 intervalString(set_time, now - note->set, user->handle_info);
1561 if (note->expires) {
1562 char exp_time[INTERVALLEN];
1563 intervalString(exp_time, note->expires - now, user->handle_info);
1564 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1566 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1573 unsigned long flen = 1;
1574 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1576 for (i=0, flen=1; handle_flags[i]; i++)
1577 if (hi->flags & 1 << i)
1578 flags[flen++] = handle_flags[i];
1580 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1582 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1585 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1586 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1587 || (hi->opserv_level > 0)) {
1588 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1591 if (hi->fakeident && hi->fakehost)
1592 reply("NSMSG_HANDLEINFO_FAKEIDENTHOST", hi->fakeident, hi->fakehost);
1593 else if (hi->fakeident)
1594 reply("NSMSG_HANDLEINFO_FAKEIDENT", hi->fakeident);
1595 else if (hi->fakehost)
1596 reply("NSMSG_HANDLEINFO_FAKEHOST", hi->fakehost);
1598 if (hi->last_quit_host[0])
1599 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1601 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1603 if (nickserv_conf.disable_nicks) {
1604 /* nicks disabled; don't show anything about registered nicks */
1605 } else if (hi->nicks) {
1606 struct nick_info *ni, *next_ni;
1607 for (ni = hi->nicks; ni; ni = next_ni) {
1608 herelen = strlen(ni->nick);
1609 if (pos + herelen + 1 > ArrayLength(buff)) {
1611 goto print_nicks_buff;
1615 memcpy(buff+pos, ni->nick, herelen);
1616 pos += herelen; buff[pos++] = ' ';
1620 reply("NSMSG_HANDLEINFO_NICKS", buff);
1625 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1628 if (hi->masks->used) {
1629 for (i=0; i < hi->masks->used; i++) {
1630 herelen = strlen(hi->masks->list[i]);
1631 if (pos + herelen + 1 > ArrayLength(buff)) {
1633 goto print_mask_buff;
1635 memcpy(buff+pos, hi->masks->list[i], herelen);
1636 pos += herelen; buff[pos++] = ' ';
1637 if (i+1 == hi->masks->used) {
1640 reply("NSMSG_HANDLEINFO_MASKS", buff);
1645 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1649 struct userData *chan, *next;
1652 for (chan = hi->channels; chan; chan = next) {
1653 next = chan->u_next;
1654 name = chan->channel->channel->name;
1655 herelen = strlen(name);
1656 if (pos + herelen + 7 > ArrayLength(buff)) {
1658 goto print_chans_buff;
1660 if (IsUserSuspended(chan))
1662 pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
1666 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1671 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1674 for (target = hi->users; target; target = next_un) {
1675 herelen = strlen(target->nick);
1676 if (pos + herelen + 1 > ArrayLength(buff)) {
1678 goto print_cnick_buff;
1680 next_un = target->next_authed;
1682 memcpy(buff+pos, target->nick, herelen);
1683 pos += herelen; buff[pos++] = ' ';
1687 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1692 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1695 static NICKSERV_FUNC(cmd_userinfo)
1697 struct userNode *target;
1699 NICKSERV_MIN_PARMS(2);
1700 if (!(target = GetUserH(argv[1]))) {
1701 reply("MSG_NICK_UNKNOWN", argv[1]);
1704 if (target->handle_info)
1705 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1707 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1711 static NICKSERV_FUNC(cmd_nickinfo)
1713 struct nick_info *ni;
1715 NICKSERV_MIN_PARMS(2);
1716 if (!(ni = get_nick_info(argv[1]))) {
1717 reply("MSG_NICK_UNKNOWN", argv[1]);
1720 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1724 static NICKSERV_FUNC(cmd_notes)
1726 struct handle_info *hi;
1727 struct handle_note *prev, *note;
1730 NICKSERV_MIN_PARMS(2);
1731 if (!(hi = get_victim_oper(user, argv[1])))
1734 WALK_NOTES(hi, prev, note) {
1735 char set_time[INTERVALLEN];
1736 intervalString(set_time, now - note->set, user->handle_info);
1737 if (note->expires) {
1738 char exp_time[INTERVALLEN];
1739 intervalString(exp_time, note->expires - now, user->handle_info);
1740 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1742 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1746 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1750 static NICKSERV_FUNC(cmd_rename_handle)
1752 struct handle_info *hi;
1753 char msgbuf[MAXLEN], *old_handle;
1756 NICKSERV_MIN_PARMS(3);
1757 if (!(hi = get_victim_oper(user, argv[1])))
1759 if (!is_valid_handle(argv[2])) {
1760 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1763 if (get_handle_info(argv[2])) {
1764 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1767 if (hi->fakehost && hi->fakehost[0] == '.' &&
1768 (strlen(argv[2]) + strlen(hi->fakehost+1) +
1769 strlen(titlehost_suffix) + 2) > HOSTLEN) {
1770 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED_RENAME");
1774 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1775 hi->handle = strdup(argv[2]);
1776 dict_insert(nickserv_handle_dict, hi->handle, hi);
1777 for (nn=0; nn<rf_list_used; nn++)
1778 rf_list[nn](hi, old_handle);
1779 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1780 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1781 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1783 apply_fakehost(hi, NULL);
1787 static failpw_func_t *failpw_func_list;
1788 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1791 reg_failpw_func(failpw_func_t func)
1793 if (failpw_func_used == failpw_func_size) {
1794 if (failpw_func_size) {
1795 failpw_func_size <<= 1;
1796 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1798 failpw_func_size = 8;
1799 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1802 failpw_func_list[failpw_func_used++] = func;
1805 static struct authlogEntry *authlog_add(struct handle_info *hi, struct userNode *user, const char *mask) {
1806 if(!hi || (!user && !mask)) return NULL;
1808 mask = generate_hostmask(user, GENMASK_USENICK|GENMASK_STRICT_IDENT|GENMASK_NO_HIDING|GENMASK_STRICT_HOST);
1809 struct authlogEntry *authlog, *next, *prev = NULL;
1810 authlog = malloc(sizeof(*authlog));
1811 authlog->login_time = now;
1812 authlog->logout_time = 0;
1813 authlog->hostmask = mask;
1814 authlog->quit_reason = NULL;
1815 authlog->user = user;
1816 authlog->next = hi->authlog;
1817 hi->authlog = authlog;
1819 for(authlog = hi->authlog; authlog; authlog = next) {
1821 next = authlog->next;
1822 if(i > nickserv_conf.max_authlog_len) {
1823 struct pendingLOCUser *pending, *prev_pending = NULL;
1824 for(pending = pendingLOCUsers; pending; pending = pending->next) {
1825 if(pending->authlog == authlog) {
1827 prev_pending->next = pending->next;
1829 pendingLOCUsers = pending->next;
1833 prev_pending = pending;
1835 free((char *) authlog->hostmask);
1836 if(authlog->quit_reason)
1837 free((char *) authlog->quit_reason);
1839 prev->next = authlog->next;
1841 hi->authlog = authlog->next;
1849 static NICKSERV_FUNC(cmd_auth)
1851 int pw_arg, used, maxlogins;
1852 struct handle_info *hi;
1854 struct userNode *other;
1856 if (user->handle_info) {
1857 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1860 if (IsStamped(user)) {
1861 /* Unauthenticated users might still have been stamped
1862 previously and could therefore have a hidden host;
1863 do not allow them to authenticate. */
1864 reply("NSMSG_STAMPED_AUTH");
1868 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1870 } else if (argc == 2) {
1871 if (nickserv_conf.disable_nicks) {
1872 if (!(hi = get_handle_info(user->nick))) {
1873 reply("NSMSG_HANDLE_NOT_FOUND");
1877 /* try to look up their handle from their nick */
1878 struct nick_info *ni;
1879 ni = get_nick_info(user->nick);
1881 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1888 reply("MSG_MISSING_PARAMS", argv[0]);
1889 svccmd_send_help(user, nickserv, cmd);
1893 reply("NSMSG_HANDLE_NOT_FOUND");
1896 /* Responses from here on look up the language used by the handle they asked about. */
1897 passwd = argv[pw_arg];
1898 if (!valid_user_for(user, hi)) {
1899 if (hi->email_addr && nickserv_conf.email_enabled)
1900 send_message_type(4, user, cmd->parent->bot,
1901 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1904 send_message_type(4, user, cmd->parent->bot,
1905 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1907 argv[pw_arg] = "BADMASK";
1910 if (!checkpass(passwd, hi->passwd)) {
1912 send_message_type(4, user, cmd->parent->bot,
1913 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1914 argv[pw_arg] = "BADPASS";
1915 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1916 if (nickserv_conf.autogag_enabled) {
1917 if (!user->auth_policer.params) {
1918 user->auth_policer.last_req = now;
1919 user->auth_policer.params = nickserv_conf.auth_policer_params;
1921 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1923 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1924 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1925 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1927 argv[pw_arg] = "GAGGED";
1932 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1933 send_message_type(4, user, cmd->parent->bot,
1934 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1935 argv[pw_arg] = "SUSPENDED";
1938 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1939 for (used = 0, other = hi->users; other; other = other->next_authed) {
1940 if (++used >= maxlogins) {
1941 send_message_type(4, user, cmd->parent->bot,
1942 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1944 argv[pw_arg] = "MAXLOGINS";
1948 if (HANDLE_FLAGGED(hi, AUTOHIDE)) {
1949 //ok we have a fakehost set... but we need to set mode +x
1950 irc_svsmode(nickserv,user,"+x");
1953 set_user_handle_info(user, hi, 1);
1954 if (nickserv_conf.email_required && !hi->email_addr)
1955 reply("NSMSG_PLEASE_SET_EMAIL");
1956 if (!is_secure_password(hi->handle, passwd, NULL))
1957 reply("NSMSG_WEAK_PASSWORD");
1958 if (hi->passwd[0] != '$')
1959 cryptpass(passwd, hi->passwd);
1960 if (!hi->masks->used) {
1962 nickserv_addmask(NULL, hi, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1963 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1964 nickserv_addmask(NULL, hi, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1966 authlog_add(hi, user, NULL);
1967 argv[pw_arg] = "****";
1968 reply("NSMSG_AUTH_SUCCESS");
1972 struct handle_info *checklogin(const char *user, const char *pass, const char *numeric, const char *hostmask, const char *ipmask)
1974 struct handle_info *hi;
1975 unsigned int match = 0, ii = 0;
1976 hi = dict_find(nickserv_handle_dict, user, NULL);
1979 /* If no hostmasks on the account, allow it. */
1980 if (hi->masks->used) {
1981 /* If any hostmask matches, allow it. */
1982 for (ii=0; ii<hi->masks->used; ii++)
1983 if (match_ircglob(hostmask, hi->masks->list[ii]) || match_ircglob(ipmask, hi->masks->list[ii])) {
1990 if(!checkpass(pass, hi->passwd))
1992 if (HANDLE_FLAGGED(hi, SUSPENDED))
1994 char *ptr = malloc(strlen(hostmask)+1);
1995 strcpy(ptr, hostmask);
1996 struct authlogEntry *authlog = authlog_add(hi, NULL, ptr);
1997 struct pendingLOCUser *pending;
1998 if(authlog && (pending = malloc(sizeof(*pending)))) {
1999 pending->handle_info = hi;
2000 pending->time = now;
2001 pending->authlog = authlog;
2002 pending->next = pendingLOCUsers;
2003 pendingLOCUsers = pending;
2008 char *getfakehost(const char *user)
2010 struct handle_info *hi;
2011 hi = dict_find(nickserv_handle_dict, user, NULL);
2014 return generate_fakehost(hi);
2017 static allowauth_func_t *allowauth_func_list;
2018 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
2021 reg_allowauth_func(allowauth_func_t func)
2023 if (allowauth_func_used == allowauth_func_size) {
2024 if (allowauth_func_size) {
2025 allowauth_func_size <<= 1;
2026 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
2028 allowauth_func_size = 8;
2029 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
2032 allowauth_func_list[allowauth_func_used++] = func;
2035 static int cmd_authlog_func(struct userNode *user, struct svccmd *cmd, struct handle_info *hi);
2037 static MODCMD_FUNC(cmd_authlog)
2039 return cmd_authlog_func(user, cmd, user->handle_info);
2042 static MODCMD_FUNC(cmd_oauthlog) {
2043 struct handle_info *hi;
2045 NICKSERV_MIN_PARMS(2);
2047 if (!(hi = get_victim_oper(user, argv[1])))
2050 return cmd_authlog_func(user, cmd, hi);
2053 static int cmd_authlog_func(struct userNode *user, struct svccmd *cmd, struct handle_info *hi) {
2054 struct helpfile_table tbl;
2055 struct authlogEntry *authlog;
2058 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
2065 tbl.flags = TABLE_NO_FREE;
2066 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
2067 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
2068 tbl.contents[0][0] = "Hostmask";
2069 tbl.contents[0][1] = "Login";
2070 tbl.contents[0][2] = "Logout";
2071 tbl.contents[0][3] = "Quit Reason";
2074 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
2076 free(tbl.contents[0]);
2082 char intervalBuf[INTERVALLEN];
2084 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
2085 tbl.contents[++i] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
2086 tbl.contents[i][0] = authlog->hostmask;
2087 str = intervalString(intervalBuf, now - authlog->login_time, hi);
2088 ptr = malloc(strlen(str)+1);
2090 tbl.contents[i][1] = ptr;
2091 if(authlog->logout_time)
2092 str = intervalString(intervalBuf, now - authlog->logout_time, hi);
2093 else if(!authlog->user)
2096 sprintf(intervalBuf, "Never (%s)", authlog->user->nick);
2099 ptr = malloc(strlen(str)+1);
2101 tbl.contents[i][2] = ptr;
2102 tbl.contents[i][3] = (authlog->quit_reason ? authlog->quit_reason : "-");
2105 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
2106 for(i = 1; i < tbl.length; ++i)
2108 free((char *) tbl.contents[i][1]);
2109 free((char *) tbl.contents[i][2]);
2110 free(tbl.contents[i]);
2112 free(tbl.contents[0]);
2118 static NICKSERV_FUNC(cmd_allowauth)
2120 struct userNode *target;
2121 struct handle_info *hi;
2124 NICKSERV_MIN_PARMS(2);
2125 if (!(target = GetUserH(argv[1]))) {
2126 reply("MSG_NICK_UNKNOWN", argv[1]);
2129 if (target->handle_info) {
2130 reply("NSMSG_USER_PREV_AUTH", target->nick);
2133 if (IsStamped(target)) {
2134 /* Unauthenticated users might still have been stamped
2135 previously and could therefore have a hidden host;
2136 do not allow them to authenticate to an account. */
2137 reply("NSMSG_USER_PREV_STAMP", target->nick);
2142 else if (!(hi = get_handle_info(argv[2]))) {
2143 reply("MSG_HANDLE_UNKNOWN", argv[2]);
2147 if (hi->opserv_level > user->handle_info->opserv_level) {
2148 reply("MSG_USER_OUTRANKED", hi->handle);
2151 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
2152 || (hi->opserv_level > 0))
2153 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
2154 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
2157 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
2158 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
2159 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
2160 if (nickserv_conf.email_enabled)
2161 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
2163 if (dict_remove(nickserv_allow_auth_dict, target->nick))
2164 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
2166 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
2168 for (n=0; n<allowauth_func_used; n++)
2169 allowauth_func_list[n](user, target, hi);
2173 static NICKSERV_FUNC(cmd_authcookie)
2175 struct handle_info *hi;
2177 NICKSERV_MIN_PARMS(2);
2178 if (user->handle_info) {
2179 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2182 if (IsStamped(user)) {
2183 /* Unauthenticated users might still have been stamped
2184 previously and could therefore have a hidden host;
2185 do not allow them to authenticate to an account. */
2186 reply("NSMSG_STAMPED_AUTHCOOKIE");
2189 if (!(hi = get_handle_info(argv[1]))) {
2190 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2193 if (!hi->email_addr) {
2194 reply("MSG_SET_EMAIL_ADDR");
2197 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
2201 static NICKSERV_FUNC(cmd_delcookie)
2203 struct handle_info *hi;
2205 hi = user->handle_info;
2207 reply("NSMSG_NO_COOKIE");
2210 switch (hi->cookie->type) {
2213 reply("NSMSG_MUST_TIME_OUT");
2216 nickserv_eat_cookie(hi->cookie);
2217 reply("NSMSG_ATE_COOKIE");
2223 static NICKSERV_FUNC(cmd_odelcookie)
2225 struct handle_info *hi;
2227 NICKSERV_MIN_PARMS(2);
2229 if (!(hi = get_victim_oper(user, argv[1])))
2233 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
2237 nickserv_eat_cookie(hi->cookie);
2238 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
2243 static NICKSERV_FUNC(cmd_resetpass)
2245 struct handle_info *hi;
2246 char crypted[MD5_CRYPT_LENGTH];
2248 NICKSERV_MIN_PARMS(3);
2249 if (user->handle_info) {
2250 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2253 if (IsStamped(user)) {
2254 /* Unauthenticated users might still have been stamped
2255 previously and could therefore have a hidden host;
2256 do not allow them to activate an account. */
2257 reply("NSMSG_STAMPED_RESETPASS");
2260 if (!(hi = get_handle_info(argv[1]))) {
2261 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2264 if (!hi->email_addr) {
2265 reply("MSG_SET_EMAIL_ADDR");
2268 cryptpass(argv[2], crypted);
2270 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
2274 static NICKSERV_FUNC(cmd_cookie)
2276 struct handle_info *hi;
2279 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
2282 NICKSERV_MIN_PARMS(3);
2283 if (!(hi = get_handle_info(argv[1]))) {
2284 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2290 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2291 reply("NSMSG_HANDLE_SUSPENDED");
2296 reply("NSMSG_NO_COOKIE");
2300 /* Check validity of operation before comparing cookie to
2301 * prohibit guessing by authed users. */
2302 if (user->handle_info
2303 && (hi->cookie->type != EMAIL_CHANGE)
2304 && (hi->cookie->type != PASSWORD_CHANGE)) {
2305 reply("NSMSG_CANNOT_COOKIE");
2309 if (strcmp(cookie, hi->cookie->cookie)) {
2310 reply("NSMSG_BAD_COOKIE");
2314 switch (hi->cookie->type) {
2316 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2317 set_user_handle_info(user, hi, 1);
2318 reply("NSMSG_HANDLE_ACTIVATED");
2320 case PASSWORD_CHANGE:
2321 set_user_handle_info(user, hi, 1);
2322 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2323 reply("NSMSG_PASSWORD_CHANGED");
2326 nickserv_set_email_addr(hi, hi->cookie->data);
2327 reply("NSMSG_EMAIL_CHANGED");
2330 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2331 set_user_handle_info(user, hi, 1);
2332 nickserv_addmask(user, hi, mask);
2333 reply("NSMSG_AUTH_SUCCESS");
2338 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2339 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2343 nickserv_eat_cookie(hi->cookie);
2348 static NICKSERV_FUNC(cmd_oregnick) {
2350 struct handle_info *target;
2351 struct nick_info *ni;
2353 NICKSERV_MIN_PARMS(3);
2354 if (!(target = modcmd_get_handle_info(user, argv[1])))
2357 if (!is_registerable_nick(nick)) {
2358 reply("NSMSG_BAD_NICK", nick);
2361 ni = dict_find(nickserv_nick_dict, nick, NULL);
2363 reply("NSMSG_NICK_EXISTS", nick);
2366 register_nick(nick, target);
2367 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2371 static NICKSERV_FUNC(cmd_regnick) {
2373 struct nick_info *ni;
2375 if (!is_registerable_nick(user->nick)) {
2376 reply("NSMSG_BAD_NICK", user->nick);
2379 /* count their nicks, see if it's too many */
2380 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2381 if (n >= nickserv_conf.nicks_per_handle) {
2382 reply("NSMSG_TOO_MANY_NICKS");
2385 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2387 reply("NSMSG_NICK_EXISTS", user->nick);
2390 register_nick(user->nick, user->handle_info);
2391 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2395 static NICKSERV_FUNC(cmd_pass)
2397 struct handle_info *hi;
2398 const char *old_pass, *new_pass;
2400 NICKSERV_MIN_PARMS(3);
2401 hi = user->handle_info;
2405 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2406 if (!checkpass(old_pass, hi->passwd)) {
2407 argv[1] = "BADPASS";
2408 reply("NSMSG_PASSWORD_INVALID");
2411 cryptpass(new_pass, hi->passwd);
2413 reply("NSMSG_PASS_SUCCESS");
2418 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2421 char *new_mask = canonicalize_hostmask(strdup(mask));
2422 for (i=0; i<hi->masks->used; i++) {
2423 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2425 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2430 string_list_append(hi->masks, new_mask);
2432 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2436 static NICKSERV_FUNC(cmd_addmask)
2439 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2440 int res = nickserv_addmask(user, user->handle_info, mask);
2444 if (!is_gline(argv[1])) {
2445 reply("NSMSG_MASK_INVALID", argv[1]);
2448 return nickserv_addmask(user, user->handle_info, argv[1]);
2452 static NICKSERV_FUNC(cmd_oaddmask)
2454 struct handle_info *hi;
2456 NICKSERV_MIN_PARMS(3);
2457 if (!(hi = get_victim_oper(user, argv[1])))
2459 return nickserv_addmask(user, hi, argv[2]);
2463 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2466 for (i=0; i<hi->masks->used; i++) {
2467 if (!strcmp(del_mask, hi->masks->list[i])) {
2468 char *old_mask = hi->masks->list[i];
2469 if (hi->masks->used == 1 && !force) {
2470 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2473 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2474 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2479 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2483 static NICKSERV_FUNC(cmd_delmask)
2485 NICKSERV_MIN_PARMS(2);
2486 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2489 static NICKSERV_FUNC(cmd_odelmask)
2491 struct handle_info *hi;
2492 NICKSERV_MIN_PARMS(3);
2493 if (!(hi = get_victim_oper(user, argv[1])))
2495 return nickserv_delmask(user, hi, argv[2], 1);
2499 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2500 unsigned int nn, add = 1, pos;
2501 unsigned long added, removed, flag;
2503 for (added=removed=nn=0; str[nn]; nn++) {
2505 case '+': add = 1; break;
2506 case '-': add = 0; break;
2508 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2509 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2512 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2513 /* cheesy avoidance of looking up the flag name.. */
2514 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2517 flag = 1 << (pos - 1);
2519 added |= flag, removed &= ~flag;
2521 removed |= flag, added &= ~flag;
2526 *premoved = removed;
2531 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2533 unsigned long before, after, added, removed;
2534 struct userNode *uNode;
2536 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2537 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2539 hi->flags = (hi->flags | added) & ~removed;
2540 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2542 /* Strip helping flag if they're only a support helper and not
2543 * currently in #support. */
2544 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2545 struct channelList *schannels;
2547 schannels = chanserv_support_channels();
2548 for (ii = 0; ii < schannels->used; ++ii)
2549 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2551 if (ii == schannels->used)
2552 HANDLE_CLEAR_FLAG(hi, HELPING);
2555 if (after && !before) {
2556 /* Add user to current helper list. */
2557 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2558 userList_append(&curr_helpers, uNode);
2559 } else if (!after && before) {
2560 /* Remove user from current helper list. */
2561 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2562 userList_remove(&curr_helpers, uNode);
2569 set_list(struct userNode *user, struct handle_info *hi, int override)
2573 char *set_display[] = {
2574 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2575 "EMAIL", "MAXLOGINS", "LANGUAGE", "DEVNULL"
2578 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2580 /* Do this so options are presented in a consistent order. */
2581 for (i = 0; i < ArrayLength(set_display); ++i)
2582 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2583 opt(user, hi, override, 0, NULL);
2586 static NICKSERV_FUNC(cmd_set)
2588 struct handle_info *hi;
2591 hi = user->handle_info;
2593 set_list(user, hi, 0);
2596 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2597 reply("NSMSG_INVALID_OPTION", argv[1]);
2600 return opt(user, hi, 0, argc-1, argv+1);
2603 static NICKSERV_FUNC(cmd_oset)
2605 struct handle_info *hi;
2606 struct svccmd *subcmd;
2608 char cmdname[MAXLEN];
2610 NICKSERV_MIN_PARMS(2);
2612 if (!(hi = get_victim_oper(user, argv[1])))
2616 set_list(user, hi, 0);
2620 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2621 reply("NSMSG_INVALID_OPTION", argv[2]);
2625 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2626 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2627 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2630 return opt(user, hi, 1, argc-2, argv+2);
2633 static OPTION_FUNC(opt_info)
2637 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2639 hi->infoline = NULL;
2641 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2645 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2646 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2650 static OPTION_FUNC(opt_devnull)
2652 const char *devnull;
2656 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2659 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2663 devnull = unsplit_string(argv+1, argc-1, NULL);
2664 if(devnull_check(devnull) == 1) {
2667 hi->devnull = strdup(devnull);
2672 devnull = hi->devnull ? hi->devnull : user_find_message(user, "MSG_NONE");
2673 send_message(user, nickserv, "NSMSG_SET_DEVNULL", devnull);
2677 void nickserv_devnull_delete(char *name) {
2679 struct handle_info *hi;
2681 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2683 if (hi->devnull && !irccasecmp(name, hi->devnull)) {
2690 void nickserv_devnull_rename(char *oldname, char *newname) {
2692 struct handle_info *hi;
2694 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2696 if (hi->devnull && !irccasecmp(oldname, hi->devnull)) {
2697 hi->devnull = strdup(newname);
2702 static OPTION_FUNC(opt_website)
2704 const char *website;
2707 if (!HANDLE_FLAGGED(user->handle_info, BOT)) {
2708 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2711 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2715 website = unsplit_string(argv+1, argc-1, NULL);
2716 hi->website = strdup(website);
2719 if (HANDLE_FLAGGED(user->handle_info, BOT)) {
2720 website = hi->website ? hi->website : user_find_message(user, "MSG_NONE");
2721 send_message(user, nickserv, "NSMSG_SET_WEBSITE", website);
2726 static OPTION_FUNC(opt_width)
2729 hi->screen_width = strtoul(argv[1], NULL, 0);
2731 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2732 hi->screen_width = MIN_LINE_SIZE;
2733 else if (hi->screen_width > MAX_LINE_SIZE)
2734 hi->screen_width = MAX_LINE_SIZE;
2736 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2740 static OPTION_FUNC(opt_tablewidth)
2743 hi->table_width = strtoul(argv[1], NULL, 0);
2745 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2746 hi->table_width = MIN_LINE_SIZE;
2747 else if (hi->screen_width > MAX_LINE_SIZE)
2748 hi->table_width = MAX_LINE_SIZE;
2750 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2754 static OPTION_FUNC(opt_color)
2757 if (enabled_string(argv[1]))
2758 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2759 else if (disabled_string(argv[1]))
2760 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2762 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2767 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2771 static OPTION_FUNC(opt_privmsg)
2774 if (enabled_string(argv[1]))
2775 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2776 else if (disabled_string(argv[1]))
2777 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2779 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2784 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2788 static OPTION_FUNC(opt_autohide)
2791 if (enabled_string(argv[1]))
2792 HANDLE_SET_FLAG(hi, AUTOHIDE);
2793 else if (disabled_string(argv[1]))
2794 HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
2796 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2801 send_message(user, nickserv, "NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
2805 static OPTION_FUNC(opt_style)
2810 if (!irccasecmp(argv[1], "Zoot"))
2811 hi->userlist_style = HI_STYLE_ZOOT;
2812 else if (!irccasecmp(argv[1], "def"))
2813 hi->userlist_style = HI_STYLE_DEF;
2816 switch (hi->userlist_style) {
2825 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2829 static OPTION_FUNC(opt_password)
2832 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2837 cryptpass(argv[1], hi->passwd);
2839 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2845 static OPTION_FUNC(opt_flags)
2848 unsigned int ii, flen;
2851 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2856 nickserv_apply_flags(user, hi, argv[1]);
2858 for (ii = flen = 0; handle_flags[ii]; ii++)
2859 if (hi->flags & (1 << ii))
2860 flags[flen++] = handle_flags[ii];
2863 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2865 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2869 static OPTION_FUNC(opt_email)
2873 if (!is_valid_email_addr(argv[1])) {
2874 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2877 if ((str = mail_prohibited_address(argv[1]))) {
2878 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2881 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2882 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2884 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2886 nickserv_set_email_addr(hi, argv[1]);
2888 nickserv_eat_cookie(hi->cookie);
2889 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2892 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2896 static OPTION_FUNC(opt_maxlogins)
2898 unsigned char maxlogins;
2900 maxlogins = strtoul(argv[1], NULL, 0);
2901 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2902 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2905 hi->maxlogins = maxlogins;
2907 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2908 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2912 static OPTION_FUNC(opt_language)
2914 struct language *lang;
2916 lang = language_find(argv[1]);
2917 if (irccasecmp(lang->name, argv[1]))
2918 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2919 hi->language = lang;
2921 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2925 static OPTION_FUNC(opt_karma)
2928 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2933 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2934 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2935 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2936 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2938 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2942 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2947 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2948 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2950 if ((user->handle_info->opserv_level < target->opserv_level)
2951 || ((user->handle_info->opserv_level == target->opserv_level)
2952 && (user->handle_info->opserv_level < 1000))) {
2953 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2956 if ((user->handle_info->opserv_level < new_level)
2957 || ((user->handle_info->opserv_level == new_level)
2958 && (user->handle_info->opserv_level < 1000))) {
2959 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2962 if (user->handle_info == target) {
2963 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2966 if (target->opserv_level == new_level)
2968 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2969 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2970 target->opserv_level = new_level;
2975 oper_try_set_staff_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2976 if (!oper_has_access(user, bot, nickserv_conf.modstaff_level, 0))
2978 if ((user->handle_info->opserv_level < target->opserv_level)
2979 || ((user->handle_info->opserv_level == target->opserv_level)
2980 && (user->handle_info->opserv_level < 1000))) {
2981 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2984 if (target->staff_level == new_level)
2986 log_module(NS_LOG, LOG_INFO, "Account %s setting staff level for account %s to %d (from %d).",
2987 user->handle_info->handle, target->handle, new_level, target->staff_level);
2988 target->staff_level = new_level;
2992 static OPTION_FUNC(opt_level)
2997 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3001 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
3002 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
3006 static OPTION_FUNC(opt_staff_level)
3011 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3015 res = (argc > 1) ? oper_try_set_staff_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
3016 send_message(user, nickserv, "NSMSG_SET_STAFFLEVEL", hi->staff_level);
3020 static OPTION_FUNC(opt_epithet)
3023 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3027 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
3028 char *epithet = unsplit_string(argv+1, argc-1, NULL);
3031 if ((epithet[0] == '*') && !epithet[1])
3034 hi->epithet = strdup(epithet);
3038 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
3040 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
3044 static OPTION_FUNC(opt_title)
3049 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3053 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
3055 if (strchr(title, '.')) {
3056 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
3059 if ((strlen(user->handle_info->handle) + strlen(title) +
3060 strlen(titlehost_suffix) + 2) > HOSTLEN) {
3061 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
3066 if (!strcmp(title, "*")) {
3067 hi->fakehost = NULL;
3069 hi->fakehost = malloc(strlen(title)+2);
3070 hi->fakehost[0] = '.';
3071 strcpy(hi->fakehost+1, title);
3073 apply_fakehost(hi, NULL);
3074 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
3075 title = hi->fakehost + 1;
3079 title = user_find_message(user, "MSG_NONE");
3080 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
3084 static OPTION_FUNC(opt_fakehost)
3086 char mask[USERLEN + HOSTLEN + 2];
3090 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3094 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
3095 if(strlen(argv[1]) >= sizeof(mask)) {
3096 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
3100 safestrncpy(mask, argv[1], sizeof(mask));
3102 if ((host = strrchr(mask, '@')) && host != mask) {
3103 /* If ident@host was used and the user doesn't have access to set idents, do not change anything. */
3104 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3116 if (ident && strlen(ident) > USERLEN) {
3117 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3121 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
3122 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
3126 if (host && host[0]) {
3128 if (!strcmp(host, "*"))
3129 hi->fakehost = NULL;
3131 hi->fakehost = strdup(host);
3132 host = hi->fakehost;
3135 host = generate_fakehost(hi);
3138 free(hi->fakeident);
3139 if (!strcmp(ident, "*"))
3140 hi->fakeident = NULL;
3142 hi->fakeident = strdup(ident);
3143 ident = hi->fakeident;
3146 ident = generate_fakeident(hi, NULL);
3148 apply_fakehost(hi, NULL);
3150 host = generate_fakehost(hi);
3151 ident = generate_fakeident(hi, NULL);
3154 host = (char *) user_find_message(user, "MSG_NONE");
3156 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
3158 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
3162 static OPTION_FUNC(opt_fakeident)
3167 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3171 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3173 if (strlen(ident) > USERLEN) {
3174 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3177 free(hi->fakeident);
3178 if (!strcmp(ident, "*"))
3179 hi->fakeident = NULL;
3181 hi->fakeident = strdup(ident);
3182 ident = hi->fakeident;
3183 apply_fakehost(hi, NULL);
3185 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
3187 ident = user_find_message(user, "MSG_NONE");
3188 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
3192 static NICKSERV_FUNC(cmd_reclaim)
3194 struct nick_info *ni;
3195 struct userNode *victim;
3197 NICKSERV_MIN_PARMS(2);
3198 ni = dict_find(nickserv_nick_dict, argv[1], 0);
3200 reply("NSMSG_UNKNOWN_NICK", argv[1]);
3203 if (ni->owner != user->handle_info) {
3204 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
3207 victim = GetUserH(ni->nick);
3209 reply("MSG_NICK_UNKNOWN", ni->nick);
3212 if (victim == user) {
3213 reply("NSMSG_NICK_USER_YOU");
3216 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
3217 switch (nickserv_conf.reclaim_action) {
3218 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
3219 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
3220 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
3221 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
3226 static NICKSERV_FUNC(cmd_unregnick)
3229 struct handle_info *hi;
3230 struct nick_info *ni;
3232 hi = user->handle_info;
3233 nick = (argc < 2) ? user->nick : (const char*)argv[1];
3234 ni = dict_find(nickserv_nick_dict, nick, NULL);
3236 reply("NSMSG_UNKNOWN_NICK", nick);
3239 if (hi != ni->owner) {
3240 reply("NSMSG_NOT_YOUR_NICK", nick);
3243 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3248 static NICKSERV_FUNC(cmd_ounregnick)
3250 struct nick_info *ni;
3252 NICKSERV_MIN_PARMS(2);
3253 if (!(ni = get_nick_info(argv[1]))) {
3254 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
3257 if (!oper_outranks(user, ni->owner))
3259 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3264 static NICKSERV_FUNC(cmd_unregister)
3266 struct handle_info *hi;
3269 NICKSERV_MIN_PARMS(2);
3270 hi = user->handle_info;
3273 if (checkpass(passwd, hi->passwd)) {
3274 nickserv_unregister_handle(hi, user);
3277 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3278 reply("NSMSG_PASSWORD_INVALID");
3283 static NICKSERV_FUNC(cmd_ounregister)
3285 struct handle_info *hi;
3286 char reason[MAXLEN];
3289 NICKSERV_MIN_PARMS(2);
3290 if (!(hi = get_victim_oper(user, argv[1])))
3293 if (HANDLE_FLAGGED(hi, NODELETE)) {
3294 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
3298 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
3300 ((hi->flags & nickserv_conf.ounregister_flags)
3302 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
3303 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
3307 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
3308 global_message(MESSAGE_RECIPIENT_STAFF, reason);
3309 nickserv_unregister_handle(hi, user);
3313 static NICKSERV_FUNC(cmd_status)
3315 if (nickserv_conf.disable_nicks) {
3316 reply("NSMSG_GLOBAL_STATS_NONICK",
3317 dict_size(nickserv_handle_dict));
3319 if (user->handle_info) {
3321 struct nick_info *ni;
3322 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
3323 reply("NSMSG_HANDLE_STATS", cnt);
3325 reply("NSMSG_HANDLE_NONE");
3327 reply("NSMSG_GLOBAL_STATS",
3328 dict_size(nickserv_handle_dict),
3329 dict_size(nickserv_nick_dict));
3334 static NICKSERV_FUNC(cmd_ghost)
3336 struct userNode *target;
3337 char reason[MAXLEN];
3339 NICKSERV_MIN_PARMS(2);
3340 if (!(target = GetUserH(argv[1]))) {
3341 reply("MSG_NICK_UNKNOWN", argv[1]);
3344 if (target == user) {
3345 reply("NSMSG_CANNOT_GHOST_SELF");
3348 if (!target->handle_info || (target->handle_info != user->handle_info)) {
3349 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
3352 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
3353 DelUser(target, nickserv, 1, reason);
3354 reply("NSMSG_GHOST_KILLED", argv[1]);
3358 static NICKSERV_FUNC(cmd_vacation)
3360 HANDLE_SET_FLAG(user->handle_info, FROZEN);
3361 reply("NSMSG_ON_VACATION");
3365 static NICKSERV_FUNC(cmd_addnote)
3367 struct handle_info *hi;
3368 unsigned long duration;
3371 struct handle_note *prev;
3372 struct handle_note *note;
3374 /* Parse parameters and figure out values for note's fields. */
3375 NICKSERV_MIN_PARMS(4);
3376 hi = get_victim_oper(user, argv[1]);
3379 if(!strcmp(argv[2], "0"))
3381 else if(!(duration = ParseInterval(argv[2])))
3383 reply("MSG_INVALID_DURATION", argv[2]);
3386 if (duration > 2*365*86400) {
3387 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
3390 unsplit_string(argv + 3, argc - 3, text);
3391 WALK_NOTES(hi, prev, note) {}
3392 id = prev ? (prev->id + 1) : 1;
3394 /* Create the new note structure. */
3395 note = calloc(1, sizeof(*note) + strlen(text));
3397 note->expires = duration ? (now + duration) : 0;
3400 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
3401 strcpy(note->note, text);
3406 reply("NSMSG_NOTE_ADDED", id, hi->handle);
3410 static NICKSERV_FUNC(cmd_delnote)
3412 struct handle_info *hi;
3413 struct handle_note *prev;
3414 struct handle_note *note;
3417 NICKSERV_MIN_PARMS(3);
3418 hi = get_victim_oper(user, argv[1]);
3421 id = strtoul(argv[2], NULL, 10);
3422 WALK_NOTES(hi, prev, note) {
3423 if (id == note->id) {
3425 prev->next = note->next;
3427 hi->notes = note->next;
3429 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3433 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3438 nickserv_saxdb_write(struct saxdb_context *ctx) {
3440 struct handle_info *hi;
3443 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3445 assert(hi->id != 0);
3446 saxdb_start_record(ctx, iter_key(it), 0);
3448 struct handle_cookie *cookie = hi->cookie;
3451 switch (cookie->type) {
3452 case ACTIVATION: type = KEY_ACTIVATION; break;
3453 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3454 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3455 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3456 default: type = NULL; break;
3459 saxdb_start_record(ctx, KEY_COOKIE, 0);
3460 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3461 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3463 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3464 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3465 saxdb_end_record(ctx);
3469 struct handle_note *prev, *note;
3470 saxdb_start_record(ctx, KEY_NOTES, 0);
3471 WALK_NOTES(hi, prev, note) {
3472 snprintf(flags, sizeof(flags), "%d", note->id);
3473 saxdb_start_record(ctx, flags, 0);
3475 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3476 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3477 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3478 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3479 saxdb_end_record(ctx);
3481 saxdb_end_record(ctx);
3484 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3486 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3488 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3490 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3494 for (ii=flen=0; handle_flags[ii]; ++ii)
3495 if (hi->flags & (1 << ii))
3496 flags[flen++] = handle_flags[ii];
3498 saxdb_write_string(ctx, KEY_FLAGS, flags);
3500 saxdb_write_int(ctx, KEY_ID, hi->id);
3502 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3504 saxdb_write_string(ctx, KEY_DEVNULL, hi->devnull);
3506 saxdb_write_string(ctx, KEY_WEBSITE, hi->website);
3507 if (hi->last_quit_host[0])
3508 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3509 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3511 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3512 if (hi->masks->used)
3513 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3515 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3517 struct string_list *slist;
3518 struct nick_info *ni;
3520 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3521 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3522 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3526 if (hi->opserv_level)
3527 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3528 if (hi->staff_level)
3529 saxdb_write_int(ctx, KEY_STAFF_LEVEL, hi->staff_level);
3530 if (hi->language != lang_C)
3531 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3532 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3533 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3534 if (hi->screen_width)
3535 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3536 if (hi->table_width)
3537 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3538 flags[0] = hi->userlist_style;
3540 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3542 saxdb_start_record(ctx, KEY_AUTHLOG, 0);
3543 struct authlogEntry *authlog;
3545 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
3546 saxdb_start_record(ctx, strtab(++i), 0);
3547 saxdb_write_int(ctx, KEY_AUTHLOG_LOGIN_TIME, authlog->login_time);
3548 saxdb_write_int(ctx, KEY_AUTHLOG_LOGOUT_TIME, authlog->logout_time);
3549 saxdb_write_string(ctx, KEY_AUTHLOG_HOSTMASK, authlog->hostmask);
3550 if(authlog->quit_reason)
3551 saxdb_write_string(ctx, KEY_AUTHLOG_QUIT_REASON, authlog->quit_reason);
3552 saxdb_end_record(ctx);
3554 saxdb_end_record(ctx); //END KEY_AUTHLOG
3556 saxdb_end_record(ctx);
3561 static handle_merge_func_t *handle_merge_func_list;
3562 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3565 reg_handle_merge_func(handle_merge_func_t func)
3567 if (handle_merge_func_used == handle_merge_func_size) {
3568 if (handle_merge_func_size) {
3569 handle_merge_func_size <<= 1;
3570 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3572 handle_merge_func_size = 8;
3573 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3576 handle_merge_func_list[handle_merge_func_used++] = func;
3579 static NICKSERV_FUNC(cmd_merge)
3581 struct handle_info *hi_from, *hi_to;
3582 struct userNode *last_user;
3583 struct userData *cList, *cListNext;
3584 unsigned int ii, jj, n;
3585 char buffer[MAXLEN];
3587 NICKSERV_MIN_PARMS(3);
3589 if (!(hi_from = get_victim_oper(user, argv[1])))
3591 if (!(hi_to = get_victim_oper(user, argv[2])))
3593 if (hi_to == hi_from) {
3594 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3598 for (n=0; n<handle_merge_func_used; n++)
3599 handle_merge_func_list[n](user, hi_to, hi_from);
3601 /* Append "from" handle's nicks to "to" handle's nick list. */
3603 struct nick_info *last_ni;
3604 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3605 last_ni->next = hi_from->nicks;
3607 while (hi_from->nicks) {
3608 hi_from->nicks->owner = hi_to;
3609 hi_from->nicks = hi_from->nicks->next;
3612 /* Merge the hostmasks. */
3613 for (ii=0; ii<hi_from->masks->used; ii++) {
3614 char *mask = hi_from->masks->list[ii];
3615 for (jj=0; jj<hi_to->masks->used; jj++)
3616 if (match_ircglobs(hi_to->masks->list[jj], mask))
3618 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3619 string_list_append(hi_to->masks, strdup(mask));
3622 /* Merge the lists of authed users. */
3624 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3625 last_user->next_authed = hi_from->users;
3627 hi_to->users = hi_from->users;
3629 /* Repoint the old "from" handle's users. */
3630 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3631 last_user->handle_info = hi_to;
3633 hi_from->users = NULL;
3635 /* Merge channel userlists. */
3636 for (cList=hi_from->channels; cList; cList=cListNext) {
3637 struct userData *cList2;
3638 cListNext = cList->u_next;
3639 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3640 if (cList->channel == cList2->channel)
3642 if (cList2 && (cList2->access >= cList->access)) {
3643 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);
3644 /* keep cList2 in hi_to; remove cList from hi_from */
3645 del_channel_user(cList, 1);
3648 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);
3649 /* remove the lower-ranking cList2 from hi_to */
3650 del_channel_user(cList2, 1);
3652 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3654 /* cList needs to be moved from hi_from to hi_to */
3655 cList->handle = hi_to;
3656 /* Remove from linked list for hi_from */
3657 assert(!cList->u_prev);
3658 hi_from->channels = cList->u_next;
3660 cList->u_next->u_prev = cList->u_prev;
3661 /* Add to linked list for hi_to */
3662 cList->u_prev = NULL;
3663 cList->u_next = hi_to->channels;
3664 if (hi_to->channels)
3665 hi_to->channels->u_prev = cList;
3666 hi_to->channels = cList;
3670 /* Do they get an OpServ level promotion? */
3671 if (hi_from->opserv_level > hi_to->opserv_level)
3672 hi_to->opserv_level = hi_from->opserv_level;
3674 /* Do they get a staff level promotion? */
3675 if (hi_from->staff_level > hi_to->staff_level)
3676 hi_to->staff_level = hi_from->staff_level;
3678 /* What about last seen time? */
3679 if (hi_from->lastseen > hi_to->lastseen)
3680 hi_to->lastseen = hi_from->lastseen;
3682 /* New karma is the sum of the two original karmas. */
3683 hi_to->karma += hi_from->karma;
3685 /* Does a fakehost carry over? (This intentionally doesn't set it
3686 * for users previously attached to hi_to. They'll just have to
3689 if (hi_from->fakehost && !hi_to->fakehost)
3690 hi_to->fakehost = strdup(hi_from->fakehost);
3691 if (hi_from->fakeident && !hi_to->fakeident)
3692 hi_to->fakeident = strdup(hi_from->fakeident);
3694 /* Notify of success. */
3695 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3696 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3697 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3699 /* Unregister the "from" handle. */
3700 nickserv_unregister_handle(hi_from, NULL);
3705 #define NICKSERV_DISCRIM_FIELDS_AUTH 0x01
3706 #define NICKSERV_DISCRIM_FIELDS_EMAIL 0x02
3707 #define NICKSERV_DISCRIM_FIELDS_SEEN 0x04
3708 #define NICKSERV_DISCRIM_FIELDS_ACCESS 0x08
3709 #define NICKSERV_DISCRIM_FIELDS_FAKEHOST 0x10
3710 #define NICKSERV_DISCRIM_FIELDS_WEBSITE 0x20
3711 #define NICKSERV_DISCRIM_FIELDS_DEVNULL 0x40
3713 #define NICKSERV_DISCRIM_FIELD_COUNT 7
3715 struct nickserv_discrim {
3716 unsigned int show_fields;
3717 struct helpfile_table *output_table;
3718 int output_table_pos;
3719 unsigned int output_table_free_fields;
3721 unsigned long flags_on, flags_off;
3722 unsigned long min_registered, max_registered;
3723 unsigned long lastseen;
3725 int min_level, max_level;
3726 int min_karma, max_karma;
3727 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3728 const char *nickmask;
3729 const char *hostmask;
3730 const char *fakehostmask;
3731 const char *fakeidentmask;
3732 const char *website;
3733 const char *devnullclass;
3734 const char *handlemask;
3735 const char *emailmask;
3738 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi, struct nickserv_discrim *discrim);
3740 struct discrim_apply_info {
3741 struct nickserv_discrim *discrim;
3742 discrim_search_func func;
3743 struct userNode *source;
3744 unsigned int matched;
3747 static struct nickserv_discrim *
3748 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3751 struct nickserv_discrim *discrim;
3753 discrim = malloc(sizeof(*discrim));
3754 memset(discrim, 0, sizeof(*discrim));
3755 discrim->min_level = 0;
3756 discrim->max_level = INT_MAX;
3757 discrim->limit = 50;
3758 discrim->min_registered = 0;
3759 discrim->max_registered = ULONG_MAX;
3760 discrim->lastseen = ULONG_MAX;
3761 discrim->min_karma = INT_MIN;
3762 discrim->max_karma = INT_MAX;
3764 for (i=0; i<argc; i++) {
3765 if (i == argc - 1) {
3766 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3769 if (!irccasecmp(argv[i], "limit")) {
3770 discrim->limit = strtoul(argv[++i], NULL, 0);
3771 } else if (!irccasecmp(argv[i], "flags")) {
3772 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3773 } else if (!irccasecmp(argv[i], "fields")) {
3774 char *fields = argv[++i];
3775 char *delimiter = strstr(fields, ",");
3779 if(!irccasecmp(fields, "auth"))
3780 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_AUTH;
3781 else if(!irccasecmp(fields, "email"))
3782 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_EMAIL;
3783 else if(!irccasecmp(fields, "seen"))
3784 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_SEEN;
3785 else if(!irccasecmp(fields, "access"))
3786 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_ACCESS;
3787 else if(!irccasecmp(fields, "fakehost"))
3788 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_FAKEHOST;
3789 else if(!irccasecmp(fields, "website") && IsBot(user))
3790 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_WEBSITE;
3791 else if(!irccasecmp(fields, "devnull"))
3792 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_DEVNULL;
3794 send_message(user, nickserv, "MSG_INVALID_FIELD", fields);
3799 fields = delimiter+1;
3801 delimiter = strstr(fields, ",");
3807 } else if (!irccasecmp(argv[i], "registered")) {
3808 const char *cmp = argv[++i];
3809 if (cmp[0] == '<') {
3810 if (cmp[1] == '=') {
3811 discrim->min_registered = now - ParseInterval(cmp+2);
3813 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3815 } else if (cmp[0] == '=') {
3816 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3817 } else if (cmp[0] == '>') {
3818 if (cmp[1] == '=') {
3819 discrim->max_registered = now - ParseInterval(cmp+2);
3821 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3824 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3826 } else if (!irccasecmp(argv[i], "seen")) {
3827 discrim->lastseen = now - ParseInterval(argv[++i]);
3828 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3829 discrim->nickmask = argv[++i];
3830 } else if (!irccasecmp(argv[i], "hostmask")) {
3832 if (!irccasecmp(argv[i], "exact")) {
3833 if (i == argc - 1) {
3834 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3837 discrim->hostmask_type = EXACT;
3838 } else if (!irccasecmp(argv[i], "subset")) {
3839 if (i == argc - 1) {
3840 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3843 discrim->hostmask_type = SUBSET;
3844 } else if (!irccasecmp(argv[i], "superset")) {
3845 if (i == argc - 1) {
3846 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3849 discrim->hostmask_type = SUPERSET;
3850 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3851 if (i == argc - 1) {
3852 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3855 discrim->hostmask_type = LASTQUIT;
3858 discrim->hostmask_type = SUPERSET;
3860 discrim->hostmask = argv[++i];
3861 } else if (!irccasecmp(argv[i], "fakehost")) {
3862 if (!irccasecmp(argv[++i], "*")) {
3863 discrim->fakehostmask = 0;
3865 discrim->fakehostmask = argv[i];
3867 } else if (!irccasecmp(argv[i], "fakeident")) {
3868 if (!irccasecmp(argv[++i], "*")) {
3869 discrim->fakeidentmask = 0;
3871 discrim->fakeidentmask = argv[i];
3873 } else if (!irccasecmp(argv[i], "website")) {
3874 if (!irccasecmp(argv[++i], "*")) {
3875 discrim->website = 0;
3877 discrim->website = argv[i];
3879 } else if (!irccasecmp(argv[i], "devnull")) {
3880 if (!irccasecmp(argv[++i], "*")) {
3881 discrim->devnullclass = 0;
3883 discrim->devnullclass = argv[i];
3885 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3886 if (!irccasecmp(argv[++i], "*")) {
3887 discrim->handlemask = 0;
3889 discrim->handlemask = argv[i];
3891 } else if (!irccasecmp(argv[i], "email")) {
3892 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3893 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3895 } else if (!irccasecmp(argv[++i], "*")) {
3896 discrim->emailmask = 0;
3898 discrim->emailmask = argv[i];
3900 } else if (!irccasecmp(argv[i], "access")) {
3901 const char *cmp = argv[++i];
3902 if (cmp[0] == '<') {
3903 if (discrim->min_level == 0) discrim->min_level = 1;
3904 if (cmp[1] == '=') {
3905 discrim->max_level = strtoul(cmp+2, NULL, 0);
3907 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3909 } else if (cmp[0] == '=') {
3910 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3911 } else if (cmp[0] == '>') {
3912 if (cmp[1] == '=') {
3913 discrim->min_level = strtoul(cmp+2, NULL, 0);
3915 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3918 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3920 } else if (!irccasecmp(argv[i], "karma")) {
3921 const char *cmp = argv[++i];
3922 if (cmp[0] == '<') {
3923 if (cmp[1] == '=') {
3924 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3926 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3928 } else if (cmp[0] == '=') {
3929 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3930 } else if (cmp[0] == '>') {
3931 if (cmp[1] == '=') {
3932 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3934 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3937 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3940 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3951 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3953 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3954 || (discrim->flags_off & hi->flags)
3955 || (discrim->min_registered > hi->registered)
3956 || (discrim->max_registered < hi->registered)
3957 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3958 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3959 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3960 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3961 || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website)))
3962 || (discrim->devnullclass && (!hi->devnull || !match_ircglob(hi->devnull, discrim->devnullclass)))
3963 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3964 || (discrim->min_level > hi->opserv_level)
3965 || (discrim->max_level < hi->opserv_level)
3966 || (discrim->min_karma > hi->karma)
3967 || (discrim->max_karma < hi->karma)
3971 if (discrim->hostmask) {
3973 for (i=0; i<hi->masks->used; i++) {
3974 const char *mask = hi->masks->list[i];
3975 if ((discrim->hostmask_type == SUBSET)
3976 && (match_ircglobs(discrim->hostmask, mask))) break;
3977 else if ((discrim->hostmask_type == EXACT)
3978 && !irccasecmp(discrim->hostmask, mask)) break;
3979 else if ((discrim->hostmask_type == SUPERSET)
3980 && (match_ircglobs(mask, discrim->hostmask))) break;
3981 else if ((discrim->hostmask_type == LASTQUIT)
3982 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3984 if (i==hi->masks->used) return 0;
3986 if (discrim->nickmask) {
3987 struct nick_info *nick = hi->nicks;
3989 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3992 if (!nick) return 0;
3998 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
4000 dict_iterator_t it, next;
4001 unsigned int matched;
4003 for (it = dict_first(nickserv_handle_dict), matched = 0;
4004 it && (matched < discrim->limit);
4006 next = iter_next(it);
4007 if (nickserv_discrim_match(discrim, iter_data(it))) {
4008 dsf(source, iter_data(it), discrim);
4016 search_print_func(struct userNode *source, struct handle_info *match, struct nickserv_discrim *discrim)
4018 if(discrim->show_fields) {
4020 if(discrim->output_table) {
4021 discrim->output_table->contents[++discrim->output_table_pos] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
4023 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
4024 discrim->output_table->contents[discrim->output_table_pos][i++] = match->handle;
4025 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
4026 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->email_addr ? match->email_addr : "*");
4027 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN) {
4029 char seenBuf[INTERVALLEN];
4032 } else if(match->lastseen == 0) {
4035 seen = intervalString(seenBuf, now - match->lastseen, source->handle_info);
4037 discrim->output_table_free_fields |= 1 << i;
4038 discrim->output_table->contents[discrim->output_table_pos][i++] = strdup(seen);
4040 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
4041 discrim->output_table->contents[discrim->output_table_pos][i++] = strtab(match->opserv_level);
4042 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
4043 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->fakehost ? match->fakehost : "*");
4044 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
4045 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->website ? match->website : "*");
4046 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
4047 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->devnull ? match->devnull : "*");
4051 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
4055 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match), UNUSED_ARG(struct nickserv_discrim *discrim))
4060 search_unregister_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
4062 if (oper_has_access(source, nickserv, match->opserv_level, 0))
4063 nickserv_unregister_handle(match, source);
4067 nickserv_sort_accounts_by_access(const void *a, const void *b)
4069 const struct handle_info *hi_a = *(const struct handle_info**)a;
4070 const struct handle_info *hi_b = *(const struct handle_info**)b;
4071 if (hi_a->opserv_level != hi_b->opserv_level)
4072 return hi_b->opserv_level - hi_a->opserv_level;
4073 return irccasecmp(hi_a->handle, hi_b->handle);
4077 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
4079 struct handle_info_list hil;
4080 struct helpfile_table tbl;
4085 memset(&hil, 0, sizeof(hil));
4086 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
4087 struct handle_info *hi = iter_data(it);
4088 if (hi->opserv_level)
4089 handle_info_list_append(&hil, hi);
4091 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
4092 tbl.length = hil.used + 1;
4094 tbl.flags = TABLE_NO_FREE;
4095 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4096 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
4099 for (ii = 0; ii < hil.used; ) {
4100 ary = malloc(tbl.width * sizeof(ary[0]));
4101 ary[0] = hil.list[ii]->handle;
4102 ary[1] = strtab(hil.list[ii]->opserv_level);
4103 tbl.contents[++ii] = ary;
4105 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4106 reply("MSG_MATCH_COUNT", hil.used);
4107 for (ii = 0; ii < hil.used; ii++)
4108 free(tbl.contents[ii]);
4113 static NICKSERV_FUNC(cmd_search)
4115 struct nickserv_discrim *discrim;
4116 discrim_search_func action;
4117 struct svccmd *subcmd;
4118 unsigned int matches;
4121 NICKSERV_MIN_PARMS(3);
4122 sprintf(buf, "search %s", argv[1]);
4123 subcmd = dict_find(nickserv_service->commands, buf, NULL);
4124 if (!irccasecmp(argv[1], "print"))
4125 action = search_print_func;
4126 else if (!irccasecmp(argv[1], "count"))
4127 action = search_count_func;
4128 else if (!irccasecmp(argv[1], "unregister"))
4129 action = search_unregister_func;
4131 reply("NSMSG_INVALID_ACTION", argv[1]);
4135 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
4138 discrim = nickserv_discrim_create(user, argc-2, argv+2);
4142 if (action == search_print_func)
4143 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
4144 else if (action == search_count_func)
4145 discrim->limit = INT_MAX;
4147 matches = nickserv_discrim_search(discrim, action, user);
4149 if(discrim->show_fields) {
4152 for(ii = 0; ii < NICKSERV_DISCRIM_FIELD_COUNT; ii++) {
4153 if(discrim->show_fields & (1 << ii)) width++;
4155 discrim->output_table = malloc(sizeof(discrim->output_table[0]));
4156 discrim->output_table->length = matches+1;
4157 discrim->output_table->width = width;
4158 discrim->output_table->flags = TABLE_NO_FREE;
4159 discrim->output_table->contents = malloc(discrim->output_table->length * sizeof(discrim->output_table->contents[0]));
4160 discrim->output_table->contents[0] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
4163 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
4164 discrim->output_table->contents[0][ii++] = "Auth";
4165 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
4166 discrim->output_table->contents[0][ii++] = "EMail";
4167 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN)
4168 discrim->output_table->contents[0][ii++] = "Seen";
4169 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
4170 discrim->output_table->contents[0][ii++] = "Access";
4171 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
4172 discrim->output_table->contents[0][ii++] = "Fakehost";
4173 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
4174 discrim->output_table->contents[0][ii++] = "Website";
4175 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
4176 discrim->output_table->contents[0][ii++] = "DevNull";
4178 nickserv_discrim_search(discrim, action, user);
4180 table_send(nickserv, user->nick, 0, NULL, *discrim->output_table);
4182 for(ii = 1; ii < discrim->output_table->length; ++ii) {
4184 for(ij = 0; ij < NICKSERV_DISCRIM_FIELD_COUNT; ij++) {
4185 if(discrim->output_table_free_fields & (1 << ij))
4186 free((char*)discrim->output_table->contents[ii][ij]);
4188 free(discrim->output_table->contents[ii]);
4190 free(discrim->output_table->contents[0]);
4191 free(discrim->output_table->contents);
4192 free(discrim->output_table);
4195 reply("MSG_MATCH_COUNT", matches);
4197 reply("MSG_NO_MATCHES");
4204 static MODCMD_FUNC(cmd_checkpass)
4206 struct handle_info *hi;
4208 NICKSERV_MIN_PARMS(3);
4209 if (!(hi = get_handle_info(argv[1]))) {
4210 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4213 if (checkpass(argv[2], hi->passwd))
4214 reply("CHECKPASS_YES");
4216 reply("CHECKPASS_NO");
4221 static MODCMD_FUNC(cmd_checkemail)
4223 struct handle_info *hi;
4225 NICKSERV_MIN_PARMS(3);
4226 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
4229 if (!hi->email_addr)
4230 reply("CHECKEMAIL_NOT_SET");
4231 else if (!irccasecmp(argv[2], hi->email_addr))
4232 reply("CHECKEMAIL_YES");
4234 reply("CHECKEMAIL_NO");
4239 nickserv_db_read_authlog(UNUSED_ARG(const char *key), void *data, void *extra)
4241 struct record_data *rd = data;
4242 struct handle_info *hi = extra;
4244 struct authlogEntry *authlog;
4245 authlog = malloc(sizeof(*authlog));
4247 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGIN_TIME, RECDB_QSTRING);
4248 authlog->login_time = str ? strtoul(str, NULL, 0) : 0;
4250 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGOUT_TIME, RECDB_QSTRING);
4251 authlog->logout_time = str ? strtoul(str, NULL, 0) : 0;
4253 str = database_get_data(rd->d.object, KEY_AUTHLOG_HOSTMASK, RECDB_QSTRING);
4254 authlog->hostmask = str ? strdup(str) : NULL;
4256 str = database_get_data(rd->d.object, KEY_AUTHLOG_QUIT_REASON, RECDB_QSTRING);
4257 authlog->quit_reason = str ? strdup(str) : NULL;
4259 authlog->user = NULL;
4261 authlog->next = NULL;
4263 //append it to the end of the list...
4264 struct authlogEntry *authlog_entry;
4266 hi->authlog = authlog;
4268 for(authlog_entry = hi->authlog; authlog_entry; authlog_entry = authlog_entry->next) {
4269 if(!authlog_entry->next) {
4270 authlog_entry->next = authlog;
4279 nickserv_db_read_handle(const char *handle, dict_t obj)
4282 struct string_list *masks, *slist;
4283 struct handle_info *hi;
4284 struct userNode *authed_users;
4285 struct userData *channel_list;
4290 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
4291 id = str ? strtoul(str, NULL, 0) : 0;
4292 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
4294 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
4297 if ((hi = get_handle_info(handle))) {
4298 authed_users = hi->users;
4299 channel_list = hi->channels;
4301 hi->channels = NULL;
4302 dict_remove(nickserv_handle_dict, hi->handle);
4304 authed_users = NULL;
4305 channel_list = NULL;
4307 hi = register_handle(handle, str, id);
4309 hi->users = authed_users;
4310 while (authed_users) {
4311 authed_users->handle_info = hi;
4312 authed_users = authed_users->next_authed;
4315 hi->channels = channel_list;
4316 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
4317 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
4318 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
4319 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
4320 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
4321 hi->language = language_find(str ? str : "C");
4322 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
4323 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
4324 str = database_get_data(obj, KEY_STAFF_LEVEL, RECDB_QSTRING);
4325 hi->staff_level = str ? strtoul(str, NULL, 0) : 0;
4326 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
4328 hi->infoline = strdup(str);
4329 str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING);
4331 hi->website = strdup(str);
4332 str = database_get_data(obj, KEY_DEVNULL, RECDB_QSTRING);
4334 hi->devnull = strdup(str);
4335 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
4336 hi->registered = str ? strtoul(str, NULL, 0) : now;
4337 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
4338 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
4339 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
4340 hi->karma = str ? strtoul(str, NULL, 0) : 0;
4341 /* We want to read the nicks even if disable_nicks is set. This is so
4342 * that we don't lose the nick data entirely. */
4343 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
4345 for (ii=0; ii<slist->used; ii++)
4346 register_nick(slist->list[ii], hi);
4348 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
4350 for (ii=0; str[ii]; ii++)
4351 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
4353 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
4354 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
4355 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
4356 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
4357 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
4358 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
4359 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
4361 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
4363 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
4364 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
4366 nickserv_set_email_addr(hi, str);
4367 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
4369 hi->epithet = strdup(str);
4370 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
4372 hi->fakehost = strdup(str);
4373 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
4375 hi->fakeident = strdup(str);
4376 /* Read the "cookie" sub-database (if it exists). */
4377 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
4379 const char *data, *type, *expires, *cookie_str;
4380 struct handle_cookie *cookie;
4382 cookie = calloc(1, sizeof(*cookie));
4383 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
4384 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
4385 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
4386 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
4387 if (!type || !expires || !cookie_str) {
4388 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
4391 if (!irccasecmp(type, KEY_ACTIVATION))
4392 cookie->type = ACTIVATION;
4393 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
4394 cookie->type = PASSWORD_CHANGE;
4395 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
4396 cookie->type = EMAIL_CHANGE;
4397 else if (!irccasecmp(type, KEY_ALLOWAUTH))
4398 cookie->type = ALLOWAUTH;
4400 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
4403 cookie->expires = strtoul(expires, NULL, 0);
4404 if (cookie->expires < now)
4407 cookie->data = strdup(data);
4408 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
4412 nickserv_bake_cookie(cookie);
4414 nickserv_free_cookie(cookie);
4416 /* Read the "notes" sub-database (if it exists). */
4417 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
4420 struct handle_note *last_note;
4421 struct handle_note *note;
4424 for (it = dict_first(subdb); it; it = iter_next(it)) {
4425 const char *expires;
4429 const char *note_id;
4432 note_id = iter_key(it);
4433 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
4435 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
4438 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
4439 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
4440 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
4441 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
4442 if (!setter || !text || !set) {
4443 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
4446 note = calloc(1, sizeof(*note) + strlen(text));
4448 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
4449 note->set = strtoul(set, NULL, 10);
4450 note->id = strtoul(note_id, NULL, 10);
4451 safestrncpy(note->setter, setter, sizeof(note->setter));
4452 strcpy(note->note, text);
4454 last_note->next = note;
4460 if ((subdb = database_get_data(obj, KEY_AUTHLOG, RECDB_OBJECT)))
4461 dict_foreach(subdb, nickserv_db_read_authlog, hi);
4465 nickserv_saxdb_read(dict_t db) {
4467 struct record_data *rd;
4469 for (it=dict_first(db); it; it=iter_next(it)) {
4471 nickserv_db_read_handle(iter_key(it), rd->d.object);
4476 static NICKSERV_FUNC(cmd_mergedb)
4478 struct timeval start, stop;
4481 NICKSERV_MIN_PARMS(2);
4482 gettimeofday(&start, NULL);
4483 if (!(db = parse_database(argv[1]))) {
4484 reply("NSMSG_DB_UNREADABLE", argv[1]);
4487 nickserv_saxdb_read(db);
4489 gettimeofday(&stop, NULL);
4490 stop.tv_sec -= start.tv_sec;
4491 stop.tv_usec -= start.tv_usec;
4492 if (stop.tv_usec < 0) {
4494 stop.tv_usec += 1000000;
4496 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
4501 expire_handles(UNUSED_ARG(void *data))
4503 dict_iterator_t it, next;
4504 unsigned long expiry;
4505 struct handle_info *hi;
4507 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4508 next = iter_next(it);
4510 if ((hi->opserv_level > 0)
4512 || HANDLE_FLAGGED(hi, FROZEN)
4513 || HANDLE_FLAGGED(hi, NODELETE)) {
4516 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4517 if ((now - hi->lastseen) > expiry) {
4518 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
4519 nickserv_unregister_handle(hi, NULL);
4523 if (nickserv_conf.handle_expire_frequency)
4524 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4528 nickserv_load_dict(const char *fname)
4532 if (!(file = fopen(fname, "r"))) {
4533 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4536 while (fgets(line, sizeof(line), file)) {
4539 if (line[strlen(line)-1] == '\n')
4540 line[strlen(line)-1] = 0;
4541 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4544 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4547 static enum reclaim_action
4548 reclaim_action_from_string(const char *str) {
4550 return RECLAIM_NONE;
4551 else if (!irccasecmp(str, "warn"))
4552 return RECLAIM_WARN;
4553 else if (!irccasecmp(str, "svsnick"))
4554 return RECLAIM_SVSNICK;
4555 else if (!irccasecmp(str, "kill"))
4556 return RECLAIM_KILL;
4558 return RECLAIM_NONE;
4562 nickserv_conf_read(void)
4564 dict_t conf_node, child;
4568 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4569 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4572 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4574 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4575 if (nickserv_conf.valid_handle_regex_set)
4576 regfree(&nickserv_conf.valid_handle_regex);
4578 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4579 nickserv_conf.valid_handle_regex_set = !err;
4580 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4582 nickserv_conf.valid_handle_regex_set = 0;
4584 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4585 if (nickserv_conf.valid_nick_regex_set)
4586 regfree(&nickserv_conf.valid_nick_regex);
4588 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4589 nickserv_conf.valid_nick_regex_set = !err;
4590 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4592 nickserv_conf.valid_nick_regex_set = 0;
4594 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4596 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4597 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4598 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4599 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4600 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4601 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4602 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4603 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4604 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4605 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4606 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4607 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4608 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4609 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4610 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4611 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4612 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4613 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4614 str = database_get_data(conf_node, KEY_MODSTAFF_LEVEL, RECDB_QSTRING);
4615 nickserv_conf.modstaff_level = str ? strtoul(str, NULL, 0) : 800;
4616 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4617 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4618 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4619 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4620 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4621 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4622 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
4623 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
4624 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4626 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4627 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4628 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4630 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4631 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4632 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4634 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4635 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4636 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4637 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4638 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4639 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4640 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4641 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4642 str = database_get_data(conf_node, KEY_MAX_AUTHLOG_LEN, RECDB_QSTRING);
4643 nickserv_conf.max_authlog_len = str ? strtoul(str, NULL, 0) : 30;
4644 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4645 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4646 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4649 nickserv_conf.ounregister_flags = 0;
4651 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4654 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4656 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4658 nickserv_conf.handle_ts_mode = TS_IGNORE;
4659 else if (!irccasecmp(str, "ircu"))
4660 nickserv_conf.handle_ts_mode = TS_IRCU;
4662 nickserv_conf.handle_ts_mode = TS_IGNORE;
4663 if (!nickserv_conf.disable_nicks) {
4664 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4665 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4666 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4667 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4668 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4669 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4670 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4671 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4673 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4674 for (it=dict_first(child); it; it=iter_next(it)) {
4675 const char *key = iter_key(it), *value;
4679 if (!strncasecmp(key, "uc_", 3))
4680 flag = toupper(key[3]);
4681 else if (!strncasecmp(key, "lc_", 3))
4682 flag = tolower(key[3]);
4686 if ((pos = handle_inverse_flags[flag])) {
4687 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4688 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4691 if (nickserv_conf.weak_password_dict)
4692 dict_delete(nickserv_conf.weak_password_dict);
4693 nickserv_conf.weak_password_dict = dict_new();
4694 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4695 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4696 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4697 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4699 nickserv_load_dict(str);
4700 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4701 if (nickserv && str)
4702 NickChange(nickserv, str, 0);
4703 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4704 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4705 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4706 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4707 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4708 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4709 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4710 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4711 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4712 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4713 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4714 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4715 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4716 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4717 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4718 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4719 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4720 titlehost_suffix = str ? str : "example.net";
4721 str = conf_get_data("server/network", RECDB_QSTRING);
4722 nickserv_conf.network_name = str ? str : "some IRC network";
4723 if (!nickserv_conf.auth_policer_params) {
4724 nickserv_conf.auth_policer_params = policer_params_new();
4725 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4726 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4728 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4729 for (it=dict_first(child); it; it=iter_next(it))
4730 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4734 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4736 char newnick[NICKLEN+1];
4745 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4747 case RECLAIM_SVSNICK:
4749 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4750 } while (GetUserH(newnick));
4751 irc_svsnick(nickserv, user, newnick);
4754 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4755 DelUser(user, nickserv, 1, msg);
4761 nickserv_reclaim_p(void *data) {
4762 struct userNode *user = data;
4763 struct nick_info *ni = get_nick_info(user->nick);
4765 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4769 check_user_nick(struct userNode *user) {
4770 //check if this user is a pending LOC user
4771 if(pendingLOCUsers) {
4772 struct pendingLOCUser *pending, *next, *prev = NULL;
4774 for(pending = pendingLOCUsers; pending; pending = next) {
4775 next = pending->next;
4777 if(user->handle_info == pending->handle_info) {
4778 pending->authlog->user = user;
4779 free((char*) pending->authlog->hostmask);
4780 pending->authlog->hostmask = generate_hostmask(user, GENMASK_USENICK|GENMASK_STRICT_IDENT|GENMASK_NO_HIDING|GENMASK_STRICT_HOST);
4782 } else if(now - pending->time > 10)
4788 pendingLOCUsers = next;
4793 struct nick_info *ni;
4794 user->modes &= ~FLAGS_REGNICK;
4795 if (!(ni = get_nick_info(user->nick)))
4797 if (user->handle_info == ni->owner) {
4798 user->modes |= FLAGS_REGNICK;
4802 if (nickserv_conf.warn_nick_owned)
4803 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4804 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4806 if (nickserv_conf.auto_reclaim_delay)
4807 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4809 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4813 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4815 struct handle_info *hi = NULL;
4818 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4819 if ((hi == NULL) && (serial != 0)) {
4821 inttobase64(id, serial, IDLEN);
4822 hi = dict_find(nickserv_id_dict, id, NULL);
4826 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4827 && (timestamp != hi->registered)) {
4830 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4833 set_user_handle_info(user, hi, 0);
4835 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4840 handle_nick_change(struct userNode *user, const char *old_nick)
4842 struct handle_info *hi;
4844 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4845 dict_remove(nickserv_allow_auth_dict, old_nick);
4846 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4848 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4849 check_user_nick(user);
4853 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), const char *why)
4855 if(user->handle_info) {
4856 //check if theres an open authlog entry
4857 struct authlogEntry *authlog;
4858 for(authlog = user->handle_info->authlog; authlog; authlog = authlog->next) {
4859 if(authlog->user == user) {
4860 authlog->user = NULL;
4861 authlog->logout_time = now;
4862 authlog->quit_reason = strdup(why);
4867 dict_remove(nickserv_allow_auth_dict, user->nick);
4868 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4869 set_user_handle_info(user, NULL, 0);
4872 static struct modcmd *
4873 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4875 if (min_level > 0) {
4877 sprintf(buf, "%u", min_level);
4878 if (must_be_qualified) {
4879 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4881 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4883 } else if (min_level == 0) {
4884 if (must_be_qualified) {
4885 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4887 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4890 if (must_be_qualified) {
4891 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4893 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4899 nickserv_db_cleanup(void)
4901 unreg_del_user_func(nickserv_remove_user);
4902 userList_clean(&curr_helpers);
4903 policer_params_delete(nickserv_conf.auth_policer_params);
4904 dict_delete(nickserv_handle_dict);
4905 dict_delete(nickserv_nick_dict);
4906 dict_delete(nickserv_opt_dict);
4907 dict_delete(nickserv_allow_auth_dict);
4908 dict_delete(nickserv_email_dict);
4909 dict_delete(nickserv_id_dict);
4910 dict_delete(nickserv_conf.weak_password_dict);
4911 free(auth_func_list);
4912 free(unreg_func_list);
4914 free(allowauth_func_list);
4915 free(handle_merge_func_list);
4916 free(failpw_func_list);
4917 if (nickserv_conf.valid_handle_regex_set)
4918 regfree(&nickserv_conf.valid_handle_regex);
4919 if (nickserv_conf.valid_nick_regex_set)
4920 regfree(&nickserv_conf.valid_nick_regex);
4921 struct pendingLOCUser *pending, *next;
4922 for(pending = pendingLOCUsers; pending; pending = next) {
4923 next = pending->next;
4926 pendingLOCUsers = NULL;
4930 init_nickserv(const char *nick)
4933 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4934 reg_new_user_func(check_user_nick);
4935 reg_nick_change_func(handle_nick_change);
4936 reg_del_user_func(nickserv_remove_user);
4937 reg_account_func(handle_account);
4939 /* set up handle_inverse_flags */
4940 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4941 for (i=0; handle_flags[i]; i++) {
4942 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4943 flag_access_levels[i] = 0;
4946 conf_register_reload(nickserv_conf_read);
4947 nickserv_opt_dict = dict_new();
4948 nickserv_email_dict = dict_new();
4949 dict_set_free_keys(nickserv_email_dict, free);
4950 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4952 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4953 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4954 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4955 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4956 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4957 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4958 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4959 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4960 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4961 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4962 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4963 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4964 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4965 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4966 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4967 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4968 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4969 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4970 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4971 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4972 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4973 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4974 if (!nickserv_conf.disable_nicks) {
4975 /* nick management commands */
4976 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4977 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4978 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4979 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4980 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4981 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4983 if (nickserv_conf.email_enabled) {
4984 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4985 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4986 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4987 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4988 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4989 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4991 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4992 /* miscellaneous commands */
4993 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4994 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4995 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4996 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4997 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4998 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4999 nickserv_define_func("AUTHLOG", cmd_authlog, -1, 1, 0);
5000 nickserv_define_func("OAUTHLOG", cmd_oauthlog, 0, 1, 0);
5002 dict_insert(nickserv_opt_dict, "INFO", opt_info);
5003 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
5004 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
5005 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
5006 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
5007 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
5008 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
5009 dict_insert(nickserv_opt_dict, "PASS", opt_password);
5010 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
5011 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
5012 dict_insert(nickserv_opt_dict, "WEBSITE", opt_website);
5013 dict_insert(nickserv_opt_dict, "DEVNULL", opt_devnull);
5014 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
5015 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
5016 dict_insert(nickserv_opt_dict, "STAFF", opt_staff_level);
5017 dict_insert(nickserv_opt_dict, "STAFF_LEVEL", opt_staff_level);
5018 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
5019 if (titlehost_suffix) {
5020 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
5021 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
5022 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
5024 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
5025 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
5026 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
5027 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
5029 nickserv_handle_dict = dict_new();
5030 dict_set_free_keys(nickserv_handle_dict, free);
5031 dict_set_free_data(nickserv_handle_dict, free_handle_info);
5033 nickserv_id_dict = dict_new();
5034 dict_set_free_keys(nickserv_id_dict, free);
5036 nickserv_nick_dict = dict_new();
5037 dict_set_free_data(nickserv_nick_dict, free);
5039 nickserv_allow_auth_dict = dict_new();
5041 userList_init(&curr_helpers);
5044 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
5045 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
5046 nickserv_service = service_register(nickserv);
5048 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
5049 reg_exit_func(nickserv_db_cleanup);
5050 if(nickserv_conf.handle_expire_frequency)
5051 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
5052 message_register_table(msgtab);