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 string_list_append(hi->masks, strdup("*@*"));
1387 string_list_append(hi->masks, 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 string_list_append(hi->masks, 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 string_list_append(hi->masks, 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 string_list_append(hi->masks, 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])) {
2424 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2429 string_list_append(hi->masks, new_mask);
2430 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2434 static NICKSERV_FUNC(cmd_addmask)
2437 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2438 int res = nickserv_addmask(user, user->handle_info, mask);
2442 if (!is_gline(argv[1])) {
2443 reply("NSMSG_MASK_INVALID", argv[1]);
2446 return nickserv_addmask(user, user->handle_info, argv[1]);
2450 static NICKSERV_FUNC(cmd_oaddmask)
2452 struct handle_info *hi;
2454 NICKSERV_MIN_PARMS(3);
2455 if (!(hi = get_victim_oper(user, argv[1])))
2457 return nickserv_addmask(user, hi, argv[2]);
2461 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2464 for (i=0; i<hi->masks->used; i++) {
2465 if (!strcmp(del_mask, hi->masks->list[i])) {
2466 char *old_mask = hi->masks->list[i];
2467 if (hi->masks->used == 1 && !force) {
2468 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2471 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2472 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2477 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2481 static NICKSERV_FUNC(cmd_delmask)
2483 NICKSERV_MIN_PARMS(2);
2484 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2487 static NICKSERV_FUNC(cmd_odelmask)
2489 struct handle_info *hi;
2490 NICKSERV_MIN_PARMS(3);
2491 if (!(hi = get_victim_oper(user, argv[1])))
2493 return nickserv_delmask(user, hi, argv[2], 1);
2497 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2498 unsigned int nn, add = 1, pos;
2499 unsigned long added, removed, flag;
2501 for (added=removed=nn=0; str[nn]; nn++) {
2503 case '+': add = 1; break;
2504 case '-': add = 0; break;
2506 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2507 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2510 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2511 /* cheesy avoidance of looking up the flag name.. */
2512 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2515 flag = 1 << (pos - 1);
2517 added |= flag, removed &= ~flag;
2519 removed |= flag, added &= ~flag;
2524 *premoved = removed;
2529 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2531 unsigned long before, after, added, removed;
2532 struct userNode *uNode;
2534 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2535 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2537 hi->flags = (hi->flags | added) & ~removed;
2538 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2540 /* Strip helping flag if they're only a support helper and not
2541 * currently in #support. */
2542 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2543 struct channelList *schannels;
2545 schannels = chanserv_support_channels();
2546 for (ii = 0; ii < schannels->used; ++ii)
2547 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2549 if (ii == schannels->used)
2550 HANDLE_CLEAR_FLAG(hi, HELPING);
2553 if (after && !before) {
2554 /* Add user to current helper list. */
2555 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2556 userList_append(&curr_helpers, uNode);
2557 } else if (!after && before) {
2558 /* Remove user from current helper list. */
2559 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2560 userList_remove(&curr_helpers, uNode);
2567 set_list(struct userNode *user, struct handle_info *hi, int override)
2571 char *set_display[] = {
2572 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2573 "EMAIL", "MAXLOGINS", "LANGUAGE", "DEVNULL"
2576 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2578 /* Do this so options are presented in a consistent order. */
2579 for (i = 0; i < ArrayLength(set_display); ++i)
2580 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2581 opt(user, hi, override, 0, NULL);
2584 static NICKSERV_FUNC(cmd_set)
2586 struct handle_info *hi;
2589 hi = user->handle_info;
2591 set_list(user, hi, 0);
2594 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2595 reply("NSMSG_INVALID_OPTION", argv[1]);
2598 return opt(user, hi, 0, argc-1, argv+1);
2601 static NICKSERV_FUNC(cmd_oset)
2603 struct handle_info *hi;
2604 struct svccmd *subcmd;
2606 char cmdname[MAXLEN];
2608 NICKSERV_MIN_PARMS(2);
2610 if (!(hi = get_victim_oper(user, argv[1])))
2614 set_list(user, hi, 0);
2618 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2619 reply("NSMSG_INVALID_OPTION", argv[2]);
2623 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2624 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2625 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2628 return opt(user, hi, 1, argc-2, argv+2);
2631 static OPTION_FUNC(opt_info)
2635 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2637 hi->infoline = NULL;
2639 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2643 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2644 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2648 static OPTION_FUNC(opt_devnull)
2650 const char *devnull;
2654 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2657 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2661 devnull = unsplit_string(argv+1, argc-1, NULL);
2662 if(devnull_check(devnull) == 1) {
2665 hi->devnull = strdup(devnull);
2670 devnull = hi->devnull ? hi->devnull : user_find_message(user, "MSG_NONE");
2671 send_message(user, nickserv, "NSMSG_SET_DEVNULL", devnull);
2675 void nickserv_devnull_delete(char *name) {
2677 struct handle_info *hi;
2679 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2681 if (hi->devnull && !irccasecmp(name, hi->devnull)) {
2688 void nickserv_devnull_rename(char *oldname, char *newname) {
2690 struct handle_info *hi;
2692 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2694 if (hi->devnull && !irccasecmp(oldname, hi->devnull)) {
2695 hi->devnull = strdup(newname);
2700 static OPTION_FUNC(opt_website)
2702 const char *website;
2705 if (!HANDLE_FLAGGED(user->handle_info, BOT)) {
2706 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2709 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2713 website = unsplit_string(argv+1, argc-1, NULL);
2714 hi->website = strdup(website);
2717 if (HANDLE_FLAGGED(user->handle_info, BOT)) {
2718 website = hi->website ? hi->website : user_find_message(user, "MSG_NONE");
2719 send_message(user, nickserv, "NSMSG_SET_WEBSITE", website);
2724 static OPTION_FUNC(opt_width)
2727 hi->screen_width = strtoul(argv[1], NULL, 0);
2729 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2730 hi->screen_width = MIN_LINE_SIZE;
2731 else if (hi->screen_width > MAX_LINE_SIZE)
2732 hi->screen_width = MAX_LINE_SIZE;
2734 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2738 static OPTION_FUNC(opt_tablewidth)
2741 hi->table_width = strtoul(argv[1], NULL, 0);
2743 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2744 hi->table_width = MIN_LINE_SIZE;
2745 else if (hi->screen_width > MAX_LINE_SIZE)
2746 hi->table_width = MAX_LINE_SIZE;
2748 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2752 static OPTION_FUNC(opt_color)
2755 if (enabled_string(argv[1]))
2756 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2757 else if (disabled_string(argv[1]))
2758 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2760 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2765 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2769 static OPTION_FUNC(opt_privmsg)
2772 if (enabled_string(argv[1]))
2773 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2774 else if (disabled_string(argv[1]))
2775 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2777 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2782 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2786 static OPTION_FUNC(opt_autohide)
2789 if (enabled_string(argv[1]))
2790 HANDLE_SET_FLAG(hi, AUTOHIDE);
2791 else if (disabled_string(argv[1]))
2792 HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
2794 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2799 send_message(user, nickserv, "NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
2803 static OPTION_FUNC(opt_style)
2808 if (!irccasecmp(argv[1], "Zoot"))
2809 hi->userlist_style = HI_STYLE_ZOOT;
2810 else if (!irccasecmp(argv[1], "def"))
2811 hi->userlist_style = HI_STYLE_DEF;
2814 switch (hi->userlist_style) {
2823 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2827 static OPTION_FUNC(opt_password)
2830 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2835 cryptpass(argv[1], hi->passwd);
2837 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2843 static OPTION_FUNC(opt_flags)
2846 unsigned int ii, flen;
2849 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2854 nickserv_apply_flags(user, hi, argv[1]);
2856 for (ii = flen = 0; handle_flags[ii]; ii++)
2857 if (hi->flags & (1 << ii))
2858 flags[flen++] = handle_flags[ii];
2861 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2863 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2867 static OPTION_FUNC(opt_email)
2871 if (!is_valid_email_addr(argv[1])) {
2872 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2875 if ((str = mail_prohibited_address(argv[1]))) {
2876 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2879 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2880 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2882 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2884 nickserv_set_email_addr(hi, argv[1]);
2886 nickserv_eat_cookie(hi->cookie);
2887 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2890 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2894 static OPTION_FUNC(opt_maxlogins)
2896 unsigned char maxlogins;
2898 maxlogins = strtoul(argv[1], NULL, 0);
2899 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2900 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2903 hi->maxlogins = maxlogins;
2905 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2906 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2910 static OPTION_FUNC(opt_language)
2912 struct language *lang;
2914 lang = language_find(argv[1]);
2915 if (irccasecmp(lang->name, argv[1]))
2916 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2917 hi->language = lang;
2919 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2923 static OPTION_FUNC(opt_karma)
2926 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2931 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2932 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2933 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2934 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2936 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2940 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2945 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2946 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2948 if ((user->handle_info->opserv_level < target->opserv_level)
2949 || ((user->handle_info->opserv_level == target->opserv_level)
2950 && (user->handle_info->opserv_level < 1000))) {
2951 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2954 if ((user->handle_info->opserv_level < new_level)
2955 || ((user->handle_info->opserv_level == new_level)
2956 && (user->handle_info->opserv_level < 1000))) {
2957 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2960 if (user->handle_info == target) {
2961 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2964 if (target->opserv_level == new_level)
2966 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2967 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2968 target->opserv_level = new_level;
2973 oper_try_set_staff_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2974 if (!oper_has_access(user, bot, nickserv_conf.modstaff_level, 0))
2976 if ((user->handle_info->opserv_level < target->opserv_level)
2977 || ((user->handle_info->opserv_level == target->opserv_level)
2978 && (user->handle_info->opserv_level < 1000))) {
2979 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2982 if (target->staff_level == new_level)
2984 log_module(NS_LOG, LOG_INFO, "Account %s setting staff level for account %s to %d (from %d).",
2985 user->handle_info->handle, target->handle, new_level, target->staff_level);
2986 target->staff_level = new_level;
2990 static OPTION_FUNC(opt_level)
2995 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2999 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
3000 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
3004 static OPTION_FUNC(opt_staff_level)
3009 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3013 res = (argc > 1) ? oper_try_set_staff_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
3014 send_message(user, nickserv, "NSMSG_SET_STAFFLEVEL", hi->staff_level);
3018 static OPTION_FUNC(opt_epithet)
3021 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3025 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
3026 char *epithet = unsplit_string(argv+1, argc-1, NULL);
3029 if ((epithet[0] == '*') && !epithet[1])
3032 hi->epithet = strdup(epithet);
3036 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
3038 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
3042 static OPTION_FUNC(opt_title)
3047 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3051 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
3053 if (strchr(title, '.')) {
3054 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
3057 if ((strlen(user->handle_info->handle) + strlen(title) +
3058 strlen(titlehost_suffix) + 2) > HOSTLEN) {
3059 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
3064 if (!strcmp(title, "*")) {
3065 hi->fakehost = NULL;
3067 hi->fakehost = malloc(strlen(title)+2);
3068 hi->fakehost[0] = '.';
3069 strcpy(hi->fakehost+1, title);
3071 apply_fakehost(hi, NULL);
3072 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
3073 title = hi->fakehost + 1;
3077 title = user_find_message(user, "MSG_NONE");
3078 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
3082 static OPTION_FUNC(opt_fakehost)
3084 char mask[USERLEN + HOSTLEN + 2];
3088 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3092 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
3093 if(strlen(argv[1]) >= sizeof(mask)) {
3094 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
3098 safestrncpy(mask, argv[1], sizeof(mask));
3100 if ((host = strrchr(mask, '@')) && host != mask) {
3101 /* If ident@host was used and the user doesn't have access to set idents, do not change anything. */
3102 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3114 if (ident && strlen(ident) > USERLEN) {
3115 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3119 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
3120 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
3124 if (host && host[0]) {
3126 if (!strcmp(host, "*"))
3127 hi->fakehost = NULL;
3129 hi->fakehost = strdup(host);
3130 host = hi->fakehost;
3133 host = generate_fakehost(hi);
3136 free(hi->fakeident);
3137 if (!strcmp(ident, "*"))
3138 hi->fakeident = NULL;
3140 hi->fakeident = strdup(ident);
3141 ident = hi->fakeident;
3144 ident = generate_fakeident(hi, NULL);
3146 apply_fakehost(hi, NULL);
3148 host = generate_fakehost(hi);
3149 ident = generate_fakeident(hi, NULL);
3152 host = (char *) user_find_message(user, "MSG_NONE");
3154 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
3156 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
3160 static OPTION_FUNC(opt_fakeident)
3165 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3169 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3171 if (strlen(ident) > USERLEN) {
3172 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3175 free(hi->fakeident);
3176 if (!strcmp(ident, "*"))
3177 hi->fakeident = NULL;
3179 hi->fakeident = strdup(ident);
3180 ident = hi->fakeident;
3181 apply_fakehost(hi, NULL);
3183 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
3185 ident = user_find_message(user, "MSG_NONE");
3186 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
3190 static NICKSERV_FUNC(cmd_reclaim)
3192 struct nick_info *ni;
3193 struct userNode *victim;
3195 NICKSERV_MIN_PARMS(2);
3196 ni = dict_find(nickserv_nick_dict, argv[1], 0);
3198 reply("NSMSG_UNKNOWN_NICK", argv[1]);
3201 if (ni->owner != user->handle_info) {
3202 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
3205 victim = GetUserH(ni->nick);
3207 reply("MSG_NICK_UNKNOWN", ni->nick);
3210 if (victim == user) {
3211 reply("NSMSG_NICK_USER_YOU");
3214 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
3215 switch (nickserv_conf.reclaim_action) {
3216 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
3217 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
3218 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
3219 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
3224 static NICKSERV_FUNC(cmd_unregnick)
3227 struct handle_info *hi;
3228 struct nick_info *ni;
3230 hi = user->handle_info;
3231 nick = (argc < 2) ? user->nick : (const char*)argv[1];
3232 ni = dict_find(nickserv_nick_dict, nick, NULL);
3234 reply("NSMSG_UNKNOWN_NICK", nick);
3237 if (hi != ni->owner) {
3238 reply("NSMSG_NOT_YOUR_NICK", nick);
3241 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3246 static NICKSERV_FUNC(cmd_ounregnick)
3248 struct nick_info *ni;
3250 NICKSERV_MIN_PARMS(2);
3251 if (!(ni = get_nick_info(argv[1]))) {
3252 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
3255 if (!oper_outranks(user, ni->owner))
3257 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3262 static NICKSERV_FUNC(cmd_unregister)
3264 struct handle_info *hi;
3267 NICKSERV_MIN_PARMS(2);
3268 hi = user->handle_info;
3271 if (checkpass(passwd, hi->passwd)) {
3272 nickserv_unregister_handle(hi, user);
3275 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3276 reply("NSMSG_PASSWORD_INVALID");
3281 static NICKSERV_FUNC(cmd_ounregister)
3283 struct handle_info *hi;
3284 char reason[MAXLEN];
3287 NICKSERV_MIN_PARMS(2);
3288 if (!(hi = get_victim_oper(user, argv[1])))
3291 if (HANDLE_FLAGGED(hi, NODELETE)) {
3292 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
3296 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
3298 ((hi->flags & nickserv_conf.ounregister_flags)
3300 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
3301 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
3305 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
3306 global_message(MESSAGE_RECIPIENT_STAFF, reason);
3307 nickserv_unregister_handle(hi, user);
3311 static NICKSERV_FUNC(cmd_status)
3313 if (nickserv_conf.disable_nicks) {
3314 reply("NSMSG_GLOBAL_STATS_NONICK",
3315 dict_size(nickserv_handle_dict));
3317 if (user->handle_info) {
3319 struct nick_info *ni;
3320 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
3321 reply("NSMSG_HANDLE_STATS", cnt);
3323 reply("NSMSG_HANDLE_NONE");
3325 reply("NSMSG_GLOBAL_STATS",
3326 dict_size(nickserv_handle_dict),
3327 dict_size(nickserv_nick_dict));
3332 static NICKSERV_FUNC(cmd_ghost)
3334 struct userNode *target;
3335 char reason[MAXLEN];
3337 NICKSERV_MIN_PARMS(2);
3338 if (!(target = GetUserH(argv[1]))) {
3339 reply("MSG_NICK_UNKNOWN", argv[1]);
3342 if (target == user) {
3343 reply("NSMSG_CANNOT_GHOST_SELF");
3346 if (!target->handle_info || (target->handle_info != user->handle_info)) {
3347 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
3350 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
3351 DelUser(target, nickserv, 1, reason);
3352 reply("NSMSG_GHOST_KILLED", argv[1]);
3356 static NICKSERV_FUNC(cmd_vacation)
3358 HANDLE_SET_FLAG(user->handle_info, FROZEN);
3359 reply("NSMSG_ON_VACATION");
3363 static NICKSERV_FUNC(cmd_addnote)
3365 struct handle_info *hi;
3366 unsigned long duration;
3369 struct handle_note *prev;
3370 struct handle_note *note;
3372 /* Parse parameters and figure out values for note's fields. */
3373 NICKSERV_MIN_PARMS(4);
3374 hi = get_victim_oper(user, argv[1]);
3377 if(!strcmp(argv[2], "0"))
3379 else if(!(duration = ParseInterval(argv[2])))
3381 reply("MSG_INVALID_DURATION", argv[2]);
3384 if (duration > 2*365*86400) {
3385 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
3388 unsplit_string(argv + 3, argc - 3, text);
3389 WALK_NOTES(hi, prev, note) {}
3390 id = prev ? (prev->id + 1) : 1;
3392 /* Create the new note structure. */
3393 note = calloc(1, sizeof(*note) + strlen(text));
3395 note->expires = duration ? (now + duration) : 0;
3398 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
3399 strcpy(note->note, text);
3404 reply("NSMSG_NOTE_ADDED", id, hi->handle);
3408 static NICKSERV_FUNC(cmd_delnote)
3410 struct handle_info *hi;
3411 struct handle_note *prev;
3412 struct handle_note *note;
3415 NICKSERV_MIN_PARMS(3);
3416 hi = get_victim_oper(user, argv[1]);
3419 id = strtoul(argv[2], NULL, 10);
3420 WALK_NOTES(hi, prev, note) {
3421 if (id == note->id) {
3423 prev->next = note->next;
3425 hi->notes = note->next;
3427 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3431 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3436 nickserv_saxdb_write(struct saxdb_context *ctx) {
3438 struct handle_info *hi;
3441 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3443 assert(hi->id != 0);
3444 saxdb_start_record(ctx, iter_key(it), 0);
3446 struct handle_cookie *cookie = hi->cookie;
3449 switch (cookie->type) {
3450 case ACTIVATION: type = KEY_ACTIVATION; break;
3451 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3452 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3453 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3454 default: type = NULL; break;
3457 saxdb_start_record(ctx, KEY_COOKIE, 0);
3458 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3459 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3461 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3462 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3463 saxdb_end_record(ctx);
3467 struct handle_note *prev, *note;
3468 saxdb_start_record(ctx, KEY_NOTES, 0);
3469 WALK_NOTES(hi, prev, note) {
3470 snprintf(flags, sizeof(flags), "%d", note->id);
3471 saxdb_start_record(ctx, flags, 0);
3473 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3474 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3475 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3476 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3477 saxdb_end_record(ctx);
3479 saxdb_end_record(ctx);
3482 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3484 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3486 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3488 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3492 for (ii=flen=0; handle_flags[ii]; ++ii)
3493 if (hi->flags & (1 << ii))
3494 flags[flen++] = handle_flags[ii];
3496 saxdb_write_string(ctx, KEY_FLAGS, flags);
3498 saxdb_write_int(ctx, KEY_ID, hi->id);
3500 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3502 saxdb_write_string(ctx, KEY_DEVNULL, hi->devnull);
3504 saxdb_write_string(ctx, KEY_WEBSITE, hi->website);
3505 if (hi->last_quit_host[0])
3506 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3507 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3509 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3510 if (hi->masks->used)
3511 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3513 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3515 struct string_list *slist;
3516 struct nick_info *ni;
3518 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3519 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3520 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3524 if (hi->opserv_level)
3525 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3526 if (hi->staff_level)
3527 saxdb_write_int(ctx, KEY_STAFF_LEVEL, hi->staff_level);
3528 if (hi->language != lang_C)
3529 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3530 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3531 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3532 if (hi->screen_width)
3533 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3534 if (hi->table_width)
3535 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3536 flags[0] = hi->userlist_style;
3538 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3540 saxdb_start_record(ctx, KEY_AUTHLOG, 0);
3541 struct authlogEntry *authlog;
3543 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
3544 saxdb_start_record(ctx, strtab(++i), 0);
3545 saxdb_write_int(ctx, KEY_AUTHLOG_LOGIN_TIME, authlog->login_time);
3546 saxdb_write_int(ctx, KEY_AUTHLOG_LOGOUT_TIME, authlog->logout_time);
3547 saxdb_write_string(ctx, KEY_AUTHLOG_HOSTMASK, authlog->hostmask);
3548 if(authlog->quit_reason)
3549 saxdb_write_string(ctx, KEY_AUTHLOG_QUIT_REASON, authlog->quit_reason);
3550 saxdb_end_record(ctx);
3552 saxdb_end_record(ctx); //END KEY_AUTHLOG
3554 saxdb_end_record(ctx);
3559 static handle_merge_func_t *handle_merge_func_list;
3560 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3563 reg_handle_merge_func(handle_merge_func_t func)
3565 if (handle_merge_func_used == handle_merge_func_size) {
3566 if (handle_merge_func_size) {
3567 handle_merge_func_size <<= 1;
3568 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3570 handle_merge_func_size = 8;
3571 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3574 handle_merge_func_list[handle_merge_func_used++] = func;
3577 static NICKSERV_FUNC(cmd_merge)
3579 struct handle_info *hi_from, *hi_to;
3580 struct userNode *last_user;
3581 struct userData *cList, *cListNext;
3582 unsigned int ii, jj, n;
3583 char buffer[MAXLEN];
3585 NICKSERV_MIN_PARMS(3);
3587 if (!(hi_from = get_victim_oper(user, argv[1])))
3589 if (!(hi_to = get_victim_oper(user, argv[2])))
3591 if (hi_to == hi_from) {
3592 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3596 for (n=0; n<handle_merge_func_used; n++)
3597 handle_merge_func_list[n](user, hi_to, hi_from);
3599 /* Append "from" handle's nicks to "to" handle's nick list. */
3601 struct nick_info *last_ni;
3602 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3603 last_ni->next = hi_from->nicks;
3605 while (hi_from->nicks) {
3606 hi_from->nicks->owner = hi_to;
3607 hi_from->nicks = hi_from->nicks->next;
3610 /* Merge the hostmasks. */
3611 for (ii=0; ii<hi_from->masks->used; ii++) {
3612 char *mask = hi_from->masks->list[ii];
3613 for (jj=0; jj<hi_to->masks->used; jj++)
3614 if (match_ircglobs(hi_to->masks->list[jj], mask))
3616 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3617 string_list_append(hi_to->masks, strdup(mask));
3620 /* Merge the lists of authed users. */
3622 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3623 last_user->next_authed = hi_from->users;
3625 hi_to->users = hi_from->users;
3627 /* Repoint the old "from" handle's users. */
3628 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3629 last_user->handle_info = hi_to;
3631 hi_from->users = NULL;
3633 /* Merge channel userlists. */
3634 for (cList=hi_from->channels; cList; cList=cListNext) {
3635 struct userData *cList2;
3636 cListNext = cList->u_next;
3637 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3638 if (cList->channel == cList2->channel)
3640 if (cList2 && (cList2->access >= cList->access)) {
3641 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);
3642 /* keep cList2 in hi_to; remove cList from hi_from */
3643 del_channel_user(cList, 1);
3646 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);
3647 /* remove the lower-ranking cList2 from hi_to */
3648 del_channel_user(cList2, 1);
3650 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3652 /* cList needs to be moved from hi_from to hi_to */
3653 cList->handle = hi_to;
3654 /* Remove from linked list for hi_from */
3655 assert(!cList->u_prev);
3656 hi_from->channels = cList->u_next;
3658 cList->u_next->u_prev = cList->u_prev;
3659 /* Add to linked list for hi_to */
3660 cList->u_prev = NULL;
3661 cList->u_next = hi_to->channels;
3662 if (hi_to->channels)
3663 hi_to->channels->u_prev = cList;
3664 hi_to->channels = cList;
3668 /* Do they get an OpServ level promotion? */
3669 if (hi_from->opserv_level > hi_to->opserv_level)
3670 hi_to->opserv_level = hi_from->opserv_level;
3672 /* Do they get a staff level promotion? */
3673 if (hi_from->staff_level > hi_to->staff_level)
3674 hi_to->staff_level = hi_from->staff_level;
3676 /* What about last seen time? */
3677 if (hi_from->lastseen > hi_to->lastseen)
3678 hi_to->lastseen = hi_from->lastseen;
3680 /* New karma is the sum of the two original karmas. */
3681 hi_to->karma += hi_from->karma;
3683 /* Does a fakehost carry over? (This intentionally doesn't set it
3684 * for users previously attached to hi_to. They'll just have to
3687 if (hi_from->fakehost && !hi_to->fakehost)
3688 hi_to->fakehost = strdup(hi_from->fakehost);
3689 if (hi_from->fakeident && !hi_to->fakeident)
3690 hi_to->fakeident = strdup(hi_from->fakeident);
3692 /* Notify of success. */
3693 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3694 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3695 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3697 /* Unregister the "from" handle. */
3698 nickserv_unregister_handle(hi_from, NULL);
3703 #define NICKSERV_DISCRIM_FIELDS_AUTH 0x01
3704 #define NICKSERV_DISCRIM_FIELDS_EMAIL 0x02
3705 #define NICKSERV_DISCRIM_FIELDS_SEEN 0x04
3706 #define NICKSERV_DISCRIM_FIELDS_ACCESS 0x08
3707 #define NICKSERV_DISCRIM_FIELDS_FAKEHOST 0x10
3708 #define NICKSERV_DISCRIM_FIELDS_WEBSITE 0x20
3709 #define NICKSERV_DISCRIM_FIELDS_DEVNULL 0x40
3711 #define NICKSERV_DISCRIM_FIELD_COUNT 7
3713 struct nickserv_discrim {
3714 unsigned int show_fields;
3715 struct helpfile_table *output_table;
3716 int output_table_pos;
3717 unsigned int output_table_free_fields;
3719 unsigned long flags_on, flags_off;
3720 unsigned long min_registered, max_registered;
3721 unsigned long lastseen;
3723 int min_level, max_level;
3724 int min_karma, max_karma;
3725 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3726 const char *nickmask;
3727 const char *hostmask;
3728 const char *fakehostmask;
3729 const char *fakeidentmask;
3730 const char *website;
3731 const char *devnullclass;
3732 const char *handlemask;
3733 const char *emailmask;
3736 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi, struct nickserv_discrim *discrim);
3738 struct discrim_apply_info {
3739 struct nickserv_discrim *discrim;
3740 discrim_search_func func;
3741 struct userNode *source;
3742 unsigned int matched;
3745 static struct nickserv_discrim *
3746 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3749 struct nickserv_discrim *discrim;
3751 discrim = malloc(sizeof(*discrim));
3752 memset(discrim, 0, sizeof(*discrim));
3753 discrim->min_level = 0;
3754 discrim->max_level = INT_MAX;
3755 discrim->limit = 50;
3756 discrim->min_registered = 0;
3757 discrim->max_registered = ULONG_MAX;
3758 discrim->lastseen = ULONG_MAX;
3759 discrim->min_karma = INT_MIN;
3760 discrim->max_karma = INT_MAX;
3762 for (i=0; i<argc; i++) {
3763 if (i == argc - 1) {
3764 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3767 if (!irccasecmp(argv[i], "limit")) {
3768 discrim->limit = strtoul(argv[++i], NULL, 0);
3769 } else if (!irccasecmp(argv[i], "flags")) {
3770 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3771 } else if (!irccasecmp(argv[i], "fields")) {
3772 char *fields = argv[++i];
3773 char *delimiter = strstr(fields, ",");
3777 if(!irccasecmp(fields, "auth"))
3778 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_AUTH;
3779 else if(!irccasecmp(fields, "email"))
3780 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_EMAIL;
3781 else if(!irccasecmp(fields, "seen"))
3782 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_SEEN;
3783 else if(!irccasecmp(fields, "access"))
3784 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_ACCESS;
3785 else if(!irccasecmp(fields, "fakehost"))
3786 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_FAKEHOST;
3787 else if(!irccasecmp(fields, "website") && IsBot(user))
3788 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_WEBSITE;
3789 else if(!irccasecmp(fields, "devnull"))
3790 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_DEVNULL;
3792 send_message(user, nickserv, "MSG_INVALID_FIELD", fields);
3797 fields = delimiter+1;
3799 delimiter = strstr(fields, ",");
3805 } else if (!irccasecmp(argv[i], "registered")) {
3806 const char *cmp = argv[++i];
3807 if (cmp[0] == '<') {
3808 if (cmp[1] == '=') {
3809 discrim->min_registered = now - ParseInterval(cmp+2);
3811 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3813 } else if (cmp[0] == '=') {
3814 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3815 } else if (cmp[0] == '>') {
3816 if (cmp[1] == '=') {
3817 discrim->max_registered = now - ParseInterval(cmp+2);
3819 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3822 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3824 } else if (!irccasecmp(argv[i], "seen")) {
3825 discrim->lastseen = now - ParseInterval(argv[++i]);
3826 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3827 discrim->nickmask = argv[++i];
3828 } else if (!irccasecmp(argv[i], "hostmask")) {
3830 if (!irccasecmp(argv[i], "exact")) {
3831 if (i == argc - 1) {
3832 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3835 discrim->hostmask_type = EXACT;
3836 } else if (!irccasecmp(argv[i], "subset")) {
3837 if (i == argc - 1) {
3838 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3841 discrim->hostmask_type = SUBSET;
3842 } else if (!irccasecmp(argv[i], "superset")) {
3843 if (i == argc - 1) {
3844 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3847 discrim->hostmask_type = SUPERSET;
3848 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3849 if (i == argc - 1) {
3850 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3853 discrim->hostmask_type = LASTQUIT;
3856 discrim->hostmask_type = SUPERSET;
3858 discrim->hostmask = argv[++i];
3859 } else if (!irccasecmp(argv[i], "fakehost")) {
3860 if (!irccasecmp(argv[++i], "*")) {
3861 discrim->fakehostmask = 0;
3863 discrim->fakehostmask = argv[i];
3865 } else if (!irccasecmp(argv[i], "fakeident")) {
3866 if (!irccasecmp(argv[++i], "*")) {
3867 discrim->fakeidentmask = 0;
3869 discrim->fakeidentmask = argv[i];
3871 } else if (!irccasecmp(argv[i], "website")) {
3872 if (!irccasecmp(argv[++i], "*")) {
3873 discrim->website = 0;
3875 discrim->website = argv[i];
3877 } else if (!irccasecmp(argv[i], "devnull")) {
3878 if (!irccasecmp(argv[++i], "*")) {
3879 discrim->devnullclass = 0;
3881 discrim->devnullclass = argv[i];
3883 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3884 if (!irccasecmp(argv[++i], "*")) {
3885 discrim->handlemask = 0;
3887 discrim->handlemask = argv[i];
3889 } else if (!irccasecmp(argv[i], "email")) {
3890 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3891 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3893 } else if (!irccasecmp(argv[++i], "*")) {
3894 discrim->emailmask = 0;
3896 discrim->emailmask = argv[i];
3898 } else if (!irccasecmp(argv[i], "access")) {
3899 const char *cmp = argv[++i];
3900 if (cmp[0] == '<') {
3901 if (discrim->min_level == 0) discrim->min_level = 1;
3902 if (cmp[1] == '=') {
3903 discrim->max_level = strtoul(cmp+2, NULL, 0);
3905 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3907 } else if (cmp[0] == '=') {
3908 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3909 } else if (cmp[0] == '>') {
3910 if (cmp[1] == '=') {
3911 discrim->min_level = strtoul(cmp+2, NULL, 0);
3913 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3916 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3918 } else if (!irccasecmp(argv[i], "karma")) {
3919 const char *cmp = argv[++i];
3920 if (cmp[0] == '<') {
3921 if (cmp[1] == '=') {
3922 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3924 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3926 } else if (cmp[0] == '=') {
3927 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3928 } else if (cmp[0] == '>') {
3929 if (cmp[1] == '=') {
3930 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3932 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3935 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3938 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3949 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3951 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3952 || (discrim->flags_off & hi->flags)
3953 || (discrim->min_registered > hi->registered)
3954 || (discrim->max_registered < hi->registered)
3955 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3956 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3957 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3958 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3959 || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website)))
3960 || (discrim->devnullclass && (!hi->devnull || !match_ircglob(hi->devnull, discrim->devnullclass)))
3961 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3962 || (discrim->min_level > hi->opserv_level)
3963 || (discrim->max_level < hi->opserv_level)
3964 || (discrim->min_karma > hi->karma)
3965 || (discrim->max_karma < hi->karma)
3969 if (discrim->hostmask) {
3971 for (i=0; i<hi->masks->used; i++) {
3972 const char *mask = hi->masks->list[i];
3973 if ((discrim->hostmask_type == SUBSET)
3974 && (match_ircglobs(discrim->hostmask, mask))) break;
3975 else if ((discrim->hostmask_type == EXACT)
3976 && !irccasecmp(discrim->hostmask, mask)) break;
3977 else if ((discrim->hostmask_type == SUPERSET)
3978 && (match_ircglobs(mask, discrim->hostmask))) break;
3979 else if ((discrim->hostmask_type == LASTQUIT)
3980 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3982 if (i==hi->masks->used) return 0;
3984 if (discrim->nickmask) {
3985 struct nick_info *nick = hi->nicks;
3987 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3990 if (!nick) return 0;
3996 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3998 dict_iterator_t it, next;
3999 unsigned int matched;
4001 for (it = dict_first(nickserv_handle_dict), matched = 0;
4002 it && (matched < discrim->limit);
4004 next = iter_next(it);
4005 if (nickserv_discrim_match(discrim, iter_data(it))) {
4006 dsf(source, iter_data(it), discrim);
4014 search_print_func(struct userNode *source, struct handle_info *match, struct nickserv_discrim *discrim)
4016 if(discrim->show_fields) {
4018 if(discrim->output_table) {
4019 discrim->output_table->contents[++discrim->output_table_pos] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
4021 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
4022 discrim->output_table->contents[discrim->output_table_pos][i++] = match->handle;
4023 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
4024 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->email_addr ? match->email_addr : "*");
4025 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN) {
4027 char seenBuf[INTERVALLEN];
4030 } else if(match->lastseen == 0) {
4033 seen = intervalString(seenBuf, now - match->lastseen, source->handle_info);
4035 discrim->output_table_free_fields |= 1 << i;
4036 discrim->output_table->contents[discrim->output_table_pos][i++] = strdup(seen);
4038 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
4039 discrim->output_table->contents[discrim->output_table_pos][i++] = strtab(match->opserv_level);
4040 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
4041 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->fakehost ? match->fakehost : "*");
4042 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
4043 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->website ? match->website : "*");
4044 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
4045 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->devnull ? match->devnull : "*");
4049 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
4053 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match), UNUSED_ARG(struct nickserv_discrim *discrim))
4058 search_unregister_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
4060 if (oper_has_access(source, nickserv, match->opserv_level, 0))
4061 nickserv_unregister_handle(match, source);
4065 nickserv_sort_accounts_by_access(const void *a, const void *b)
4067 const struct handle_info *hi_a = *(const struct handle_info**)a;
4068 const struct handle_info *hi_b = *(const struct handle_info**)b;
4069 if (hi_a->opserv_level != hi_b->opserv_level)
4070 return hi_b->opserv_level - hi_a->opserv_level;
4071 return irccasecmp(hi_a->handle, hi_b->handle);
4075 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
4077 struct handle_info_list hil;
4078 struct helpfile_table tbl;
4083 memset(&hil, 0, sizeof(hil));
4084 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
4085 struct handle_info *hi = iter_data(it);
4086 if (hi->opserv_level)
4087 handle_info_list_append(&hil, hi);
4089 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
4090 tbl.length = hil.used + 1;
4092 tbl.flags = TABLE_NO_FREE;
4093 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4094 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
4097 for (ii = 0; ii < hil.used; ) {
4098 ary = malloc(tbl.width * sizeof(ary[0]));
4099 ary[0] = hil.list[ii]->handle;
4100 ary[1] = strtab(hil.list[ii]->opserv_level);
4101 tbl.contents[++ii] = ary;
4103 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4104 reply("MSG_MATCH_COUNT", hil.used);
4105 for (ii = 0; ii < hil.used; ii++)
4106 free(tbl.contents[ii]);
4111 static NICKSERV_FUNC(cmd_search)
4113 struct nickserv_discrim *discrim;
4114 discrim_search_func action;
4115 struct svccmd *subcmd;
4116 unsigned int matches;
4119 NICKSERV_MIN_PARMS(3);
4120 sprintf(buf, "search %s", argv[1]);
4121 subcmd = dict_find(nickserv_service->commands, buf, NULL);
4122 if (!irccasecmp(argv[1], "print"))
4123 action = search_print_func;
4124 else if (!irccasecmp(argv[1], "count"))
4125 action = search_count_func;
4126 else if (!irccasecmp(argv[1], "unregister"))
4127 action = search_unregister_func;
4129 reply("NSMSG_INVALID_ACTION", argv[1]);
4133 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
4136 discrim = nickserv_discrim_create(user, argc-2, argv+2);
4140 if (action == search_print_func)
4141 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
4142 else if (action == search_count_func)
4143 discrim->limit = INT_MAX;
4145 matches = nickserv_discrim_search(discrim, action, user);
4147 if(discrim->show_fields) {
4150 for(ii = 0; ii < NICKSERV_DISCRIM_FIELD_COUNT; ii++) {
4151 if(discrim->show_fields & (1 << ii)) width++;
4153 discrim->output_table = malloc(sizeof(discrim->output_table[0]));
4154 discrim->output_table->length = matches+1;
4155 discrim->output_table->width = width;
4156 discrim->output_table->flags = TABLE_NO_FREE;
4157 discrim->output_table->contents = malloc(discrim->output_table->length * sizeof(discrim->output_table->contents[0]));
4158 discrim->output_table->contents[0] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
4161 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
4162 discrim->output_table->contents[0][ii++] = "Auth";
4163 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
4164 discrim->output_table->contents[0][ii++] = "EMail";
4165 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN)
4166 discrim->output_table->contents[0][ii++] = "Seen";
4167 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
4168 discrim->output_table->contents[0][ii++] = "Access";
4169 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
4170 discrim->output_table->contents[0][ii++] = "Fakehost";
4171 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
4172 discrim->output_table->contents[0][ii++] = "Website";
4173 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
4174 discrim->output_table->contents[0][ii++] = "DevNull";
4176 nickserv_discrim_search(discrim, action, user);
4178 table_send(nickserv, user->nick, 0, NULL, *discrim->output_table);
4180 for(ii = 1; ii < discrim->output_table->length; ++ii) {
4182 for(ij = 0; ij < NICKSERV_DISCRIM_FIELD_COUNT; ij++) {
4183 if(discrim->output_table_free_fields & (1 << ij))
4184 free((char*)discrim->output_table->contents[ii][ij]);
4186 free(discrim->output_table->contents[ii]);
4188 free(discrim->output_table->contents[0]);
4189 free(discrim->output_table->contents);
4190 free(discrim->output_table);
4193 reply("MSG_MATCH_COUNT", matches);
4195 reply("MSG_NO_MATCHES");
4202 static MODCMD_FUNC(cmd_checkpass)
4204 struct handle_info *hi;
4206 NICKSERV_MIN_PARMS(3);
4207 if (!(hi = get_handle_info(argv[1]))) {
4208 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4211 if (checkpass(argv[2], hi->passwd))
4212 reply("CHECKPASS_YES");
4214 reply("CHECKPASS_NO");
4219 static MODCMD_FUNC(cmd_checkemail)
4221 struct handle_info *hi;
4223 NICKSERV_MIN_PARMS(3);
4224 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
4227 if (!hi->email_addr)
4228 reply("CHECKEMAIL_NOT_SET");
4229 else if (!irccasecmp(argv[2], hi->email_addr))
4230 reply("CHECKEMAIL_YES");
4232 reply("CHECKEMAIL_NO");
4237 nickserv_db_read_authlog(UNUSED_ARG(const char *key), void *data, void *extra)
4239 struct record_data *rd = data;
4240 struct handle_info *hi = extra;
4242 struct authlogEntry *authlog;
4243 authlog = malloc(sizeof(*authlog));
4245 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGIN_TIME, RECDB_QSTRING);
4246 authlog->login_time = str ? strtoul(str, NULL, 0) : 0;
4248 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGOUT_TIME, RECDB_QSTRING);
4249 authlog->logout_time = str ? strtoul(str, NULL, 0) : 0;
4251 str = database_get_data(rd->d.object, KEY_AUTHLOG_HOSTMASK, RECDB_QSTRING);
4252 authlog->hostmask = str ? strdup(str) : NULL;
4254 str = database_get_data(rd->d.object, KEY_AUTHLOG_QUIT_REASON, RECDB_QSTRING);
4255 authlog->quit_reason = str ? strdup(str) : NULL;
4257 authlog->user = NULL;
4259 authlog->next = NULL;
4261 //append it to the end of the list...
4262 struct authlogEntry *authlog_entry;
4264 hi->authlog = authlog;
4266 for(authlog_entry = hi->authlog; authlog_entry; authlog_entry = authlog_entry->next) {
4267 if(!authlog_entry->next) {
4268 authlog_entry->next = authlog;
4277 nickserv_db_read_handle(const char *handle, dict_t obj)
4280 struct string_list *masks, *slist;
4281 struct handle_info *hi;
4282 struct userNode *authed_users;
4283 struct userData *channel_list;
4288 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
4289 id = str ? strtoul(str, NULL, 0) : 0;
4290 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
4292 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
4295 if ((hi = get_handle_info(handle))) {
4296 authed_users = hi->users;
4297 channel_list = hi->channels;
4299 hi->channels = NULL;
4300 dict_remove(nickserv_handle_dict, hi->handle);
4302 authed_users = NULL;
4303 channel_list = NULL;
4305 hi = register_handle(handle, str, id);
4307 hi->users = authed_users;
4308 while (authed_users) {
4309 authed_users->handle_info = hi;
4310 authed_users = authed_users->next_authed;
4313 hi->channels = channel_list;
4314 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
4315 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
4316 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
4317 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
4318 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
4319 hi->language = language_find(str ? str : "C");
4320 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
4321 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
4322 str = database_get_data(obj, KEY_STAFF_LEVEL, RECDB_QSTRING);
4323 hi->staff_level = str ? strtoul(str, NULL, 0) : 0;
4324 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
4326 hi->infoline = strdup(str);
4327 str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING);
4329 hi->website = strdup(str);
4330 str = database_get_data(obj, KEY_DEVNULL, RECDB_QSTRING);
4332 hi->devnull = strdup(str);
4333 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
4334 hi->registered = str ? strtoul(str, NULL, 0) : now;
4335 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
4336 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
4337 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
4338 hi->karma = str ? strtoul(str, NULL, 0) : 0;
4339 /* We want to read the nicks even if disable_nicks is set. This is so
4340 * that we don't lose the nick data entirely. */
4341 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
4343 for (ii=0; ii<slist->used; ii++)
4344 register_nick(slist->list[ii], hi);
4346 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
4348 for (ii=0; str[ii]; ii++)
4349 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
4351 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
4352 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
4353 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
4354 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
4355 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
4356 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
4357 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
4359 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
4361 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
4362 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
4364 nickserv_set_email_addr(hi, str);
4365 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
4367 hi->epithet = strdup(str);
4368 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
4370 hi->fakehost = strdup(str);
4371 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
4373 hi->fakeident = strdup(str);
4374 /* Read the "cookie" sub-database (if it exists). */
4375 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
4377 const char *data, *type, *expires, *cookie_str;
4378 struct handle_cookie *cookie;
4380 cookie = calloc(1, sizeof(*cookie));
4381 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
4382 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
4383 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
4384 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
4385 if (!type || !expires || !cookie_str) {
4386 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
4389 if (!irccasecmp(type, KEY_ACTIVATION))
4390 cookie->type = ACTIVATION;
4391 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
4392 cookie->type = PASSWORD_CHANGE;
4393 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
4394 cookie->type = EMAIL_CHANGE;
4395 else if (!irccasecmp(type, KEY_ALLOWAUTH))
4396 cookie->type = ALLOWAUTH;
4398 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
4401 cookie->expires = strtoul(expires, NULL, 0);
4402 if (cookie->expires < now)
4405 cookie->data = strdup(data);
4406 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
4410 nickserv_bake_cookie(cookie);
4412 nickserv_free_cookie(cookie);
4414 /* Read the "notes" sub-database (if it exists). */
4415 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
4418 struct handle_note *last_note;
4419 struct handle_note *note;
4422 for (it = dict_first(subdb); it; it = iter_next(it)) {
4423 const char *expires;
4427 const char *note_id;
4430 note_id = iter_key(it);
4431 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
4433 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
4436 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
4437 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
4438 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
4439 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
4440 if (!setter || !text || !set) {
4441 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
4444 note = calloc(1, sizeof(*note) + strlen(text));
4446 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
4447 note->set = strtoul(set, NULL, 10);
4448 note->id = strtoul(note_id, NULL, 10);
4449 safestrncpy(note->setter, setter, sizeof(note->setter));
4450 strcpy(note->note, text);
4452 last_note->next = note;
4458 if ((subdb = database_get_data(obj, KEY_AUTHLOG, RECDB_OBJECT)))
4459 dict_foreach(subdb, nickserv_db_read_authlog, hi);
4463 nickserv_saxdb_read(dict_t db) {
4465 struct record_data *rd;
4467 for (it=dict_first(db); it; it=iter_next(it)) {
4469 nickserv_db_read_handle(iter_key(it), rd->d.object);
4474 static NICKSERV_FUNC(cmd_mergedb)
4476 struct timeval start, stop;
4479 NICKSERV_MIN_PARMS(2);
4480 gettimeofday(&start, NULL);
4481 if (!(db = parse_database(argv[1]))) {
4482 reply("NSMSG_DB_UNREADABLE", argv[1]);
4485 nickserv_saxdb_read(db);
4487 gettimeofday(&stop, NULL);
4488 stop.tv_sec -= start.tv_sec;
4489 stop.tv_usec -= start.tv_usec;
4490 if (stop.tv_usec < 0) {
4492 stop.tv_usec += 1000000;
4494 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
4499 expire_handles(UNUSED_ARG(void *data))
4501 dict_iterator_t it, next;
4502 unsigned long expiry;
4503 struct handle_info *hi;
4505 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4506 next = iter_next(it);
4508 if ((hi->opserv_level > 0)
4510 || HANDLE_FLAGGED(hi, FROZEN)
4511 || HANDLE_FLAGGED(hi, NODELETE)) {
4514 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4515 if ((now - hi->lastseen) > expiry) {
4516 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
4517 nickserv_unregister_handle(hi, NULL);
4521 if (nickserv_conf.handle_expire_frequency)
4522 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4526 nickserv_load_dict(const char *fname)
4530 if (!(file = fopen(fname, "r"))) {
4531 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4534 while (fgets(line, sizeof(line), file)) {
4537 if (line[strlen(line)-1] == '\n')
4538 line[strlen(line)-1] = 0;
4539 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4542 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4545 static enum reclaim_action
4546 reclaim_action_from_string(const char *str) {
4548 return RECLAIM_NONE;
4549 else if (!irccasecmp(str, "warn"))
4550 return RECLAIM_WARN;
4551 else if (!irccasecmp(str, "svsnick"))
4552 return RECLAIM_SVSNICK;
4553 else if (!irccasecmp(str, "kill"))
4554 return RECLAIM_KILL;
4556 return RECLAIM_NONE;
4560 nickserv_conf_read(void)
4562 dict_t conf_node, child;
4566 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4567 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4570 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4572 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4573 if (nickserv_conf.valid_handle_regex_set)
4574 regfree(&nickserv_conf.valid_handle_regex);
4576 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4577 nickserv_conf.valid_handle_regex_set = !err;
4578 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4580 nickserv_conf.valid_handle_regex_set = 0;
4582 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4583 if (nickserv_conf.valid_nick_regex_set)
4584 regfree(&nickserv_conf.valid_nick_regex);
4586 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4587 nickserv_conf.valid_nick_regex_set = !err;
4588 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4590 nickserv_conf.valid_nick_regex_set = 0;
4592 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4594 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4595 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4596 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4597 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4598 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4599 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4600 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4601 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4602 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4603 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4604 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4605 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4606 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4607 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4608 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4609 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4610 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4611 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4612 str = database_get_data(conf_node, KEY_MODSTAFF_LEVEL, RECDB_QSTRING);
4613 nickserv_conf.modstaff_level = str ? strtoul(str, NULL, 0) : 800;
4614 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4615 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4616 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4617 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4618 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4619 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4620 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
4621 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
4622 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4624 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4625 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4626 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4628 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4629 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4630 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4632 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4633 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4634 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4635 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4636 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4637 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4638 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4639 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4640 str = database_get_data(conf_node, KEY_MAX_AUTHLOG_LEN, RECDB_QSTRING);
4641 nickserv_conf.max_authlog_len = str ? strtoul(str, NULL, 0) : 30;
4642 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4643 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4644 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4647 nickserv_conf.ounregister_flags = 0;
4649 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4652 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4654 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4656 nickserv_conf.handle_ts_mode = TS_IGNORE;
4657 else if (!irccasecmp(str, "ircu"))
4658 nickserv_conf.handle_ts_mode = TS_IRCU;
4660 nickserv_conf.handle_ts_mode = TS_IGNORE;
4661 if (!nickserv_conf.disable_nicks) {
4662 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4663 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4664 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4665 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4666 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4667 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4668 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4669 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4671 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4672 for (it=dict_first(child); it; it=iter_next(it)) {
4673 const char *key = iter_key(it), *value;
4677 if (!strncasecmp(key, "uc_", 3))
4678 flag = toupper(key[3]);
4679 else if (!strncasecmp(key, "lc_", 3))
4680 flag = tolower(key[3]);
4684 if ((pos = handle_inverse_flags[flag])) {
4685 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4686 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4689 if (nickserv_conf.weak_password_dict)
4690 dict_delete(nickserv_conf.weak_password_dict);
4691 nickserv_conf.weak_password_dict = dict_new();
4692 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4693 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4694 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4695 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4697 nickserv_load_dict(str);
4698 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4699 if (nickserv && str)
4700 NickChange(nickserv, str, 0);
4701 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4702 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4703 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4704 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4705 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4706 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4707 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4708 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4709 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4710 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4711 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4712 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4713 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4714 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4715 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4716 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4717 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4718 titlehost_suffix = str ? str : "example.net";
4719 str = conf_get_data("server/network", RECDB_QSTRING);
4720 nickserv_conf.network_name = str ? str : "some IRC network";
4721 if (!nickserv_conf.auth_policer_params) {
4722 nickserv_conf.auth_policer_params = policer_params_new();
4723 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4724 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4726 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4727 for (it=dict_first(child); it; it=iter_next(it))
4728 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4732 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4734 char newnick[NICKLEN+1];
4743 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4745 case RECLAIM_SVSNICK:
4747 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4748 } while (GetUserH(newnick));
4749 irc_svsnick(nickserv, user, newnick);
4752 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4753 DelUser(user, nickserv, 1, msg);
4759 nickserv_reclaim_p(void *data) {
4760 struct userNode *user = data;
4761 struct nick_info *ni = get_nick_info(user->nick);
4763 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4767 check_user_nick(struct userNode *user) {
4768 //check if this user is a pending LOC user
4769 if(pendingLOCUsers) {
4770 struct pendingLOCUser *pending, *next, *prev = NULL;
4771 for(pending = pendingLOCUsers; pending; pending = next) {
4772 next = pending->next;
4773 if(user->handle_info == pending->handle_info) {
4774 pending->authlog->user = user;
4775 free((char*) pending->authlog->hostmask);
4776 pending->authlog->hostmask = generate_hostmask(user, GENMASK_USENICK|GENMASK_STRICT_IDENT|GENMASK_NO_HIDING|GENMASK_STRICT_HOST);
4780 pendingLOCUsers = next;
4783 if(now - pending->time > 10) {
4787 pendingLOCUsers = next;
4792 struct nick_info *ni;
4793 user->modes &= ~FLAGS_REGNICK;
4794 if (!(ni = get_nick_info(user->nick)))
4796 if (user->handle_info == ni->owner) {
4797 user->modes |= FLAGS_REGNICK;
4801 if (nickserv_conf.warn_nick_owned)
4802 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4803 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4805 if (nickserv_conf.auto_reclaim_delay)
4806 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4808 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4812 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4814 struct handle_info *hi = NULL;
4817 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4818 if ((hi == NULL) && (serial != 0)) {
4820 inttobase64(id, serial, IDLEN);
4821 hi = dict_find(nickserv_id_dict, id, NULL);
4825 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4826 && (timestamp != hi->registered)) {
4829 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4832 set_user_handle_info(user, hi, 0);
4834 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4839 handle_nick_change(struct userNode *user, const char *old_nick)
4841 struct handle_info *hi;
4843 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4844 dict_remove(nickserv_allow_auth_dict, old_nick);
4845 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4847 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4848 check_user_nick(user);
4852 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), const char *why)
4854 if(user->handle_info) {
4855 //check if theres an open authlog entry
4856 struct authlogEntry *authlog;
4857 for(authlog = user->handle_info->authlog; authlog; authlog = authlog->next) {
4858 if(authlog->user == user) {
4859 authlog->user = NULL;
4860 authlog->logout_time = now;
4861 authlog->quit_reason = strdup(why);
4866 dict_remove(nickserv_allow_auth_dict, user->nick);
4867 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4868 set_user_handle_info(user, NULL, 0);
4871 static struct modcmd *
4872 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4874 if (min_level > 0) {
4876 sprintf(buf, "%u", min_level);
4877 if (must_be_qualified) {
4878 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4880 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4882 } else if (min_level == 0) {
4883 if (must_be_qualified) {
4884 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4886 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4889 if (must_be_qualified) {
4890 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4892 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4898 nickserv_db_cleanup(void)
4900 unreg_del_user_func(nickserv_remove_user);
4901 userList_clean(&curr_helpers);
4902 policer_params_delete(nickserv_conf.auth_policer_params);
4903 dict_delete(nickserv_handle_dict);
4904 dict_delete(nickserv_nick_dict);
4905 dict_delete(nickserv_opt_dict);
4906 dict_delete(nickserv_allow_auth_dict);
4907 dict_delete(nickserv_email_dict);
4908 dict_delete(nickserv_id_dict);
4909 dict_delete(nickserv_conf.weak_password_dict);
4910 free(auth_func_list);
4911 free(unreg_func_list);
4913 free(allowauth_func_list);
4914 free(handle_merge_func_list);
4915 free(failpw_func_list);
4916 if (nickserv_conf.valid_handle_regex_set)
4917 regfree(&nickserv_conf.valid_handle_regex);
4918 if (nickserv_conf.valid_nick_regex_set)
4919 regfree(&nickserv_conf.valid_nick_regex);
4920 struct pendingLOCUser *pending, *next;
4921 for(pending = pendingLOCUsers; pending; pending = next) {
4922 next = pending->next;
4925 pendingLOCUsers = NULL;
4929 init_nickserv(const char *nick)
4932 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4933 reg_new_user_func(check_user_nick);
4934 reg_nick_change_func(handle_nick_change);
4935 reg_del_user_func(nickserv_remove_user);
4936 reg_account_func(handle_account);
4938 /* set up handle_inverse_flags */
4939 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4940 for (i=0; handle_flags[i]; i++) {
4941 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4942 flag_access_levels[i] = 0;
4945 conf_register_reload(nickserv_conf_read);
4946 nickserv_opt_dict = dict_new();
4947 nickserv_email_dict = dict_new();
4948 dict_set_free_keys(nickserv_email_dict, free);
4949 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4951 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4952 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4953 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4954 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4955 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4956 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4957 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4958 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4959 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4960 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4961 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4962 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4963 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4964 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4965 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4966 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4967 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4968 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4969 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4970 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4971 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4972 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4973 if (!nickserv_conf.disable_nicks) {
4974 /* nick management commands */
4975 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4976 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4977 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4978 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4979 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4980 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4982 if (nickserv_conf.email_enabled) {
4983 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4984 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4985 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4986 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4987 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4988 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4990 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4991 /* miscellaneous commands */
4992 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4993 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4994 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4995 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4996 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4997 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4998 nickserv_define_func("AUTHLOG", cmd_authlog, -1, 1, 0);
4999 nickserv_define_func("OAUTHLOG", cmd_oauthlog, 0, 1, 0);
5001 dict_insert(nickserv_opt_dict, "INFO", opt_info);
5002 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
5003 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
5004 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
5005 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
5006 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
5007 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
5008 dict_insert(nickserv_opt_dict, "PASS", opt_password);
5009 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
5010 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
5011 dict_insert(nickserv_opt_dict, "WEBSITE", opt_website);
5012 dict_insert(nickserv_opt_dict, "DEVNULL", opt_devnull);
5013 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
5014 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
5015 dict_insert(nickserv_opt_dict, "STAFF", opt_staff_level);
5016 dict_insert(nickserv_opt_dict, "STAFF_LEVEL", opt_staff_level);
5017 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
5018 if (titlehost_suffix) {
5019 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
5020 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
5021 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
5023 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
5024 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
5025 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
5026 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
5028 nickserv_handle_dict = dict_new();
5029 dict_set_free_keys(nickserv_handle_dict, free);
5030 dict_set_free_data(nickserv_handle_dict, free_handle_info);
5032 nickserv_id_dict = dict_new();
5033 dict_set_free_keys(nickserv_id_dict, free);
5035 nickserv_nick_dict = dict_new();
5036 dict_set_free_data(nickserv_nick_dict, free);
5038 nickserv_allow_auth_dict = dict_new();
5040 userList_init(&curr_helpers);
5043 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
5044 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
5045 nickserv_service = service_register(nickserv);
5047 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
5048 reg_exit_func(nickserv_db_cleanup);
5049 if(nickserv_conf.handle_expire_frequency)
5050 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
5051 message_register_table(msgtab);