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);
584 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
585 nickserv_free_cookie(hi->cookie);
588 struct handle_note *note = hi->notes;
589 hi->notes = note->next;
592 if (hi->email_addr) {
593 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
594 handle_info_list_remove(hil, hi);
596 dict_remove(nickserv_email_dict, hi->email_addr);
598 struct authlogEntry *authlog, *next;
599 for(authlog = hi->authlog; authlog; authlog = next) {
600 next = authlog->next;
601 struct pendingLOCUser *pending, *prev_pending = NULL;
602 for(pending = pendingLOCUsers; pending; pending = pending->next) {
603 if(pending->authlog == authlog) {
605 prev_pending->next = pending->next;
607 pendingLOCUsers = pending->next;
611 prev_pending = pending;
613 free((char *) authlog->hostmask);
614 if(authlog->quit_reason)
615 free((char *) authlog->quit_reason);
621 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
624 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
628 for (n=0; n<unreg_func_used; n++)
629 unreg_func_list[n](notify, hi);
631 set_user_handle_info(hi->users, NULL, 0);
633 if (nickserv_conf.disable_nicks)
634 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
636 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
638 dict_remove(nickserv_handle_dict, hi->handle);
642 get_handle_info(const char *handle)
644 return dict_find(nickserv_handle_dict, handle, 0);
648 get_nick_info(const char *nick)
650 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
654 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
659 for (nn=0; nn<channel->members.used; ++nn) {
660 mn = channel->members.list[nn];
661 if ((mn->user != except) && (mn->user->handle_info == handle))
668 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
669 if (!user->handle_info) {
671 send_message(user, bot, "MSG_AUTHENTICATE");
675 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
677 send_message(user, bot, "NSMSG_NO_ACCESS");
681 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
683 send_message(user, bot, "MSG_OPER_SUSPENDED");
687 if (user->handle_info->opserv_level < min_level) {
689 send_message(user, bot, "NSMSG_NO_ACCESS");
697 staff_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
698 if (!user->handle_info) {
700 send_message(user, bot, "MSG_AUTHENTICATE");
704 if (user->handle_info->staff_level < min_level) {
706 send_message(user, bot, "NSMSG_NO_ACCESS");
714 is_valid_handle(const char *handle)
716 struct userNode *user;
717 /* cant register a juped nick/service nick as handle, to prevent confusion */
718 user = GetUserH(handle);
719 if (user && IsLocal(user))
721 /* check against maximum length */
722 if (strlen(handle) > NICKSERV_HANDLE_LEN)
724 /* for consistency, only allow account names that could be nicks */
725 if (!is_valid_nick(handle))
727 /* disallow account names that look like bad words */
728 if (opserv_bad_channel(handle))
730 /* test either regex or containing all valid chars */
731 if (nickserv_conf.valid_handle_regex_set) {
732 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
735 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
736 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
740 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
745 is_registerable_nick(const char *nick)
747 /* make sure it could be used as an account name */
748 if (!is_valid_handle(nick))
751 if (strlen(nick) > NICKLEN)
753 /* test either regex or as valid handle */
754 if (nickserv_conf.valid_nick_regex_set) {
755 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
758 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
759 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
767 is_valid_email_addr(const char *org_email)
769 char email[strlen(org_email)+1];
770 strcpy(email, org_email);
771 //validate email address
772 //1st check: there need to be one @
773 char *p1 = strchr(email, '@');
774 if(!p1 || strchr(p1+1, '@')) return 0;
776 //2nd check: username (bevore @) must be at least 1 char long and out of part_chars
777 char *part_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._%+-";
779 if(p1 - email == 0) return 0;
780 for(i = 0; i < (p1 - email); i++) {
781 if(!strchr(part_chars, email[i])) return 0;
783 //3rd check: there need to be at least 1 dot in the domain part and all characters out of part_chars
784 part_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-";
793 } else if(!strchr(part_chars, *p1))
800 //4th check: TLD must be <= 5 chars, no special chars
815 visible_email_addr(struct userNode *user, struct handle_info *hi)
817 if (hi->email_addr) {
818 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
819 return hi->email_addr;
829 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
831 struct handle_info *hi;
832 struct userNode *target;
836 if (!(hi = get_handle_info(++name))) {
837 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
842 if (!(target = GetUserH(name))) {
843 send_message(user, service, "MSG_NICK_UNKNOWN", name);
846 if (IsLocal(target)) {
847 if (IsService(target))
848 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
850 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
853 if (!(hi = target->handle_info)) {
854 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
862 oper_outranks(struct userNode *user, struct handle_info *hi) {
863 if (user->handle_info->opserv_level > hi->opserv_level)
865 if (user->handle_info->opserv_level == hi->opserv_level) {
866 if ((user->handle_info->opserv_level == 1000)
867 || (user->handle_info == hi)
868 || ((user->handle_info->opserv_level == 0)
869 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
870 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
874 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
878 static struct handle_info *
879 get_victim_oper(struct userNode *user, const char *target)
881 struct handle_info *hi;
882 if (!(hi = smart_get_handle_info(nickserv, user, target)))
884 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
885 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
888 return oper_outranks(user, hi) ? hi : NULL;
892 valid_user_for(struct userNode *user, struct handle_info *hi)
896 /* If no hostmasks on the account, allow it. */
897 if (!hi->masks->used || IsDummy(user))
899 /* If any hostmask matches, allow it. */
900 for (ii=0; ii<hi->masks->used; ii++)
901 if (user_matches_glob(user, hi->masks->list[ii], 0))
903 /* If they are allowauthed to this account, allow it (removing the aa). */
904 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
905 dict_remove(nickserv_allow_auth_dict, user->nick);
908 /* The user is not allowed to use this account. */
913 is_secure_password(const char *handle, const char *pass, struct userNode *user)
916 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
920 if (len < nickserv_conf.password_min_length) {
922 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
925 if (!irccasecmp(pass, handle)) {
927 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
930 dict_find(nickserv_conf.weak_password_dict, pass, &p);
933 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
936 for (i=0; i<len; i++) {
937 if (isdigit(pass[i]))
939 if (isupper(pass[i]))
941 if (islower(pass[i]))
944 if ((cnt_lower < nickserv_conf.password_min_lower)
945 || (cnt_upper < nickserv_conf.password_min_upper)
946 || (cnt_digits < nickserv_conf.password_min_digits)) {
948 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
954 static auth_func_t *auth_func_list;
955 static unsigned int auth_func_size = 0, auth_func_used = 0;
958 reg_auth_func(auth_func_t func)
960 if (auth_func_used == auth_func_size) {
961 if (auth_func_size) {
962 auth_func_size <<= 1;
963 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
966 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
969 auth_func_list[auth_func_used++] = func;
972 static handle_rename_func_t *rf_list;
973 static unsigned int rf_list_size, rf_list_used;
976 reg_handle_rename_func(handle_rename_func_t func)
978 if (rf_list_used == rf_list_size) {
981 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
984 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
987 rf_list[rf_list_used++] = func;
991 generate_fakehost(struct handle_info *handle)
993 extern const char *hidden_host_suffix;
994 static char buffer[HOSTLEN+1];
996 if (!handle->fakehost) {
997 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
999 } else if (handle->fakehost[0] == '.') {
1000 /* A leading dot indicates the stored value is actually a title. */
1001 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, titlehost_suffix);
1003 } else if (handle->fakehost[0] == '$') {
1004 /* A leading $ indicates the stored value begins with the user handle. */
1005 snprintf(buffer, sizeof(buffer), "%s%s", handle->handle, handle->fakehost+1);
1008 return handle->fakehost;
1012 generate_fakeident(struct handle_info *handle, struct userNode *user)
1014 static char buffer[USERLEN+1];
1016 if (!handle->fakeident) {
1019 safestrncpy(buffer, user->ident, sizeof(buffer));
1022 return handle->fakeident;
1026 apply_fakehost(struct handle_info *handle, struct userNode *user)
1028 struct userNode *target;
1029 char *fakehost, *fakeident;
1034 fakehost = generate_fakehost(handle);
1037 fakeident = generate_fakeident(handle, user);
1038 assign_fakehost(user, fakehost, fakeident, 0, 1);
1042 for (target = handle->users; target; target = target->next_authed) {
1043 fakeident = generate_fakeident(handle, target);
1044 assign_fakehost(target, fakehost, fakeident, 0, 1);
1049 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
1052 struct handle_info *old_info;
1054 /* This can happen if somebody uses COOKIE while authed, or if
1055 * they re-auth to their current handle (which is silly, but users
1056 * are like that). */
1057 if (user->handle_info == hi)
1060 if (user->handle_info) {
1061 struct userNode *other;
1064 userList_remove(&curr_helpers, user);
1066 /* remove from next_authed linked list */
1067 if (user->handle_info->users == user) {
1068 user->handle_info->users = user->next_authed;
1069 } else if (user->handle_info->users != NULL) {
1070 for (other = user->handle_info->users;
1071 other->next_authed != user;
1072 other = other->next_authed) ;
1073 other->next_authed = user->next_authed;
1075 /* No users authed to the account - can happen if they get
1076 * killed for authing. */
1078 /* if nobody left on old handle, and they're not an oper, remove !god */
1079 if (!user->handle_info->users && !user->handle_info->opserv_level)
1080 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
1081 /* record them as being last seen at this time */
1082 user->handle_info->lastseen = now;
1083 /* and record their hostmask */
1084 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
1086 old_info = user->handle_info;
1087 user->handle_info = hi;
1088 if (hi && !hi->users && !hi->opserv_level)
1089 HANDLE_CLEAR_FLAG(hi, HELPING);
1090 for (n=0; n<auth_func_used; n++) {
1091 auth_func_list[n](user, old_info);
1096 struct nick_info *ni;
1098 HANDLE_CLEAR_FLAG(hi, FROZEN);
1099 if (nickserv_conf.warn_clone_auth) {
1100 struct userNode *other;
1101 for (other = hi->users; other; other = other->next_authed)
1102 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
1104 user->next_authed = hi->users;
1107 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
1108 userList_append(&curr_helpers, user);
1110 if (hi->fakehost || hi->fakeident || old_info)
1111 apply_fakehost(hi, user);
1114 if (!nickserv_conf.disable_nicks) {
1115 struct nick_info *ni2;
1116 for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
1117 if (!irccasecmp(user->nick, ni2->nick)) {
1118 user->modes |= FLAGS_REGNICK;
1123 StampUser(user, hi->handle, hi->registered, hi->id);
1126 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
1127 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
1129 /* We cannot clear the user's account ID, unfortunately. */
1130 user->next_authed = NULL;
1134 static struct handle_info*
1135 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
1137 struct handle_info *hi;
1138 struct nick_info *ni;
1139 char crypted[MD5_CRYPT_LENGTH];
1141 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
1142 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
1146 if (!is_secure_password(handle, passwd, user))
1149 cryptpass(passwd, crypted);
1150 hi = register_handle(handle, crypted, 0);
1151 hi->masks = alloc_string_list(1);
1153 hi->language = lang_C;
1154 hi->registered = now;
1156 hi->flags = HI_DEFAULT_FLAGS;
1157 if (settee && !no_auth)
1158 set_user_handle_info(settee, hi, 1);
1161 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1162 else if (nickserv_conf.disable_nicks)
1163 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1164 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1165 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1167 register_nick(user->nick, hi);
1168 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1170 if (settee && (user != settee))
1171 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1176 nickserv_bake_cookie(struct handle_cookie *cookie)
1178 cookie->hi->cookie = cookie;
1179 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1183 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1185 struct handle_cookie *cookie;
1186 char subject[128], body[4096], *misc;
1187 const char *netname, *fmt;
1191 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1195 cookie = calloc(1, sizeof(*cookie));
1197 cookie->type = type;
1198 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1199 cookie->expires = now + nickserv_conf.cookie_timeout;
1200 inttobase64(cookie->cookie, rand(), 5);
1201 inttobase64(cookie->cookie+5, rand(), 5);
1203 netname = nickserv_conf.network_name;
1206 switch (cookie->type) {
1208 hi->passwd[0] = 0; /* invalidate password */
1209 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1210 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1211 snprintf(subject, sizeof(subject), fmt, netname);
1212 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1213 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1216 case PASSWORD_CHANGE:
1217 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1218 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1219 snprintf(subject, sizeof(subject), fmt, netname);
1220 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1221 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1224 misc = hi->email_addr;
1225 hi->email_addr = cookie->data;
1227 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1228 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1229 snprintf(subject, sizeof(subject), fmt, netname);
1230 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1231 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1232 mail_send(nickserv, hi, subject, body, 1);
1233 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1234 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1236 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1237 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1238 snprintf(subject, sizeof(subject), fmt, netname);
1239 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1240 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1241 mail_send(nickserv, hi, subject, body, 1);
1244 hi->email_addr = misc;
1247 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1248 snprintf(subject, sizeof(subject), fmt, netname);
1249 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1250 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1251 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1254 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1258 mail_send(nickserv, hi, subject, body, first_time);
1259 nickserv_bake_cookie(cookie);
1263 nickserv_eat_cookie(struct handle_cookie *cookie)
1265 cookie->hi->cookie = NULL;
1266 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1267 nickserv_free_cookie(cookie);
1271 nickserv_free_email_addr(void *data)
1273 handle_info_list_clean(data);
1278 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1280 struct handle_info_list *hil;
1281 /* Remove from old handle_info_list ... */
1282 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1283 handle_info_list_remove(hil, hi);
1284 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1285 hi->email_addr = NULL;
1287 /* Add to the new list.. */
1288 if (new_email_addr) {
1289 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1290 hil = calloc(1, sizeof(*hil));
1291 hil->tag = strdup(new_email_addr);
1292 handle_info_list_init(hil);
1293 dict_insert(nickserv_email_dict, hil->tag, hil);
1295 handle_info_list_append(hil, hi);
1296 hi->email_addr = hil->tag;
1300 static NICKSERV_FUNC(cmd_register)
1303 struct handle_info *hi;
1304 const char *email_addr, *password;
1307 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1308 /* Require the first handle registered to belong to someone +o. */
1309 reply("NSMSG_REQUIRE_OPER");
1313 if (user->handle_info) {
1314 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1318 if (IsRegistering(user)) {
1319 reply("NSMSG_ALREADY_REGISTERING");
1323 if (IsStamped(user)) {
1324 /* Unauthenticated users might still have been stamped
1325 previously and could therefore have a hidden host;
1326 do not allow them to register a new account. */
1327 reply("NSMSG_STAMPED_REGISTER");
1331 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1333 if (!is_valid_handle(argv[1])) {
1334 reply("NSMSG_BAD_HANDLE", argv[1]);
1338 if ((argc >= 4) && nickserv_conf.email_enabled) {
1339 struct handle_info_list *hil;
1342 /* Remember email address. */
1343 email_addr = argv[3];
1345 /* Check that the email address looks valid.. */
1346 if (!is_valid_email_addr(email_addr)) {
1347 reply("NSMSG_BAD_EMAIL_ADDR");
1351 /* .. and that we are allowed to send to it. */
1352 if ((str = mail_prohibited_address(email_addr))) {
1353 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1357 /* If we do email verify, make sure we don't spam the address. */
1358 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1360 for (nn=0; nn<hil->used; nn++) {
1361 if (hil->list[nn]->cookie) {
1362 reply("NSMSG_EMAIL_UNACTIVATED");
1366 if (hil->used >= nickserv_conf.handles_per_email) {
1367 reply("NSMSG_EMAIL_OVERUSED");
1380 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1382 /* Add any masks they should get. */
1383 if (nickserv_conf.default_hostmask) {
1384 nickserv_addmask(NULL, hi, strdup("*@*"));
1386 nickserv_addmask(NULL, hi, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1387 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1388 nickserv_addmask(NULL, hi, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1391 /* If they're the first to register, give them level 1000. */
1392 if (dict_size(nickserv_handle_dict) == 1) {
1393 hi->opserv_level = 1000;
1394 hi->staff_level = 1000;
1395 reply("NSMSG_ROOT_HANDLE", argv[1]);
1398 /* Set their email address. */
1400 nickserv_set_email_addr(hi, email_addr);
1402 /* If they need to do email verification, tell them. */
1404 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1406 /* Set registering flag.. */
1407 user->modes |= FLAGS_REGISTERING;
1412 static NICKSERV_FUNC(cmd_oregister)
1415 struct userNode *settee;
1416 struct handle_info *hi;
1417 const char *pass, *email;
1419 NICKSERV_MIN_PARMS(3);
1424 if (!is_valid_handle(argv[1])) {
1425 reply("NSMSG_BAD_HANDLE", argv[1]);
1429 if (argc < 5 || !nickserv_conf.email_enabled) {
1434 if (!is_valid_email_addr(email)) {
1435 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
1438 if ((str = mail_prohibited_address(email))) {
1439 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", email, str);
1444 if (argc < 4 || !strcmp(argv[3], "*")) {
1447 } else if (strchr(argv[3], '@')) {
1448 mask = canonicalize_hostmask(strdup(argv[3]));
1450 settee = GetUserH(argv[4]);
1452 reply("MSG_NICK_UNKNOWN", argv[4]);
1459 } else if ((settee = GetUserH(argv[3]))) {
1460 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1462 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1465 if (settee && settee->handle_info) {
1466 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1470 if (!(hi = nickserv_register(user, settee, argv[1], pass, 0))) {
1475 string_list_append(hi->masks, mask);
1477 nickserv_set_email_addr(hi, email);
1481 static NICKSERV_FUNC(cmd_handleinfo)
1484 unsigned int i, pos=0, herelen;
1485 struct userNode *target, *next_un;
1486 struct handle_info *hi;
1487 const char *nsmsg_none;
1491 if (!(hi = user->handle_info)) {
1492 reply("NSMSG_MUST_AUTH");
1495 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1499 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1500 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1501 feh = hi->registered;
1502 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1505 intervalString(buff, now - hi->lastseen, user->handle_info);
1506 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1508 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1511 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1512 if ((oper_has_access(user, cmd->parent->bot, 200, 1)) || IsNetworkHelper(user)) {
1513 struct devnull_class *devnull_c = (hi->devnull_id ? devnull_find_id(hi->devnull_id) : NULL);
1514 reply("NSMSG_HANDLEINFO_DEVNULL", (devnull_c ? devnull_c->name : nsmsg_none));
1516 if (user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1517 reply("NSMSG_HANDLEINFO_WEBSITE", (hi->website ? hi->website : nsmsg_none));
1518 if(hi->opserv_level > 0 && user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1519 reply("NSMSG_HANDLEINFO_ACCESS", hi->opserv_level);
1520 if (HANDLE_FLAGGED(hi, FROZEN))
1521 reply("NSMSG_HANDLEINFO_VACATION");
1523 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1524 struct do_not_register *dnr;
1525 if ((dnr = chanserv_is_dnr(NULL, hi)))
1526 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1527 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1529 } else if (hi != user->handle_info)
1533 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1535 if (nickserv_conf.email_enabled)
1536 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1540 switch (hi->cookie->type) {
1541 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1542 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1543 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1544 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1545 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1550 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1551 reply("NSMSG_HANDLEINFO_ID", hi->id);
1553 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1555 reply("NSMSG_HANDLEINFO_NO_NOTES");
1557 struct handle_note *prev, *note;
1559 WALK_NOTES(hi, prev, note) {
1560 char set_time[INTERVALLEN];
1561 intervalString(set_time, now - note->set, user->handle_info);
1562 if (note->expires) {
1563 char exp_time[INTERVALLEN];
1564 intervalString(exp_time, note->expires - now, user->handle_info);
1565 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1567 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1574 unsigned long flen = 1;
1575 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1577 for (i=0, flen=1; handle_flags[i]; i++)
1578 if (hi->flags & 1 << i)
1579 flags[flen++] = handle_flags[i];
1581 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1583 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1586 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1587 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1588 || (hi->opserv_level > 0)) {
1589 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1592 if (hi->fakeident && hi->fakehost)
1593 reply("NSMSG_HANDLEINFO_FAKEIDENTHOST", hi->fakeident, hi->fakehost);
1594 else if (hi->fakeident)
1595 reply("NSMSG_HANDLEINFO_FAKEIDENT", hi->fakeident);
1596 else if (hi->fakehost)
1597 reply("NSMSG_HANDLEINFO_FAKEHOST", hi->fakehost);
1599 if (hi->last_quit_host[0])
1600 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1602 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1604 if (nickserv_conf.disable_nicks) {
1605 /* nicks disabled; don't show anything about registered nicks */
1606 } else if (hi->nicks) {
1607 struct nick_info *ni, *next_ni;
1608 for (ni = hi->nicks; ni; ni = next_ni) {
1609 herelen = strlen(ni->nick);
1610 if (pos + herelen + 1 > ArrayLength(buff)) {
1612 goto print_nicks_buff;
1616 memcpy(buff+pos, ni->nick, herelen);
1617 pos += herelen; buff[pos++] = ' ';
1621 reply("NSMSG_HANDLEINFO_NICKS", buff);
1626 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1629 if (hi->masks->used) {
1630 for (i=0; i < hi->masks->used; i++) {
1631 herelen = strlen(hi->masks->list[i]);
1632 if (pos + herelen + 1 > ArrayLength(buff)) {
1634 goto print_mask_buff;
1636 memcpy(buff+pos, hi->masks->list[i], herelen);
1637 pos += herelen; buff[pos++] = ' ';
1638 if (i+1 == hi->masks->used) {
1641 reply("NSMSG_HANDLEINFO_MASKS", buff);
1646 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1650 struct userData *chan, *next;
1653 for (chan = hi->channels; chan; chan = next) {
1654 next = chan->u_next;
1655 name = chan->channel->channel->name;
1656 herelen = strlen(name);
1657 if (pos + herelen + 7 > ArrayLength(buff)) {
1659 goto print_chans_buff;
1661 if (IsUserSuspended(chan))
1663 pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
1667 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1672 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1675 for (target = hi->users; target; target = next_un) {
1676 herelen = strlen(target->nick);
1677 if (pos + herelen + 1 > ArrayLength(buff)) {
1679 goto print_cnick_buff;
1681 next_un = target->next_authed;
1683 memcpy(buff+pos, target->nick, herelen);
1684 pos += herelen; buff[pos++] = ' ';
1688 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1693 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1696 static NICKSERV_FUNC(cmd_userinfo)
1698 struct userNode *target;
1700 NICKSERV_MIN_PARMS(2);
1701 if (!(target = GetUserH(argv[1]))) {
1702 reply("MSG_NICK_UNKNOWN", argv[1]);
1705 if (target->handle_info)
1706 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1708 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1712 static NICKSERV_FUNC(cmd_nickinfo)
1714 struct nick_info *ni;
1716 NICKSERV_MIN_PARMS(2);
1717 if (!(ni = get_nick_info(argv[1]))) {
1718 reply("MSG_NICK_UNKNOWN", argv[1]);
1721 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1725 static NICKSERV_FUNC(cmd_notes)
1727 struct handle_info *hi;
1728 struct handle_note *prev, *note;
1731 NICKSERV_MIN_PARMS(2);
1732 if (!(hi = get_victim_oper(user, argv[1])))
1735 WALK_NOTES(hi, prev, note) {
1736 char set_time[INTERVALLEN];
1737 intervalString(set_time, now - note->set, user->handle_info);
1738 if (note->expires) {
1739 char exp_time[INTERVALLEN];
1740 intervalString(exp_time, note->expires - now, user->handle_info);
1741 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1743 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1747 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1751 static NICKSERV_FUNC(cmd_rename_handle)
1753 struct handle_info *hi;
1754 char msgbuf[MAXLEN], *old_handle;
1757 NICKSERV_MIN_PARMS(3);
1758 if (!(hi = get_victim_oper(user, argv[1])))
1760 if (!is_valid_handle(argv[2])) {
1761 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1764 if (get_handle_info(argv[2])) {
1765 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1768 if (hi->fakehost && hi->fakehost[0] == '.' &&
1769 (strlen(argv[2]) + strlen(hi->fakehost+1) +
1770 strlen(titlehost_suffix) + 2) > HOSTLEN) {
1771 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED_RENAME");
1775 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1776 hi->handle = strdup(argv[2]);
1777 dict_insert(nickserv_handle_dict, hi->handle, hi);
1778 for (nn=0; nn<rf_list_used; nn++)
1779 rf_list[nn](hi, old_handle);
1780 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1781 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1782 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1784 apply_fakehost(hi, NULL);
1788 static failpw_func_t *failpw_func_list;
1789 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1792 reg_failpw_func(failpw_func_t func)
1794 if (failpw_func_used == failpw_func_size) {
1795 if (failpw_func_size) {
1796 failpw_func_size <<= 1;
1797 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1799 failpw_func_size = 8;
1800 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1803 failpw_func_list[failpw_func_used++] = func;
1806 static struct authlogEntry *authlog_add(struct handle_info *hi, struct userNode *user, const char *mask) {
1807 if(!hi || (!user && !mask)) return NULL;
1809 mask = generate_hostmask(user, GENMASK_USENICK|GENMASK_STRICT_IDENT|GENMASK_NO_HIDING|GENMASK_STRICT_HOST);
1810 struct authlogEntry *authlog, *next, *prev = NULL;
1811 authlog = malloc(sizeof(*authlog));
1812 authlog->login_time = now;
1813 authlog->logout_time = 0;
1814 authlog->hostmask = mask;
1815 authlog->quit_reason = NULL;
1816 authlog->user = user;
1817 authlog->next = hi->authlog;
1818 hi->authlog = authlog;
1820 for(authlog = hi->authlog; authlog; authlog = next) {
1822 next = authlog->next;
1823 if(i > nickserv_conf.max_authlog_len) {
1824 struct pendingLOCUser *pending, *prev_pending = NULL;
1825 for(pending = pendingLOCUsers; pending; pending = pending->next) {
1826 if(pending->authlog == authlog) {
1828 prev_pending->next = pending->next;
1830 pendingLOCUsers = pending->next;
1834 prev_pending = pending;
1836 free((char *) authlog->hostmask);
1837 if(authlog->quit_reason)
1838 free((char *) authlog->quit_reason);
1840 prev->next = authlog->next;
1842 hi->authlog = authlog->next;
1850 static NICKSERV_FUNC(cmd_auth)
1852 int pw_arg, used, maxlogins;
1853 struct handle_info *hi;
1855 struct userNode *other;
1857 if (user->handle_info) {
1858 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1861 if (IsStamped(user)) {
1862 /* Unauthenticated users might still have been stamped
1863 previously and could therefore have a hidden host;
1864 do not allow them to authenticate. */
1865 reply("NSMSG_STAMPED_AUTH");
1869 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1871 } else if (argc == 2) {
1872 if (nickserv_conf.disable_nicks) {
1873 if (!(hi = get_handle_info(user->nick))) {
1874 reply("NSMSG_HANDLE_NOT_FOUND");
1878 /* try to look up their handle from their nick */
1879 struct nick_info *ni;
1880 ni = get_nick_info(user->nick);
1882 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1889 reply("MSG_MISSING_PARAMS", argv[0]);
1890 svccmd_send_help(user, nickserv, cmd);
1894 reply("NSMSG_HANDLE_NOT_FOUND");
1897 /* Responses from here on look up the language used by the handle they asked about. */
1898 passwd = argv[pw_arg];
1899 if (!valid_user_for(user, hi)) {
1900 if (hi->email_addr && nickserv_conf.email_enabled)
1901 send_message_type(4, user, cmd->parent->bot,
1902 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1905 send_message_type(4, user, cmd->parent->bot,
1906 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1908 argv[pw_arg] = "BADMASK";
1911 if (!checkpass(passwd, hi->passwd)) {
1913 send_message_type(4, user, cmd->parent->bot,
1914 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1915 argv[pw_arg] = "BADPASS";
1916 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1917 if (nickserv_conf.autogag_enabled) {
1918 if (!user->auth_policer.params) {
1919 user->auth_policer.last_req = now;
1920 user->auth_policer.params = nickserv_conf.auth_policer_params;
1922 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1924 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1925 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1926 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1928 argv[pw_arg] = "GAGGED";
1933 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1934 send_message_type(4, user, cmd->parent->bot,
1935 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1936 argv[pw_arg] = "SUSPENDED";
1939 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1940 for (used = 0, other = hi->users; other; other = other->next_authed) {
1941 if (++used >= maxlogins) {
1942 send_message_type(4, user, cmd->parent->bot,
1943 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1945 argv[pw_arg] = "MAXLOGINS";
1949 if (HANDLE_FLAGGED(hi, AUTOHIDE)) {
1950 //ok we have a fakehost set... but we need to set mode +x
1951 irc_svsmode(nickserv,user,"+x");
1954 set_user_handle_info(user, hi, 1);
1955 if (nickserv_conf.email_required && !hi->email_addr)
1956 reply("NSMSG_PLEASE_SET_EMAIL");
1957 if (!is_secure_password(hi->handle, passwd, NULL))
1958 reply("NSMSG_WEAK_PASSWORD");
1959 if (hi->passwd[0] != '$')
1960 cryptpass(passwd, hi->passwd);
1961 if (!hi->masks->used) {
1963 nickserv_addmask(NULL, hi, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1964 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1965 nickserv_addmask(NULL, hi, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1967 authlog_add(hi, user, NULL);
1968 argv[pw_arg] = "****";
1969 reply("NSMSG_AUTH_SUCCESS");
1973 struct handle_info *checklogin(const char *user, const char *pass, UNUSED_ARG(const char *numeric), const char *hostmask, const char *ipmask)
1975 struct handle_info *hi;
1976 unsigned int match = 0, ii = 0;
1977 hi = dict_find(nickserv_handle_dict, user, NULL);
1980 /* If no hostmasks on the account, allow it. */
1981 if (hi->masks->used) {
1982 /* If any hostmask matches, allow it. */
1983 for (ii=0; ii<hi->masks->used; ii++)
1984 if (match_ircglob(hostmask, hi->masks->list[ii]) || match_ircglob(ipmask, hi->masks->list[ii])) {
1991 if(!checkpass(pass, hi->passwd))
1993 if (HANDLE_FLAGGED(hi, SUSPENDED))
1995 char *ptr = malloc(strlen(hostmask)+1);
1996 strcpy(ptr, hostmask);
1997 struct authlogEntry *authlog = authlog_add(hi, NULL, ptr);
1998 struct pendingLOCUser *pending;
1999 if(authlog && (pending = malloc(sizeof(*pending)))) {
2000 pending->handle_info = hi;
2001 pending->time = now;
2002 pending->authlog = authlog;
2003 pending->next = pendingLOCUsers;
2004 pendingLOCUsers = pending;
2009 char *getfakehost(const char *user)
2011 struct handle_info *hi;
2012 hi = dict_find(nickserv_handle_dict, user, NULL);
2015 return generate_fakehost(hi);
2018 static allowauth_func_t *allowauth_func_list;
2019 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
2022 reg_allowauth_func(allowauth_func_t func)
2024 if (allowauth_func_used == allowauth_func_size) {
2025 if (allowauth_func_size) {
2026 allowauth_func_size <<= 1;
2027 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
2029 allowauth_func_size = 8;
2030 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
2033 allowauth_func_list[allowauth_func_used++] = func;
2036 static int cmd_authlog_func(struct userNode *user, struct svccmd *cmd, struct handle_info *hi);
2038 static MODCMD_FUNC(cmd_authlog)
2040 return cmd_authlog_func(user, cmd, user->handle_info);
2043 static MODCMD_FUNC(cmd_oauthlog) {
2044 struct handle_info *hi;
2046 NICKSERV_MIN_PARMS(2);
2048 if (!(hi = get_victim_oper(user, argv[1])))
2051 return cmd_authlog_func(user, cmd, hi);
2054 static int cmd_authlog_func(struct userNode *user, struct svccmd *cmd, struct handle_info *hi) {
2055 struct helpfile_table tbl;
2056 struct authlogEntry *authlog;
2059 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
2066 tbl.flags = TABLE_NO_FREE;
2067 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
2068 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
2069 tbl.contents[0][0] = "Hostmask";
2070 tbl.contents[0][1] = "Login";
2071 tbl.contents[0][2] = "Logout";
2072 tbl.contents[0][3] = "Quit Reason";
2075 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
2077 free(tbl.contents[0]);
2083 char intervalBuf[INTERVALLEN];
2085 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
2086 tbl.contents[++i] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
2087 tbl.contents[i][0] = authlog->hostmask;
2088 str = intervalString(intervalBuf, now - authlog->login_time, hi);
2089 ptr = malloc(strlen(str)+1);
2091 tbl.contents[i][1] = ptr;
2092 if(authlog->logout_time)
2093 str = intervalString(intervalBuf, now - authlog->logout_time, hi);
2094 else if(!authlog->user)
2097 sprintf(intervalBuf, "Never (%s)", authlog->user->nick);
2100 ptr = malloc(strlen(str)+1);
2102 tbl.contents[i][2] = ptr;
2103 tbl.contents[i][3] = (authlog->quit_reason ? authlog->quit_reason : "-");
2106 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
2107 for(i = 1; i < tbl.length; ++i)
2109 free((char *) tbl.contents[i][1]);
2110 free((char *) tbl.contents[i][2]);
2111 free(tbl.contents[i]);
2113 free(tbl.contents[0]);
2119 static NICKSERV_FUNC(cmd_allowauth)
2121 struct userNode *target;
2122 struct handle_info *hi;
2125 NICKSERV_MIN_PARMS(2);
2126 if (!(target = GetUserH(argv[1]))) {
2127 reply("MSG_NICK_UNKNOWN", argv[1]);
2130 if (target->handle_info) {
2131 reply("NSMSG_USER_PREV_AUTH", target->nick);
2134 if (IsStamped(target)) {
2135 /* Unauthenticated users might still have been stamped
2136 previously and could therefore have a hidden host;
2137 do not allow them to authenticate to an account. */
2138 reply("NSMSG_USER_PREV_STAMP", target->nick);
2143 else if (!(hi = get_handle_info(argv[2]))) {
2144 reply("MSG_HANDLE_UNKNOWN", argv[2]);
2148 if (hi->opserv_level > user->handle_info->opserv_level) {
2149 reply("MSG_USER_OUTRANKED", hi->handle);
2152 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
2153 || (hi->opserv_level > 0))
2154 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
2155 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
2158 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
2159 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
2160 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
2161 if (nickserv_conf.email_enabled)
2162 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
2164 if (dict_remove(nickserv_allow_auth_dict, target->nick))
2165 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
2167 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
2169 for (n=0; n<allowauth_func_used; n++)
2170 allowauth_func_list[n](user, target, hi);
2174 static NICKSERV_FUNC(cmd_authcookie)
2176 struct handle_info *hi;
2178 NICKSERV_MIN_PARMS(2);
2179 if (user->handle_info) {
2180 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2183 if (IsStamped(user)) {
2184 /* Unauthenticated users might still have been stamped
2185 previously and could therefore have a hidden host;
2186 do not allow them to authenticate to an account. */
2187 reply("NSMSG_STAMPED_AUTHCOOKIE");
2190 if (!(hi = get_handle_info(argv[1]))) {
2191 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2194 if (!hi->email_addr) {
2195 reply("MSG_SET_EMAIL_ADDR");
2198 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
2202 static NICKSERV_FUNC(cmd_delcookie)
2204 struct handle_info *hi;
2206 hi = user->handle_info;
2208 reply("NSMSG_NO_COOKIE");
2211 switch (hi->cookie->type) {
2214 reply("NSMSG_MUST_TIME_OUT");
2217 nickserv_eat_cookie(hi->cookie);
2218 reply("NSMSG_ATE_COOKIE");
2224 static NICKSERV_FUNC(cmd_odelcookie)
2226 struct handle_info *hi;
2228 NICKSERV_MIN_PARMS(2);
2230 if (!(hi = get_victim_oper(user, argv[1])))
2234 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
2238 nickserv_eat_cookie(hi->cookie);
2239 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
2244 static NICKSERV_FUNC(cmd_resetpass)
2246 struct handle_info *hi;
2247 char crypted[MD5_CRYPT_LENGTH];
2249 NICKSERV_MIN_PARMS(3);
2250 if (user->handle_info) {
2251 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2254 if (IsStamped(user)) {
2255 /* Unauthenticated users might still have been stamped
2256 previously and could therefore have a hidden host;
2257 do not allow them to activate an account. */
2258 reply("NSMSG_STAMPED_RESETPASS");
2261 if (!(hi = get_handle_info(argv[1]))) {
2262 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2265 if (!hi->email_addr) {
2266 reply("MSG_SET_EMAIL_ADDR");
2269 cryptpass(argv[2], crypted);
2271 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
2275 static NICKSERV_FUNC(cmd_cookie)
2277 struct handle_info *hi;
2280 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
2283 NICKSERV_MIN_PARMS(3);
2284 if (!(hi = get_handle_info(argv[1]))) {
2285 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2291 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2292 reply("NSMSG_HANDLE_SUSPENDED");
2297 reply("NSMSG_NO_COOKIE");
2301 /* Check validity of operation before comparing cookie to
2302 * prohibit guessing by authed users. */
2303 if (user->handle_info
2304 && (hi->cookie->type != EMAIL_CHANGE)
2305 && (hi->cookie->type != PASSWORD_CHANGE)) {
2306 reply("NSMSG_CANNOT_COOKIE");
2310 if (strcmp(cookie, hi->cookie->cookie)) {
2311 reply("NSMSG_BAD_COOKIE");
2315 switch (hi->cookie->type) {
2317 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2318 set_user_handle_info(user, hi, 1);
2319 reply("NSMSG_HANDLE_ACTIVATED");
2321 case PASSWORD_CHANGE:
2322 set_user_handle_info(user, hi, 1);
2323 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2324 reply("NSMSG_PASSWORD_CHANGED");
2327 nickserv_set_email_addr(hi, hi->cookie->data);
2328 reply("NSMSG_EMAIL_CHANGED");
2331 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2332 set_user_handle_info(user, hi, 1);
2333 nickserv_addmask(user, hi, mask);
2334 reply("NSMSG_AUTH_SUCCESS");
2339 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2340 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2344 nickserv_eat_cookie(hi->cookie);
2349 static NICKSERV_FUNC(cmd_oregnick) {
2351 struct handle_info *target;
2352 struct nick_info *ni;
2354 NICKSERV_MIN_PARMS(3);
2355 if (!(target = modcmd_get_handle_info(user, argv[1])))
2358 if (!is_registerable_nick(nick)) {
2359 reply("NSMSG_BAD_NICK", nick);
2362 ni = dict_find(nickserv_nick_dict, nick, NULL);
2364 reply("NSMSG_NICK_EXISTS", nick);
2367 register_nick(nick, target);
2368 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2372 static NICKSERV_FUNC(cmd_regnick) {
2374 struct nick_info *ni;
2376 if (!is_registerable_nick(user->nick)) {
2377 reply("NSMSG_BAD_NICK", user->nick);
2380 /* count their nicks, see if it's too many */
2381 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2382 if (n >= nickserv_conf.nicks_per_handle) {
2383 reply("NSMSG_TOO_MANY_NICKS");
2386 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2388 reply("NSMSG_NICK_EXISTS", user->nick);
2391 register_nick(user->nick, user->handle_info);
2392 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2396 static NICKSERV_FUNC(cmd_pass)
2398 struct handle_info *hi;
2399 const char *old_pass, *new_pass;
2401 NICKSERV_MIN_PARMS(3);
2402 hi = user->handle_info;
2406 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2407 if (!checkpass(old_pass, hi->passwd)) {
2408 argv[1] = "BADPASS";
2409 reply("NSMSG_PASSWORD_INVALID");
2412 cryptpass(new_pass, hi->passwd);
2414 reply("NSMSG_PASS_SUCCESS");
2419 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2422 char *new_mask = canonicalize_hostmask(strdup(mask));
2423 for (i=0; i<hi->masks->used; i++) {
2424 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2426 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2431 string_list_append(hi->masks, new_mask);
2433 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2437 static NICKSERV_FUNC(cmd_addmask)
2440 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2441 int res = nickserv_addmask(user, user->handle_info, mask);
2445 if (!is_gline(argv[1])) {
2446 reply("NSMSG_MASK_INVALID", argv[1]);
2449 return nickserv_addmask(user, user->handle_info, argv[1]);
2453 static NICKSERV_FUNC(cmd_oaddmask)
2455 struct handle_info *hi;
2457 NICKSERV_MIN_PARMS(3);
2458 if (!(hi = get_victim_oper(user, argv[1])))
2460 return nickserv_addmask(user, hi, argv[2]);
2464 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2467 for (i=0; i<hi->masks->used; i++) {
2468 if (!strcmp(del_mask, hi->masks->list[i])) {
2469 char *old_mask = hi->masks->list[i];
2470 if (hi->masks->used == 1 && !force) {
2471 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2474 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2475 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2480 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2484 static NICKSERV_FUNC(cmd_delmask)
2486 NICKSERV_MIN_PARMS(2);
2487 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2490 static NICKSERV_FUNC(cmd_odelmask)
2492 struct handle_info *hi;
2493 NICKSERV_MIN_PARMS(3);
2494 if (!(hi = get_victim_oper(user, argv[1])))
2496 return nickserv_delmask(user, hi, argv[2], 1);
2500 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2501 unsigned int nn, add = 1, pos;
2502 unsigned long added, removed, flag;
2504 for (added=removed=nn=0; str[nn]; nn++) {
2506 case '+': add = 1; break;
2507 case '-': add = 0; break;
2509 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2510 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2513 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2514 /* cheesy avoidance of looking up the flag name.. */
2515 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2518 flag = 1 << (pos - 1);
2520 added |= flag, removed &= ~flag;
2522 removed |= flag, added &= ~flag;
2527 *premoved = removed;
2532 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2534 unsigned long before, after, added, removed;
2535 struct userNode *uNode;
2537 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2538 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2540 hi->flags = (hi->flags | added) & ~removed;
2541 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2543 /* Strip helping flag if they're only a support helper and not
2544 * currently in #support. */
2545 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2546 struct channelList *schannels;
2548 schannels = chanserv_support_channels();
2549 for (ii = 0; ii < schannels->used; ++ii)
2550 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2552 if (ii == schannels->used)
2553 HANDLE_CLEAR_FLAG(hi, HELPING);
2556 if (after && !before) {
2557 /* Add user to current helper list. */
2558 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2559 userList_append(&curr_helpers, uNode);
2560 } else if (!after && before) {
2561 /* Remove user from current helper list. */
2562 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2563 userList_remove(&curr_helpers, uNode);
2570 set_list(struct userNode *user, struct handle_info *hi, int override)
2574 char *set_display[] = {
2575 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2576 "EMAIL", "MAXLOGINS", "LANGUAGE", "DEVNULL"
2579 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2581 /* Do this so options are presented in a consistent order. */
2582 for (i = 0; i < ArrayLength(set_display); ++i)
2583 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2584 opt(user, hi, override, 0, NULL);
2587 static NICKSERV_FUNC(cmd_set)
2589 struct handle_info *hi;
2592 hi = user->handle_info;
2594 set_list(user, hi, 0);
2597 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2598 reply("NSMSG_INVALID_OPTION", argv[1]);
2601 return opt(user, hi, 0, argc-1, argv+1);
2604 static NICKSERV_FUNC(cmd_oset)
2606 struct handle_info *hi;
2607 struct svccmd *subcmd;
2609 char cmdname[MAXLEN];
2611 NICKSERV_MIN_PARMS(2);
2613 if (!(hi = get_victim_oper(user, argv[1])))
2617 set_list(user, hi, 0);
2621 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2622 reply("NSMSG_INVALID_OPTION", argv[2]);
2626 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2627 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2628 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2631 return opt(user, hi, 1, argc-2, argv+2);
2634 static OPTION_FUNC(opt_info)
2638 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2640 hi->infoline = NULL;
2642 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2646 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2647 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2651 static OPTION_FUNC(opt_devnull)
2653 const char *devnull_name;
2654 struct devnull_class *devnull_c;
2658 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2661 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2664 devnull_name = unsplit_string(argv+1, argc-1, NULL);
2665 devnull_c = devnull_find_name(devnull_name);
2667 hi->devnull_id = devnull_c->id;
2671 if(hi->devnull_id) {
2672 devnull_c = devnull_find_id(hi->devnull_id);
2674 devnull_name = devnull_c->name;
2676 devnull_name = user_find_message(user, "MSG_NONE");
2680 devnull_name = user_find_message(user, "MSG_NONE");
2681 send_message(user, nickserv, "NSMSG_SET_DEVNULL", devnull_name);
2685 void nickserv_devnull_delete(unsigned int devnull_id) {
2687 struct handle_info *hi;
2689 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2691 if (hi->devnull_id == devnull_id) {
2697 static OPTION_FUNC(opt_website)
2699 const char *website;
2702 if (!HANDLE_FLAGGED(user->handle_info, BOT)) {
2703 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2706 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2710 website = unsplit_string(argv+1, argc-1, NULL);
2711 hi->website = strdup(website);
2714 if (HANDLE_FLAGGED(user->handle_info, BOT)) {
2715 website = hi->website ? hi->website : user_find_message(user, "MSG_NONE");
2716 send_message(user, nickserv, "NSMSG_SET_WEBSITE", website);
2721 static OPTION_FUNC(opt_width)
2724 hi->screen_width = strtoul(argv[1], NULL, 0);
2726 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2727 hi->screen_width = MIN_LINE_SIZE;
2728 else if (hi->screen_width > MAX_LINE_SIZE)
2729 hi->screen_width = MAX_LINE_SIZE;
2731 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2735 static OPTION_FUNC(opt_tablewidth)
2738 hi->table_width = strtoul(argv[1], NULL, 0);
2740 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2741 hi->table_width = MIN_LINE_SIZE;
2742 else if (hi->screen_width > MAX_LINE_SIZE)
2743 hi->table_width = MAX_LINE_SIZE;
2745 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2749 static OPTION_FUNC(opt_color)
2752 if (enabled_string(argv[1]))
2753 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2754 else if (disabled_string(argv[1]))
2755 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2757 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2762 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2766 static OPTION_FUNC(opt_privmsg)
2769 if (enabled_string(argv[1]))
2770 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2771 else if (disabled_string(argv[1]))
2772 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2774 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2779 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2783 static OPTION_FUNC(opt_autohide)
2786 if (enabled_string(argv[1]))
2787 HANDLE_SET_FLAG(hi, AUTOHIDE);
2788 else if (disabled_string(argv[1]))
2789 HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
2791 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2796 send_message(user, nickserv, "NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
2800 static OPTION_FUNC(opt_style)
2805 if (!irccasecmp(argv[1], "Zoot"))
2806 hi->userlist_style = HI_STYLE_ZOOT;
2807 else if (!irccasecmp(argv[1], "def"))
2808 hi->userlist_style = HI_STYLE_DEF;
2811 switch (hi->userlist_style) {
2820 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2824 static OPTION_FUNC(opt_password)
2827 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2832 cryptpass(argv[1], hi->passwd);
2834 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2840 static OPTION_FUNC(opt_flags)
2843 unsigned int ii, flen;
2846 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2851 nickserv_apply_flags(user, hi, argv[1]);
2853 for (ii = flen = 0; handle_flags[ii]; ii++)
2854 if (hi->flags & (1 << ii))
2855 flags[flen++] = handle_flags[ii];
2858 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2860 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2864 static OPTION_FUNC(opt_email)
2868 if (!is_valid_email_addr(argv[1])) {
2869 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2872 if ((str = mail_prohibited_address(argv[1]))) {
2873 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2876 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2877 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2879 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2881 nickserv_set_email_addr(hi, argv[1]);
2883 nickserv_eat_cookie(hi->cookie);
2884 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2887 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2891 static OPTION_FUNC(opt_maxlogins)
2893 unsigned char maxlogins;
2895 maxlogins = strtoul(argv[1], NULL, 0);
2896 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2897 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2900 hi->maxlogins = maxlogins;
2902 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2903 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2907 static OPTION_FUNC(opt_language)
2909 struct language *lang;
2911 lang = language_find(argv[1]);
2912 if (irccasecmp(lang->name, argv[1]))
2913 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2914 hi->language = lang;
2916 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2920 static OPTION_FUNC(opt_karma)
2923 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2928 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2929 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2930 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2931 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2933 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2937 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2942 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2943 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2945 if ((user->handle_info->opserv_level < target->opserv_level)
2946 || ((user->handle_info->opserv_level == target->opserv_level)
2947 && (user->handle_info->opserv_level < 1000))) {
2948 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2951 if ((user->handle_info->opserv_level < new_level)
2952 || ((user->handle_info->opserv_level == new_level)
2953 && (user->handle_info->opserv_level < 1000))) {
2954 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2957 if (user->handle_info == target) {
2958 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2961 if (target->opserv_level == new_level)
2963 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2964 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2965 target->opserv_level = new_level;
2970 oper_try_set_staff_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2971 if (!oper_has_access(user, bot, nickserv_conf.modstaff_level, 0))
2973 if ((user->handle_info->opserv_level < target->opserv_level)
2974 || ((user->handle_info->opserv_level == target->opserv_level)
2975 && (user->handle_info->opserv_level < 1000))) {
2976 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2979 if (target->staff_level == new_level)
2981 log_module(NS_LOG, LOG_INFO, "Account %s setting staff level for account %s to %d (from %d).",
2982 user->handle_info->handle, target->handle, new_level, target->staff_level);
2983 target->staff_level = new_level;
2987 static OPTION_FUNC(opt_level)
2992 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2996 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2997 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
3001 static OPTION_FUNC(opt_staff_level)
3006 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3010 res = (argc > 1) ? oper_try_set_staff_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
3011 send_message(user, nickserv, "NSMSG_SET_STAFFLEVEL", hi->staff_level);
3015 static OPTION_FUNC(opt_epithet)
3018 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3022 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
3023 char *epithet = unsplit_string(argv+1, argc-1, NULL);
3026 if ((epithet[0] == '*') && !epithet[1])
3029 hi->epithet = strdup(epithet);
3033 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
3035 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
3039 static OPTION_FUNC(opt_title)
3044 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3048 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
3050 if (strchr(title, '.')) {
3051 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
3054 if ((strlen(user->handle_info->handle) + strlen(title) +
3055 strlen(titlehost_suffix) + 2) > HOSTLEN) {
3056 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
3061 if (!strcmp(title, "*")) {
3062 hi->fakehost = NULL;
3064 hi->fakehost = malloc(strlen(title)+2);
3065 hi->fakehost[0] = '.';
3066 strcpy(hi->fakehost+1, title);
3068 apply_fakehost(hi, NULL);
3069 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
3070 title = hi->fakehost + 1;
3074 title = user_find_message(user, "MSG_NONE");
3075 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
3079 static OPTION_FUNC(opt_fakehost)
3081 char mask[USERLEN + HOSTLEN + 2];
3085 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3089 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
3090 if(strlen(argv[1]) >= sizeof(mask)) {
3091 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
3095 safestrncpy(mask, argv[1], sizeof(mask));
3097 if ((host = strrchr(mask, '@')) && host != mask) {
3098 /* If ident@host was used and the user doesn't have access to set idents, do not change anything. */
3099 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3111 if (ident && strlen(ident) > USERLEN) {
3112 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3116 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
3117 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
3121 if (host && host[0]) {
3123 if (!strcmp(host, "*"))
3124 hi->fakehost = NULL;
3126 hi->fakehost = strdup(host);
3127 host = hi->fakehost;
3130 host = generate_fakehost(hi);
3133 free(hi->fakeident);
3134 if (!strcmp(ident, "*"))
3135 hi->fakeident = NULL;
3137 hi->fakeident = strdup(ident);
3138 ident = hi->fakeident;
3141 ident = generate_fakeident(hi, NULL);
3143 apply_fakehost(hi, NULL);
3145 host = generate_fakehost(hi);
3146 ident = generate_fakeident(hi, NULL);
3149 host = (char *) user_find_message(user, "MSG_NONE");
3151 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
3153 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
3157 static OPTION_FUNC(opt_fakeident)
3162 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3166 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3168 if (strlen(ident) > USERLEN) {
3169 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3172 free(hi->fakeident);
3173 if (!strcmp(ident, "*"))
3174 hi->fakeident = NULL;
3176 hi->fakeident = strdup(ident);
3177 ident = hi->fakeident;
3178 apply_fakehost(hi, NULL);
3180 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
3182 ident = user_find_message(user, "MSG_NONE");
3183 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
3187 static NICKSERV_FUNC(cmd_reclaim)
3189 struct nick_info *ni;
3190 struct userNode *victim;
3192 NICKSERV_MIN_PARMS(2);
3193 ni = dict_find(nickserv_nick_dict, argv[1], 0);
3195 reply("NSMSG_UNKNOWN_NICK", argv[1]);
3198 if (ni->owner != user->handle_info) {
3199 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
3202 victim = GetUserH(ni->nick);
3204 reply("MSG_NICK_UNKNOWN", ni->nick);
3207 if (victim == user) {
3208 reply("NSMSG_NICK_USER_YOU");
3211 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
3212 switch (nickserv_conf.reclaim_action) {
3213 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
3214 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
3215 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
3216 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
3221 static NICKSERV_FUNC(cmd_unregnick)
3224 struct handle_info *hi;
3225 struct nick_info *ni;
3227 hi = user->handle_info;
3228 nick = (argc < 2) ? user->nick : (const char*)argv[1];
3229 ni = dict_find(nickserv_nick_dict, nick, NULL);
3231 reply("NSMSG_UNKNOWN_NICK", nick);
3234 if (hi != ni->owner) {
3235 reply("NSMSG_NOT_YOUR_NICK", nick);
3238 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3243 static NICKSERV_FUNC(cmd_ounregnick)
3245 struct nick_info *ni;
3247 NICKSERV_MIN_PARMS(2);
3248 if (!(ni = get_nick_info(argv[1]))) {
3249 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
3252 if (!oper_outranks(user, ni->owner))
3254 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3259 static NICKSERV_FUNC(cmd_unregister)
3261 struct handle_info *hi;
3264 NICKSERV_MIN_PARMS(2);
3265 hi = user->handle_info;
3268 if (checkpass(passwd, hi->passwd)) {
3269 nickserv_unregister_handle(hi, user);
3272 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3273 reply("NSMSG_PASSWORD_INVALID");
3278 static NICKSERV_FUNC(cmd_ounregister)
3280 struct handle_info *hi;
3281 char reason[MAXLEN];
3284 NICKSERV_MIN_PARMS(2);
3285 if (!(hi = get_victim_oper(user, argv[1])))
3288 if (HANDLE_FLAGGED(hi, NODELETE)) {
3289 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
3293 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
3295 ((hi->flags & nickserv_conf.ounregister_flags)
3297 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
3298 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
3302 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
3303 global_message(MESSAGE_RECIPIENT_STAFF, reason);
3304 nickserv_unregister_handle(hi, user);
3308 static NICKSERV_FUNC(cmd_status)
3310 if (nickserv_conf.disable_nicks) {
3311 reply("NSMSG_GLOBAL_STATS_NONICK",
3312 dict_size(nickserv_handle_dict));
3314 if (user->handle_info) {
3316 struct nick_info *ni;
3317 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
3318 reply("NSMSG_HANDLE_STATS", cnt);
3320 reply("NSMSG_HANDLE_NONE");
3322 reply("NSMSG_GLOBAL_STATS",
3323 dict_size(nickserv_handle_dict),
3324 dict_size(nickserv_nick_dict));
3329 static NICKSERV_FUNC(cmd_ghost)
3331 struct userNode *target;
3332 char reason[MAXLEN];
3334 NICKSERV_MIN_PARMS(2);
3335 if (!(target = GetUserH(argv[1]))) {
3336 reply("MSG_NICK_UNKNOWN", argv[1]);
3339 if (target == user) {
3340 reply("NSMSG_CANNOT_GHOST_SELF");
3343 if (!target->handle_info || (target->handle_info != user->handle_info)) {
3344 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
3347 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
3348 DelUser(target, nickserv, 1, reason);
3349 reply("NSMSG_GHOST_KILLED", argv[1]);
3353 static NICKSERV_FUNC(cmd_vacation)
3355 HANDLE_SET_FLAG(user->handle_info, FROZEN);
3356 reply("NSMSG_ON_VACATION");
3360 static NICKSERV_FUNC(cmd_addnote)
3362 struct handle_info *hi;
3363 unsigned long duration;
3366 struct handle_note *prev;
3367 struct handle_note *note;
3369 /* Parse parameters and figure out values for note's fields. */
3370 NICKSERV_MIN_PARMS(4);
3371 hi = get_victim_oper(user, argv[1]);
3374 if(!strcmp(argv[2], "0"))
3376 else if(!(duration = ParseInterval(argv[2])))
3378 reply("MSG_INVALID_DURATION", argv[2]);
3381 if (duration > 2*365*86400) {
3382 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
3385 unsplit_string(argv + 3, argc - 3, text);
3386 WALK_NOTES(hi, prev, note) {}
3387 id = prev ? (prev->id + 1) : 1;
3389 /* Create the new note structure. */
3390 note = calloc(1, sizeof(*note) + strlen(text));
3392 note->expires = duration ? (now + duration) : 0;
3395 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
3396 strcpy(note->note, text);
3401 reply("NSMSG_NOTE_ADDED", id, hi->handle);
3405 static NICKSERV_FUNC(cmd_delnote)
3407 struct handle_info *hi;
3408 struct handle_note *prev;
3409 struct handle_note *note;
3412 NICKSERV_MIN_PARMS(3);
3413 hi = get_victim_oper(user, argv[1]);
3416 id = strtoul(argv[2], NULL, 10);
3417 WALK_NOTES(hi, prev, note) {
3418 if (id == note->id) {
3420 prev->next = note->next;
3422 hi->notes = note->next;
3424 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3428 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3433 nickserv_saxdb_write(struct saxdb_context *ctx) {
3435 struct handle_info *hi;
3438 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3440 assert(hi->id != 0);
3441 saxdb_start_record(ctx, iter_key(it), 0);
3443 struct handle_cookie *cookie = hi->cookie;
3446 switch (cookie->type) {
3447 case ACTIVATION: type = KEY_ACTIVATION; break;
3448 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3449 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3450 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3451 default: type = NULL; break;
3454 saxdb_start_record(ctx, KEY_COOKIE, 0);
3455 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3456 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3458 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3459 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3460 saxdb_end_record(ctx);
3464 struct handle_note *prev, *note;
3465 saxdb_start_record(ctx, KEY_NOTES, 0);
3466 WALK_NOTES(hi, prev, note) {
3467 snprintf(flags, sizeof(flags), "%d", note->id);
3468 saxdb_start_record(ctx, flags, 0);
3470 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3471 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3472 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3473 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3474 saxdb_end_record(ctx);
3476 saxdb_end_record(ctx);
3479 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3481 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3483 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3485 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3489 for (ii=flen=0; handle_flags[ii]; ++ii)
3490 if (hi->flags & (1 << ii))
3491 flags[flen++] = handle_flags[ii];
3493 saxdb_write_string(ctx, KEY_FLAGS, flags);
3495 saxdb_write_int(ctx, KEY_ID, hi->id);
3497 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3499 saxdb_write_int(ctx, KEY_DEVNULL, hi->devnull_id);
3501 saxdb_write_string(ctx, KEY_WEBSITE, hi->website);
3502 if (hi->last_quit_host[0])
3503 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3504 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3506 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3507 if (hi->masks->used)
3508 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3510 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3512 struct string_list *slist;
3513 struct nick_info *ni;
3515 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3516 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3517 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3521 if (hi->opserv_level)
3522 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3523 if (hi->staff_level)
3524 saxdb_write_int(ctx, KEY_STAFF_LEVEL, hi->staff_level);
3525 if (hi->language != lang_C)
3526 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3527 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3528 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3529 if (hi->screen_width)
3530 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3531 if (hi->table_width)
3532 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3533 flags[0] = hi->userlist_style;
3535 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3537 saxdb_start_record(ctx, KEY_AUTHLOG, 0);
3538 struct authlogEntry *authlog;
3540 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
3541 saxdb_start_record(ctx, strtab(++i), 0);
3542 saxdb_write_int(ctx, KEY_AUTHLOG_LOGIN_TIME, authlog->login_time);
3543 saxdb_write_int(ctx, KEY_AUTHLOG_LOGOUT_TIME, authlog->logout_time);
3544 saxdb_write_string(ctx, KEY_AUTHLOG_HOSTMASK, authlog->hostmask);
3545 if(authlog->quit_reason)
3546 saxdb_write_string(ctx, KEY_AUTHLOG_QUIT_REASON, authlog->quit_reason);
3547 saxdb_end_record(ctx);
3549 saxdb_end_record(ctx); //END KEY_AUTHLOG
3551 saxdb_end_record(ctx);
3556 static handle_merge_func_t *handle_merge_func_list;
3557 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3560 reg_handle_merge_func(handle_merge_func_t func)
3562 if (handle_merge_func_used == handle_merge_func_size) {
3563 if (handle_merge_func_size) {
3564 handle_merge_func_size <<= 1;
3565 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3567 handle_merge_func_size = 8;
3568 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3571 handle_merge_func_list[handle_merge_func_used++] = func;
3574 static NICKSERV_FUNC(cmd_merge)
3576 struct handle_info *hi_from, *hi_to;
3577 struct userNode *last_user;
3578 struct userData *cList, *cListNext;
3579 unsigned int ii, jj, n;
3580 char buffer[MAXLEN];
3582 NICKSERV_MIN_PARMS(3);
3584 if (!(hi_from = get_victim_oper(user, argv[1])))
3586 if (!(hi_to = get_victim_oper(user, argv[2])))
3588 if (hi_to == hi_from) {
3589 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3593 for (n=0; n<handle_merge_func_used; n++)
3594 handle_merge_func_list[n](user, hi_to, hi_from);
3596 /* Append "from" handle's nicks to "to" handle's nick list. */
3598 struct nick_info *last_ni;
3599 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3600 last_ni->next = hi_from->nicks;
3602 while (hi_from->nicks) {
3603 hi_from->nicks->owner = hi_to;
3604 hi_from->nicks = hi_from->nicks->next;
3607 /* Merge the hostmasks. */
3608 for (ii=0; ii<hi_from->masks->used; ii++) {
3609 char *mask = hi_from->masks->list[ii];
3610 for (jj=0; jj<hi_to->masks->used; jj++)
3611 if (match_ircglobs(hi_to->masks->list[jj], mask))
3613 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3614 string_list_append(hi_to->masks, strdup(mask));
3617 /* Merge the lists of authed users. */
3619 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3620 last_user->next_authed = hi_from->users;
3622 hi_to->users = hi_from->users;
3624 /* Repoint the old "from" handle's users. */
3625 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3626 last_user->handle_info = hi_to;
3628 hi_from->users = NULL;
3630 /* Merge channel userlists. */
3631 for (cList=hi_from->channels; cList; cList=cListNext) {
3632 struct userData *cList2;
3633 cListNext = cList->u_next;
3634 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3635 if (cList->channel == cList2->channel)
3637 if (cList2 && (cList2->access >= cList->access)) {
3638 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);
3639 /* keep cList2 in hi_to; remove cList from hi_from */
3640 del_channel_user(cList, 1);
3643 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);
3644 /* remove the lower-ranking cList2 from hi_to */
3645 del_channel_user(cList2, 1);
3647 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3649 /* cList needs to be moved from hi_from to hi_to */
3650 cList->handle = hi_to;
3651 /* Remove from linked list for hi_from */
3652 assert(!cList->u_prev);
3653 hi_from->channels = cList->u_next;
3655 cList->u_next->u_prev = cList->u_prev;
3656 /* Add to linked list for hi_to */
3657 cList->u_prev = NULL;
3658 cList->u_next = hi_to->channels;
3659 if (hi_to->channels)
3660 hi_to->channels->u_prev = cList;
3661 hi_to->channels = cList;
3665 /* Do they get an OpServ level promotion? */
3666 if (hi_from->opserv_level > hi_to->opserv_level)
3667 hi_to->opserv_level = hi_from->opserv_level;
3669 /* Do they get a staff level promotion? */
3670 if (hi_from->staff_level > hi_to->staff_level)
3671 hi_to->staff_level = hi_from->staff_level;
3673 /* What about last seen time? */
3674 if (hi_from->lastseen > hi_to->lastseen)
3675 hi_to->lastseen = hi_from->lastseen;
3677 /* New karma is the sum of the two original karmas. */
3678 hi_to->karma += hi_from->karma;
3680 /* Does a fakehost carry over? (This intentionally doesn't set it
3681 * for users previously attached to hi_to. They'll just have to
3684 if (hi_from->fakehost && !hi_to->fakehost)
3685 hi_to->fakehost = strdup(hi_from->fakehost);
3686 if (hi_from->fakeident && !hi_to->fakeident)
3687 hi_to->fakeident = strdup(hi_from->fakeident);
3689 /* Notify of success. */
3690 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3691 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3692 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3694 /* Unregister the "from" handle. */
3695 nickserv_unregister_handle(hi_from, NULL);
3700 #define NICKSERV_DISCRIM_FIELDS_AUTH 0x01
3701 #define NICKSERV_DISCRIM_FIELDS_EMAIL 0x02
3702 #define NICKSERV_DISCRIM_FIELDS_SEEN 0x04
3703 #define NICKSERV_DISCRIM_FIELDS_ACCESS 0x08
3704 #define NICKSERV_DISCRIM_FIELDS_FAKEHOST 0x10
3705 #define NICKSERV_DISCRIM_FIELDS_WEBSITE 0x20
3706 #define NICKSERV_DISCRIM_FIELDS_DEVNULL 0x40
3708 #define NICKSERV_DISCRIM_FIELD_COUNT 7
3710 struct nickserv_discrim {
3711 unsigned int show_fields;
3712 struct helpfile_table *output_table;
3713 int output_table_pos;
3714 unsigned int output_table_free_fields;
3716 unsigned long flags_on, flags_off;
3717 unsigned long min_registered, max_registered;
3718 unsigned long lastseen;
3720 int min_level, max_level;
3721 int min_karma, max_karma;
3722 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3723 const char *nickmask;
3724 const char *hostmask;
3725 const char *fakehostmask;
3726 const char *fakeidentmask;
3727 const char *website;
3728 const char *handlemask;
3729 const char *emailmask;
3730 const char *devnull_name;
3731 unsigned int devnull_id;
3734 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi, struct nickserv_discrim *discrim);
3736 struct discrim_apply_info {
3737 struct nickserv_discrim *discrim;
3738 discrim_search_func func;
3739 struct userNode *source;
3740 unsigned int matched;
3743 static struct nickserv_discrim *
3744 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3747 struct nickserv_discrim *discrim;
3749 discrim = malloc(sizeof(*discrim));
3750 memset(discrim, 0, sizeof(*discrim));
3751 discrim->min_level = 0;
3752 discrim->max_level = INT_MAX;
3753 discrim->limit = 50;
3754 discrim->min_registered = 0;
3755 discrim->max_registered = ULONG_MAX;
3756 discrim->lastseen = ULONG_MAX;
3757 discrim->min_karma = INT_MIN;
3758 discrim->max_karma = INT_MAX;
3760 for (i=0; i<argc; i++) {
3761 if (i == argc - 1) {
3762 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3765 if (!irccasecmp(argv[i], "limit")) {
3766 discrim->limit = strtoul(argv[++i], NULL, 0);
3767 } else if (!irccasecmp(argv[i], "flags")) {
3768 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3769 } else if (!irccasecmp(argv[i], "fields")) {
3770 char *fields = argv[++i];
3771 char *delimiter = strstr(fields, ",");
3775 if(!irccasecmp(fields, "auth"))
3776 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_AUTH;
3777 else if(!irccasecmp(fields, "email"))
3778 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_EMAIL;
3779 else if(!irccasecmp(fields, "seen"))
3780 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_SEEN;
3781 else if(!irccasecmp(fields, "access"))
3782 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_ACCESS;
3783 else if(!irccasecmp(fields, "fakehost"))
3784 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_FAKEHOST;
3785 else if(!irccasecmp(fields, "website") && IsBot(user))
3786 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_WEBSITE;
3787 else if(!irccasecmp(fields, "devnull"))
3788 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_DEVNULL;
3790 send_message(user, nickserv, "MSG_INVALID_FIELD", fields);
3795 fields = delimiter+1;
3797 delimiter = strstr(fields, ",");
3803 } else if (!irccasecmp(argv[i], "registered")) {
3804 const char *cmp = argv[++i];
3805 if (cmp[0] == '<') {
3806 if (cmp[1] == '=') {
3807 discrim->min_registered = now - ParseInterval(cmp+2);
3809 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3811 } else if (cmp[0] == '=') {
3812 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3813 } else if (cmp[0] == '>') {
3814 if (cmp[1] == '=') {
3815 discrim->max_registered = now - ParseInterval(cmp+2);
3817 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3820 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3822 } else if (!irccasecmp(argv[i], "seen")) {
3823 discrim->lastseen = now - ParseInterval(argv[++i]);
3824 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3825 discrim->nickmask = argv[++i];
3826 } else if (!irccasecmp(argv[i], "hostmask")) {
3828 if (!irccasecmp(argv[i], "exact")) {
3829 if (i == argc - 1) {
3830 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3833 discrim->hostmask_type = EXACT;
3834 } else if (!irccasecmp(argv[i], "subset")) {
3835 if (i == argc - 1) {
3836 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3839 discrim->hostmask_type = SUBSET;
3840 } else if (!irccasecmp(argv[i], "superset")) {
3841 if (i == argc - 1) {
3842 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3845 discrim->hostmask_type = SUPERSET;
3846 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3847 if (i == argc - 1) {
3848 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3851 discrim->hostmask_type = LASTQUIT;
3854 discrim->hostmask_type = SUPERSET;
3856 discrim->hostmask = argv[++i];
3857 } else if (!irccasecmp(argv[i], "fakehost")) {
3858 if (!irccasecmp(argv[++i], "*")) {
3859 discrim->fakehostmask = 0;
3861 discrim->fakehostmask = argv[i];
3863 } else if (!irccasecmp(argv[i], "fakeident")) {
3864 if (!irccasecmp(argv[++i], "*")) {
3865 discrim->fakeidentmask = 0;
3867 discrim->fakeidentmask = argv[i];
3869 } else if (!irccasecmp(argv[i], "website")) {
3870 if (!irccasecmp(argv[++i], "*")) {
3871 discrim->website = 0;
3873 discrim->website = argv[i];
3875 } else if (!irccasecmp(argv[i], "devnull")) {
3876 if (!irccasecmp(argv[++i], "*")) {
3877 discrim->devnull_id = 0;
3878 discrim->devnull_name = 0;
3880 struct devnull_class *th = devnull_find_name(argv[i]);
3882 send_message(user, nickserv, "OSMSG_DEVNULL_NOTFOUND", argv[i]);
3885 discrim->devnull_name = argv[i];
3886 discrim->devnull_id = th->id;
3888 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3889 if (!irccasecmp(argv[++i], "*")) {
3890 discrim->handlemask = 0;
3892 discrim->handlemask = argv[i];
3894 } else if (!irccasecmp(argv[i], "email")) {
3895 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3896 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3898 } else if (!irccasecmp(argv[++i], "*")) {
3899 discrim->emailmask = 0;
3901 discrim->emailmask = argv[i];
3903 } else if (!irccasecmp(argv[i], "access")) {
3904 const char *cmp = argv[++i];
3905 if (cmp[0] == '<') {
3906 if (discrim->min_level == 0) discrim->min_level = 1;
3907 if (cmp[1] == '=') {
3908 discrim->max_level = strtoul(cmp+2, NULL, 0);
3910 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3912 } else if (cmp[0] == '=') {
3913 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3914 } else if (cmp[0] == '>') {
3915 if (cmp[1] == '=') {
3916 discrim->min_level = strtoul(cmp+2, NULL, 0);
3918 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3921 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3923 } else if (!irccasecmp(argv[i], "karma")) {
3924 const char *cmp = argv[++i];
3925 if (cmp[0] == '<') {
3926 if (cmp[1] == '=') {
3927 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3929 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3931 } else if (cmp[0] == '=') {
3932 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3933 } else if (cmp[0] == '>') {
3934 if (cmp[1] == '=') {
3935 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3937 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3940 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3943 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3954 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3956 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3957 || (discrim->flags_off & hi->flags)
3958 || (discrim->min_registered > hi->registered)
3959 || (discrim->max_registered < hi->registered)
3960 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3961 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3962 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3963 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3964 || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website)))
3965 || (discrim->devnull_id && discrim->devnull_id != hi->devnull_id)
3966 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3967 || (discrim->min_level > hi->opserv_level)
3968 || (discrim->max_level < hi->opserv_level)
3969 || (discrim->min_karma > hi->karma)
3970 || (discrim->max_karma < hi->karma)
3974 if (discrim->hostmask) {
3976 for (i=0; i<hi->masks->used; i++) {
3977 const char *mask = hi->masks->list[i];
3978 if ((discrim->hostmask_type == SUBSET)
3979 && (match_ircglobs(discrim->hostmask, mask))) break;
3980 else if ((discrim->hostmask_type == EXACT)
3981 && !irccasecmp(discrim->hostmask, mask)) break;
3982 else if ((discrim->hostmask_type == SUPERSET)
3983 && (match_ircglobs(mask, discrim->hostmask))) break;
3984 else if ((discrim->hostmask_type == LASTQUIT)
3985 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3987 if (i==hi->masks->used) return 0;
3989 if (discrim->nickmask) {
3990 struct nick_info *nick = hi->nicks;
3992 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3995 if (!nick) return 0;
4001 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
4003 dict_iterator_t it, next;
4004 unsigned int matched;
4006 for (it = dict_first(nickserv_handle_dict), matched = 0;
4007 it && (matched < discrim->limit);
4009 next = iter_next(it);
4010 if (nickserv_discrim_match(discrim, iter_data(it))) {
4011 dsf(source, iter_data(it), discrim);
4019 search_print_func(struct userNode *source, struct handle_info *match, struct nickserv_discrim *discrim)
4021 if(discrim->show_fields) {
4023 if(discrim->output_table) {
4024 discrim->output_table->contents[++discrim->output_table_pos] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
4026 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
4027 discrim->output_table->contents[discrim->output_table_pos][i++] = match->handle;
4028 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
4029 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->email_addr ? match->email_addr : "*");
4030 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN) {
4032 char seenBuf[INTERVALLEN];
4035 } else if(match->lastseen == 0) {
4038 seen = intervalString(seenBuf, now - match->lastseen, source->handle_info);
4040 discrim->output_table_free_fields |= 1 << i;
4041 discrim->output_table->contents[discrim->output_table_pos][i++] = strdup(seen);
4043 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
4044 discrim->output_table->contents[discrim->output_table_pos][i++] = strtab(match->opserv_level);
4045 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
4046 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->fakehost ? match->fakehost : "*");
4047 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
4048 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->website ? match->website : "*");
4049 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL) {
4050 if(discrim->devnull_name)
4051 discrim->output_table->contents[discrim->output_table_pos][i++] = discrim->devnull_name;
4053 struct devnull_class *devnull = devnull_find_id(match->devnull_id);
4054 discrim->output_table->contents[discrim->output_table_pos][i++] = (devnull ? devnull->name : "*");
4059 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
4063 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match), UNUSED_ARG(struct nickserv_discrim *discrim))
4068 search_unregister_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
4070 if (oper_has_access(source, nickserv, match->opserv_level, 0))
4071 nickserv_unregister_handle(match, source);
4075 nickserv_sort_accounts_by_access(const void *a, const void *b)
4077 const struct handle_info *hi_a = *(const struct handle_info**)a;
4078 const struct handle_info *hi_b = *(const struct handle_info**)b;
4079 if (hi_a->opserv_level != hi_b->opserv_level)
4080 return hi_b->opserv_level - hi_a->opserv_level;
4081 return irccasecmp(hi_a->handle, hi_b->handle);
4085 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
4087 struct handle_info_list hil;
4088 struct helpfile_table tbl;
4093 memset(&hil, 0, sizeof(hil));
4094 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
4095 struct handle_info *hi = iter_data(it);
4096 if (hi->opserv_level)
4097 handle_info_list_append(&hil, hi);
4099 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
4100 tbl.length = hil.used + 1;
4102 tbl.flags = TABLE_NO_FREE;
4103 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4104 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
4107 for (ii = 0; ii < hil.used; ) {
4108 ary = malloc(tbl.width * sizeof(ary[0]));
4109 ary[0] = hil.list[ii]->handle;
4110 ary[1] = strtab(hil.list[ii]->opserv_level);
4111 tbl.contents[++ii] = ary;
4113 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4114 reply("MSG_MATCH_COUNT", hil.used);
4115 for (ii = 0; ii < hil.used; ii++)
4116 free(tbl.contents[ii]);
4121 static NICKSERV_FUNC(cmd_search)
4123 struct nickserv_discrim *discrim;
4124 discrim_search_func action;
4125 struct svccmd *subcmd;
4126 unsigned int matches;
4129 NICKSERV_MIN_PARMS(3);
4130 sprintf(buf, "search %s", argv[1]);
4131 subcmd = dict_find(nickserv_service->commands, buf, NULL);
4132 if (!irccasecmp(argv[1], "print"))
4133 action = search_print_func;
4134 else if (!irccasecmp(argv[1], "count"))
4135 action = search_count_func;
4136 else if (!irccasecmp(argv[1], "unregister"))
4137 action = search_unregister_func;
4139 reply("NSMSG_INVALID_ACTION", argv[1]);
4143 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
4146 discrim = nickserv_discrim_create(user, argc-2, argv+2);
4150 if (action == search_print_func)
4151 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
4152 else if (action == search_count_func)
4153 discrim->limit = INT_MAX;
4155 matches = nickserv_discrim_search(discrim, action, user);
4157 if(discrim->show_fields) {
4160 for(ii = 0; ii < NICKSERV_DISCRIM_FIELD_COUNT; ii++) {
4161 if(discrim->show_fields & (1 << ii)) width++;
4163 discrim->output_table = malloc(sizeof(discrim->output_table[0]));
4164 discrim->output_table->length = matches+1;
4165 discrim->output_table->width = width;
4166 discrim->output_table->flags = TABLE_NO_FREE;
4167 discrim->output_table->contents = malloc(discrim->output_table->length * sizeof(discrim->output_table->contents[0]));
4168 discrim->output_table->contents[0] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
4171 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
4172 discrim->output_table->contents[0][ii++] = "Auth";
4173 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
4174 discrim->output_table->contents[0][ii++] = "EMail";
4175 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN)
4176 discrim->output_table->contents[0][ii++] = "Seen";
4177 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
4178 discrim->output_table->contents[0][ii++] = "Access";
4179 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
4180 discrim->output_table->contents[0][ii++] = "Fakehost";
4181 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
4182 discrim->output_table->contents[0][ii++] = "Website";
4183 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
4184 discrim->output_table->contents[0][ii++] = "DevNull";
4186 nickserv_discrim_search(discrim, action, user);
4188 table_send(nickserv, user->nick, 0, NULL, *discrim->output_table);
4190 for(ii = 1; ii < discrim->output_table->length; ++ii) {
4192 for(ij = 0; ij < NICKSERV_DISCRIM_FIELD_COUNT; ij++) {
4193 if(discrim->output_table_free_fields & (1 << ij))
4194 free((char*)discrim->output_table->contents[ii][ij]);
4196 free(discrim->output_table->contents[ii]);
4198 free(discrim->output_table->contents[0]);
4199 free(discrim->output_table->contents);
4200 free(discrim->output_table);
4203 reply("MSG_MATCH_COUNT", matches);
4205 reply("MSG_NO_MATCHES");
4212 static MODCMD_FUNC(cmd_checkpass)
4214 struct handle_info *hi;
4216 NICKSERV_MIN_PARMS(3);
4217 if (!(hi = get_handle_info(argv[1]))) {
4218 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4221 if (checkpass(argv[2], hi->passwd))
4222 reply("CHECKPASS_YES");
4224 reply("CHECKPASS_NO");
4229 static MODCMD_FUNC(cmd_checkemail)
4231 struct handle_info *hi;
4233 NICKSERV_MIN_PARMS(3);
4234 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
4237 if (!hi->email_addr)
4238 reply("CHECKEMAIL_NOT_SET");
4239 else if (!irccasecmp(argv[2], hi->email_addr))
4240 reply("CHECKEMAIL_YES");
4242 reply("CHECKEMAIL_NO");
4247 nickserv_db_read_authlog(UNUSED_ARG(const char *key), void *data, void *extra)
4249 struct record_data *rd = data;
4250 struct handle_info *hi = extra;
4252 struct authlogEntry *authlog;
4253 authlog = malloc(sizeof(*authlog));
4255 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGIN_TIME, RECDB_QSTRING);
4256 authlog->login_time = str ? strtoul(str, NULL, 0) : 0;
4258 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGOUT_TIME, RECDB_QSTRING);
4259 authlog->logout_time = str ? strtoul(str, NULL, 0) : 0;
4261 str = database_get_data(rd->d.object, KEY_AUTHLOG_HOSTMASK, RECDB_QSTRING);
4262 authlog->hostmask = str ? strdup(str) : NULL;
4264 str = database_get_data(rd->d.object, KEY_AUTHLOG_QUIT_REASON, RECDB_QSTRING);
4265 authlog->quit_reason = str ? strdup(str) : NULL;
4267 authlog->user = NULL;
4269 authlog->next = NULL;
4271 //append it to the end of the list...
4272 struct authlogEntry *authlog_entry;
4274 hi->authlog = authlog;
4276 for(authlog_entry = hi->authlog; authlog_entry; authlog_entry = authlog_entry->next) {
4277 if(!authlog_entry->next) {
4278 authlog_entry->next = authlog;
4287 nickserv_db_read_handle(const char *handle, dict_t obj)
4290 struct string_list *masks, *slist;
4291 struct handle_info *hi;
4292 struct userNode *authed_users;
4293 struct userData *channel_list;
4298 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
4299 id = str ? strtoul(str, NULL, 0) : 0;
4300 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
4302 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
4305 if ((hi = get_handle_info(handle))) {
4306 authed_users = hi->users;
4307 channel_list = hi->channels;
4309 hi->channels = NULL;
4310 dict_remove(nickserv_handle_dict, hi->handle);
4312 authed_users = NULL;
4313 channel_list = NULL;
4315 hi = register_handle(handle, str, id);
4317 hi->users = authed_users;
4318 while (authed_users) {
4319 authed_users->handle_info = hi;
4320 authed_users = authed_users->next_authed;
4323 hi->channels = channel_list;
4324 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
4325 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
4326 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
4327 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
4328 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
4329 hi->language = language_find(str ? str : "C");
4330 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
4331 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
4332 str = database_get_data(obj, KEY_STAFF_LEVEL, RECDB_QSTRING);
4333 hi->staff_level = str ? strtoul(str, NULL, 0) : 0;
4334 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
4336 hi->infoline = strdup(str);
4337 str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING);
4339 hi->website = strdup(str);
4340 str = database_get_data(obj, KEY_DEVNULL, RECDB_QSTRING);
4341 hi->devnull_id = str ? strtoul(str, NULL, 0) : 0;
4342 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
4343 hi->registered = str ? strtoul(str, NULL, 0) : now;
4344 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
4345 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
4346 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
4347 hi->karma = str ? strtoul(str, NULL, 0) : 0;
4348 /* We want to read the nicks even if disable_nicks is set. This is so
4349 * that we don't lose the nick data entirely. */
4350 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
4352 for (ii=0; ii<slist->used; ii++)
4353 register_nick(slist->list[ii], hi);
4355 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
4357 for (ii=0; str[ii]; ii++)
4358 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
4360 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
4361 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
4362 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
4363 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
4364 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
4365 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
4366 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
4368 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
4370 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
4371 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
4373 nickserv_set_email_addr(hi, str);
4374 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
4376 hi->epithet = strdup(str);
4377 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
4379 hi->fakehost = strdup(str);
4380 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
4382 hi->fakeident = strdup(str);
4383 /* Read the "cookie" sub-database (if it exists). */
4384 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
4386 const char *data, *type, *expires, *cookie_str;
4387 struct handle_cookie *cookie;
4389 cookie = calloc(1, sizeof(*cookie));
4390 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
4391 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
4392 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
4393 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
4394 if (!type || !expires || !cookie_str) {
4395 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
4398 if (!irccasecmp(type, KEY_ACTIVATION))
4399 cookie->type = ACTIVATION;
4400 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
4401 cookie->type = PASSWORD_CHANGE;
4402 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
4403 cookie->type = EMAIL_CHANGE;
4404 else if (!irccasecmp(type, KEY_ALLOWAUTH))
4405 cookie->type = ALLOWAUTH;
4407 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
4410 cookie->expires = strtoul(expires, NULL, 0);
4411 if (cookie->expires < now)
4414 cookie->data = strdup(data);
4415 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
4419 nickserv_bake_cookie(cookie);
4421 nickserv_free_cookie(cookie);
4423 /* Read the "notes" sub-database (if it exists). */
4424 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
4427 struct handle_note *last_note;
4428 struct handle_note *note;
4431 for (it = dict_first(subdb); it; it = iter_next(it)) {
4432 const char *expires;
4436 const char *note_id;
4439 note_id = iter_key(it);
4440 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
4442 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
4445 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
4446 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
4447 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
4448 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
4449 if (!setter || !text || !set) {
4450 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
4453 note = calloc(1, sizeof(*note) + strlen(text));
4455 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
4456 note->set = strtoul(set, NULL, 10);
4457 note->id = strtoul(note_id, NULL, 10);
4458 safestrncpy(note->setter, setter, sizeof(note->setter));
4459 strcpy(note->note, text);
4461 last_note->next = note;
4467 if ((subdb = database_get_data(obj, KEY_AUTHLOG, RECDB_OBJECT)))
4468 dict_foreach(subdb, nickserv_db_read_authlog, hi);
4472 nickserv_saxdb_read(dict_t db) {
4474 struct record_data *rd;
4476 for (it=dict_first(db); it; it=iter_next(it)) {
4478 nickserv_db_read_handle(iter_key(it), rd->d.object);
4483 static NICKSERV_FUNC(cmd_mergedb)
4485 struct timeval start, stop;
4488 NICKSERV_MIN_PARMS(2);
4489 gettimeofday(&start, NULL);
4490 if (!(db = parse_database(argv[1]))) {
4491 reply("NSMSG_DB_UNREADABLE", argv[1]);
4494 nickserv_saxdb_read(db);
4496 gettimeofday(&stop, NULL);
4497 stop.tv_sec -= start.tv_sec;
4498 stop.tv_usec -= start.tv_usec;
4499 if (stop.tv_usec < 0) {
4501 stop.tv_usec += 1000000;
4503 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
4508 expire_handles(UNUSED_ARG(void *data))
4510 dict_iterator_t it, next;
4511 unsigned long expiry;
4512 struct handle_info *hi;
4514 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4515 next = iter_next(it);
4517 if ((hi->opserv_level > 0)
4519 || HANDLE_FLAGGED(hi, FROZEN)
4520 || HANDLE_FLAGGED(hi, NODELETE)) {
4523 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4524 if ((now - hi->lastseen) > expiry) {
4525 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
4526 nickserv_unregister_handle(hi, NULL);
4530 if (nickserv_conf.handle_expire_frequency)
4531 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4535 nickserv_load_dict(const char *fname)
4539 if (!(file = fopen(fname, "r"))) {
4540 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4543 while (fgets(line, sizeof(line), file)) {
4546 if (line[strlen(line)-1] == '\n')
4547 line[strlen(line)-1] = 0;
4548 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4551 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4554 static enum reclaim_action
4555 reclaim_action_from_string(const char *str) {
4557 return RECLAIM_NONE;
4558 else if (!irccasecmp(str, "warn"))
4559 return RECLAIM_WARN;
4560 else if (!irccasecmp(str, "svsnick"))
4561 return RECLAIM_SVSNICK;
4562 else if (!irccasecmp(str, "kill"))
4563 return RECLAIM_KILL;
4565 return RECLAIM_NONE;
4569 nickserv_conf_read(void)
4571 dict_t conf_node, child;
4575 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4576 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4579 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4581 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4582 if (nickserv_conf.valid_handle_regex_set)
4583 regfree(&nickserv_conf.valid_handle_regex);
4585 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4586 nickserv_conf.valid_handle_regex_set = !err;
4587 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4589 nickserv_conf.valid_handle_regex_set = 0;
4591 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4592 if (nickserv_conf.valid_nick_regex_set)
4593 regfree(&nickserv_conf.valid_nick_regex);
4595 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4596 nickserv_conf.valid_nick_regex_set = !err;
4597 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4599 nickserv_conf.valid_nick_regex_set = 0;
4601 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4603 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4604 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4605 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4606 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4607 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4608 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4609 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4610 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4611 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4612 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4613 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4614 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4615 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4616 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4617 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4618 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4619 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4620 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4621 str = database_get_data(conf_node, KEY_MODSTAFF_LEVEL, RECDB_QSTRING);
4622 nickserv_conf.modstaff_level = str ? strtoul(str, NULL, 0) : 800;
4623 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4624 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4625 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4626 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4627 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4628 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4629 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
4630 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
4631 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4633 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4634 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4635 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4637 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4638 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4639 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4641 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4642 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4643 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4644 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4645 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4646 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4647 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4648 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4649 str = database_get_data(conf_node, KEY_MAX_AUTHLOG_LEN, RECDB_QSTRING);
4650 nickserv_conf.max_authlog_len = str ? strtoul(str, NULL, 0) : 30;
4651 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4652 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4653 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4656 nickserv_conf.ounregister_flags = 0;
4658 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4661 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4663 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4665 nickserv_conf.handle_ts_mode = TS_IGNORE;
4666 else if (!irccasecmp(str, "ircu"))
4667 nickserv_conf.handle_ts_mode = TS_IRCU;
4669 nickserv_conf.handle_ts_mode = TS_IGNORE;
4670 if (!nickserv_conf.disable_nicks) {
4671 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4672 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4673 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4674 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4675 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4676 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4677 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4678 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4680 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4681 for (it=dict_first(child); it; it=iter_next(it)) {
4682 const char *key = iter_key(it), *value;
4686 if (!strncasecmp(key, "uc_", 3))
4687 flag = toupper(key[3]);
4688 else if (!strncasecmp(key, "lc_", 3))
4689 flag = tolower(key[3]);
4693 if ((pos = handle_inverse_flags[flag])) {
4694 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4695 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4698 if (nickserv_conf.weak_password_dict)
4699 dict_delete(nickserv_conf.weak_password_dict);
4700 nickserv_conf.weak_password_dict = dict_new();
4701 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4702 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4703 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4704 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4706 nickserv_load_dict(str);
4707 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4708 if (nickserv && str)
4709 NickChange(nickserv, str, 0);
4710 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4711 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4712 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4713 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4714 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4715 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4716 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4717 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4718 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4719 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4720 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4721 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4722 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4723 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4724 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4725 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4726 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4727 titlehost_suffix = str ? str : "example.net";
4728 str = conf_get_data("server/network", RECDB_QSTRING);
4729 nickserv_conf.network_name = str ? str : "some IRC network";
4730 if (!nickserv_conf.auth_policer_params) {
4731 nickserv_conf.auth_policer_params = policer_params_new();
4732 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4733 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4735 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4736 for (it=dict_first(child); it; it=iter_next(it))
4737 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4741 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4743 char newnick[NICKLEN+1];
4752 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4754 case RECLAIM_SVSNICK:
4756 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4757 } while (GetUserH(newnick));
4758 irc_svsnick(nickserv, user, newnick);
4761 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4762 DelUser(user, nickserv, 1, msg);
4768 nickserv_reclaim_p(void *data) {
4769 struct userNode *user = data;
4770 struct nick_info *ni = get_nick_info(user->nick);
4772 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4776 check_user_nick(struct userNode *user) {
4777 //check if this user is a pending LOC user
4778 if(pendingLOCUsers) {
4779 struct pendingLOCUser *pending, *next, *prev = NULL;
4781 for(pending = pendingLOCUsers; pending; pending = next) {
4782 next = pending->next;
4784 if(user->handle_info == pending->handle_info) {
4785 pending->authlog->user = user;
4786 free((char*) pending->authlog->hostmask);
4787 pending->authlog->hostmask = generate_hostmask(user, GENMASK_USENICK|GENMASK_STRICT_IDENT|GENMASK_NO_HIDING|GENMASK_STRICT_HOST);
4789 } else if(now - pending->time > 10)
4795 pendingLOCUsers = next;
4800 struct nick_info *ni;
4801 user->modes &= ~FLAGS_REGNICK;
4802 if (!(ni = get_nick_info(user->nick)))
4804 if (user->handle_info == ni->owner) {
4805 user->modes |= FLAGS_REGNICK;
4809 if (nickserv_conf.warn_nick_owned)
4810 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4811 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4813 if (nickserv_conf.auto_reclaim_delay)
4814 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4816 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4820 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4822 struct handle_info *hi = NULL;
4825 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4826 if ((hi == NULL) && (serial != 0)) {
4828 inttobase64(id, serial, IDLEN);
4829 hi = dict_find(nickserv_id_dict, id, NULL);
4833 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4834 && (timestamp != hi->registered)) {
4837 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4840 set_user_handle_info(user, hi, 0);
4842 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4847 handle_nick_change(struct userNode *user, const char *old_nick)
4849 struct handle_info *hi;
4851 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4852 dict_remove(nickserv_allow_auth_dict, old_nick);
4853 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4855 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4856 check_user_nick(user);
4860 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), const char *why)
4862 if(user->handle_info) {
4863 //check if theres an open authlog entry
4864 struct authlogEntry *authlog;
4865 for(authlog = user->handle_info->authlog; authlog; authlog = authlog->next) {
4866 if(authlog->user == user) {
4867 authlog->user = NULL;
4868 authlog->logout_time = now;
4869 authlog->quit_reason = strdup(why);
4874 dict_remove(nickserv_allow_auth_dict, user->nick);
4875 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4876 set_user_handle_info(user, NULL, 0);
4879 static struct modcmd *
4880 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4882 if (min_level > 0) {
4884 sprintf(buf, "%u", min_level);
4885 if (must_be_qualified) {
4886 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4888 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4890 } else if (min_level == 0) {
4891 if (must_be_qualified) {
4892 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4894 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4897 if (must_be_qualified) {
4898 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4900 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4906 nickserv_db_cleanup(void)
4908 unreg_del_user_func(nickserv_remove_user);
4909 userList_clean(&curr_helpers);
4910 policer_params_delete(nickserv_conf.auth_policer_params);
4911 dict_delete(nickserv_handle_dict);
4912 dict_delete(nickserv_nick_dict);
4913 dict_delete(nickserv_opt_dict);
4914 dict_delete(nickserv_allow_auth_dict);
4915 dict_delete(nickserv_email_dict);
4916 dict_delete(nickserv_id_dict);
4917 dict_delete(nickserv_conf.weak_password_dict);
4918 free(auth_func_list);
4919 free(unreg_func_list);
4921 free(allowauth_func_list);
4922 free(handle_merge_func_list);
4923 free(failpw_func_list);
4924 if (nickserv_conf.valid_handle_regex_set)
4925 regfree(&nickserv_conf.valid_handle_regex);
4926 if (nickserv_conf.valid_nick_regex_set)
4927 regfree(&nickserv_conf.valid_nick_regex);
4928 struct pendingLOCUser *pending, *next;
4929 for(pending = pendingLOCUsers; pending; pending = next) {
4930 next = pending->next;
4933 pendingLOCUsers = NULL;
4937 init_nickserv(const char *nick)
4940 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4941 reg_new_user_func(check_user_nick);
4942 reg_nick_change_func(handle_nick_change);
4943 reg_del_user_func(nickserv_remove_user);
4944 reg_account_func(handle_account);
4946 /* set up handle_inverse_flags */
4947 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4948 for (i=0; handle_flags[i]; i++) {
4949 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4950 flag_access_levels[i] = 0;
4953 conf_register_reload(nickserv_conf_read);
4954 nickserv_opt_dict = dict_new();
4955 nickserv_email_dict = dict_new();
4956 dict_set_free_keys(nickserv_email_dict, free);
4957 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4959 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4960 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4961 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4962 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4963 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4964 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4965 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4966 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4967 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4968 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4969 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4970 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4971 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4972 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4973 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4974 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4975 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4976 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4977 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4978 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4979 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4980 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4981 if (!nickserv_conf.disable_nicks) {
4982 /* nick management commands */
4983 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4984 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4985 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4986 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4987 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4988 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4990 if (nickserv_conf.email_enabled) {
4991 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4992 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4993 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4994 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4995 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4996 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4998 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4999 /* miscellaneous commands */
5000 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
5001 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
5002 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
5003 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
5004 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
5005 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
5006 nickserv_define_func("AUTHLOG", cmd_authlog, -1, 1, 0);
5007 nickserv_define_func("OAUTHLOG", cmd_oauthlog, 0, 1, 0);
5009 dict_insert(nickserv_opt_dict, "INFO", opt_info);
5010 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
5011 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
5012 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
5013 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
5014 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
5015 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
5016 dict_insert(nickserv_opt_dict, "PASS", opt_password);
5017 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
5018 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
5019 dict_insert(nickserv_opt_dict, "WEBSITE", opt_website);
5020 dict_insert(nickserv_opt_dict, "DEVNULL", opt_devnull);
5021 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
5022 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
5023 dict_insert(nickserv_opt_dict, "STAFF", opt_staff_level);
5024 dict_insert(nickserv_opt_dict, "STAFF_LEVEL", opt_staff_level);
5025 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
5026 if (titlehost_suffix) {
5027 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
5028 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
5029 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
5031 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
5032 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
5033 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
5034 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
5036 nickserv_handle_dict = dict_new();
5037 dict_set_free_keys(nickserv_handle_dict, free);
5038 dict_set_free_data(nickserv_handle_dict, free_handle_info);
5040 nickserv_id_dict = dict_new();
5041 dict_set_free_keys(nickserv_id_dict, free);
5043 nickserv_nick_dict = dict_new();
5044 dict_set_free_data(nickserv_nick_dict, free);
5046 nickserv_allow_auth_dict = dict_new();
5048 userList_init(&curr_helpers);
5051 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
5052 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
5053 nickserv_service = service_register(nickserv);
5055 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
5056 reg_exit_func(nickserv_db_cleanup);
5057 if(nickserv_conf.handle_expire_frequency)
5058 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
5059 message_register_table(msgtab);