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
3707 #define NICKSERV_DISCRIM_FIELDS_ID 0x80
3709 #define NICKSERV_DISCRIM_FIELD_COUNT 8
3711 struct nickserv_discrim {
3712 unsigned int show_fields;
3713 struct helpfile_table *output_table;
3714 int output_table_pos;
3715 unsigned int output_table_free_fields;
3717 unsigned long flags_on, flags_off;
3718 unsigned long min_registered, max_registered;
3719 unsigned long lastseen;
3721 int min_level, max_level;
3722 unsigned int min_id, max_id;
3723 int min_karma, max_karma;
3724 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3725 const char *nickmask;
3726 const char *hostmask;
3727 const char *fakehostmask;
3728 const char *fakeidentmask;
3729 const char *website;
3730 const char *handlemask;
3731 const char *emailmask;
3732 const char *devnull_name;
3733 unsigned int devnull_id;
3736 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi, struct nickserv_discrim *discrim);
3738 struct discrim_apply_info {
3739 struct nickserv_discrim *discrim;
3740 discrim_search_func func;
3741 struct userNode *source;
3742 unsigned int matched;
3745 static struct nickserv_discrim *
3746 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3749 struct nickserv_discrim *discrim;
3751 discrim = malloc(sizeof(*discrim));
3752 memset(discrim, 0, sizeof(*discrim));
3753 discrim->min_level = 0;
3754 discrim->max_level = INT_MAX;
3755 discrim->limit = 50;
3756 discrim->min_registered = 0;
3757 discrim->max_registered = ULONG_MAX;
3758 discrim->lastseen = ULONG_MAX;
3759 discrim->min_karma = INT_MIN;
3760 discrim->max_karma = INT_MAX;
3761 discrim->min_id = 0;
3762 discrim->max_id = INT_MAX;
3765 for (i=0; i<argc; i++) {
3766 if (i == argc - 1) {
3767 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3770 if (!irccasecmp(argv[i], "limit")) {
3771 discrim->limit = strtoul(argv[++i], NULL, 0);
3772 } else if (!irccasecmp(argv[i], "flags")) {
3773 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3774 } else if (!irccasecmp(argv[i], "fields")) {
3775 char *fields = argv[++i];
3776 char *delimiter = strstr(fields, ",");
3780 if(!irccasecmp(fields, "auth"))
3781 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_AUTH;
3782 else if(!irccasecmp(fields, "email"))
3783 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_EMAIL;
3784 else if(!irccasecmp(fields, "seen"))
3785 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_SEEN;
3786 else if(!irccasecmp(fields, "access"))
3787 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_ACCESS;
3788 else if(!irccasecmp(fields, "id"))
3789 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_ID;
3790 else if(!irccasecmp(fields, "fakehost"))
3791 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_FAKEHOST;
3792 else if(!irccasecmp(fields, "website") && IsBot(user))
3793 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_WEBSITE;
3794 else if(!irccasecmp(fields, "devnull"))
3795 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_DEVNULL;
3797 send_message(user, nickserv, "MSG_INVALID_FIELD", fields);
3802 fields = delimiter+1;
3804 delimiter = strstr(fields, ",");
3810 } else if (!irccasecmp(argv[i], "registered")) {
3811 const char *cmp = argv[++i];
3812 if (cmp[0] == '<') {
3813 if (cmp[1] == '=') {
3814 discrim->min_registered = now - ParseInterval(cmp+2);
3816 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3818 } else if (cmp[0] == '=') {
3819 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3820 } else if (cmp[0] == '>') {
3821 if (cmp[1] == '=') {
3822 discrim->max_registered = now - ParseInterval(cmp+2);
3824 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3827 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3829 } else if (!irccasecmp(argv[i], "seen")) {
3830 discrim->lastseen = now - ParseInterval(argv[++i]);
3831 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3832 discrim->nickmask = argv[++i];
3833 } else if (!irccasecmp(argv[i], "hostmask")) {
3835 if (!irccasecmp(argv[i], "exact")) {
3836 if (i == argc - 1) {
3837 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3840 discrim->hostmask_type = EXACT;
3841 } else if (!irccasecmp(argv[i], "subset")) {
3842 if (i == argc - 1) {
3843 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3846 discrim->hostmask_type = SUBSET;
3847 } else if (!irccasecmp(argv[i], "superset")) {
3848 if (i == argc - 1) {
3849 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3852 discrim->hostmask_type = SUPERSET;
3853 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3854 if (i == argc - 1) {
3855 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3858 discrim->hostmask_type = LASTQUIT;
3861 discrim->hostmask_type = SUPERSET;
3863 discrim->hostmask = argv[++i];
3864 } else if (!irccasecmp(argv[i], "fakehost")) {
3865 if (!irccasecmp(argv[++i], "*")) {
3866 discrim->fakehostmask = 0;
3868 discrim->fakehostmask = argv[i];
3870 } else if (!irccasecmp(argv[i], "fakeident")) {
3871 if (!irccasecmp(argv[++i], "*")) {
3872 discrim->fakeidentmask = 0;
3874 discrim->fakeidentmask = argv[i];
3876 } else if (!irccasecmp(argv[i], "website")) {
3877 if (!irccasecmp(argv[++i], "*")) {
3878 discrim->website = 0;
3880 discrim->website = argv[i];
3882 } else if (!irccasecmp(argv[i], "devnull")) {
3883 if (!irccasecmp(argv[++i], "*")) {
3884 discrim->devnull_id = 0;
3885 discrim->devnull_name = 0;
3887 struct devnull_class *th = devnull_find_name(argv[i]);
3889 send_message(user, nickserv, "OSMSG_DEVNULL_NOTFOUND", argv[i]);
3892 discrim->devnull_name = argv[i];
3893 discrim->devnull_id = th->id;
3895 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3896 if (!irccasecmp(argv[++i], "*")) {
3897 discrim->handlemask = 0;
3899 discrim->handlemask = argv[i];
3901 } else if (!irccasecmp(argv[i], "email")) {
3902 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3903 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3905 } else if (!irccasecmp(argv[++i], "*")) {
3906 discrim->emailmask = 0;
3908 discrim->emailmask = argv[i];
3910 } else if (!irccasecmp(argv[i], "access")) {
3911 const char *cmp = argv[++i];
3912 if (cmp[0] == '<') {
3913 if (discrim->min_level == 0) discrim->min_level = 1;
3914 if (cmp[1] == '=') {
3915 discrim->max_level = strtoul(cmp+2, NULL, 0);
3917 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3919 } else if (cmp[0] == '=') {
3920 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3921 } else if (cmp[0] == '>') {
3922 if (cmp[1] == '=') {
3923 discrim->min_level = strtoul(cmp+2, NULL, 0);
3925 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3928 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3930 } else if (!irccasecmp(argv[i], "id")) {
3931 const char *cmp = argv[++i];
3932 if (cmp[0] == '<') {
3933 if (discrim->min_id == 0) discrim->min_id = 1;
3934 if (cmp[1] == '=') {
3935 discrim->max_id = strtoul(cmp+2, NULL, 0);
3937 discrim->max_id = strtoul(cmp+1, NULL, 0) - 1;
3939 } else if (cmp[0] == '=') {
3940 discrim->min_id = discrim->max_id = strtoul(cmp+1, NULL, 0);
3941 } else if (cmp[0] == '>') {
3942 if (cmp[1] == '=') {
3943 discrim->min_id = strtoul(cmp+2, NULL, 0);
3945 discrim->min_id = strtoul(cmp+1, NULL, 0) + 1;
3948 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3950 } else if (!irccasecmp(argv[i], "karma")) {
3951 const char *cmp = argv[++i];
3952 if (cmp[0] == '<') {
3953 if (cmp[1] == '=') {
3954 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3956 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3958 } else if (cmp[0] == '=') {
3959 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3960 } else if (cmp[0] == '>') {
3961 if (cmp[1] == '=') {
3962 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3964 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3967 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3970 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3981 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3983 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3984 || (discrim->flags_off & hi->flags)
3985 || (discrim->min_registered > hi->registered)
3986 || (discrim->max_registered < hi->registered)
3987 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3988 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3989 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3990 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3991 || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website)))
3992 || (discrim->devnull_id && discrim->devnull_id != hi->devnull_id)
3993 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3994 || (discrim->min_level > hi->opserv_level)
3995 || (discrim->max_level < hi->opserv_level)
3996 || (discrim->min_id > hi->id)
3997 || (discrim->max_id < hi->id)
3998 || (discrim->min_karma > hi->karma)
3999 || (discrim->max_karma < hi->karma)
4003 if (discrim->hostmask) {
4005 for (i=0; i<hi->masks->used; i++) {
4006 const char *mask = hi->masks->list[i];
4007 if ((discrim->hostmask_type == SUBSET)
4008 && (match_ircglobs(discrim->hostmask, mask))) break;
4009 else if ((discrim->hostmask_type == EXACT)
4010 && !irccasecmp(discrim->hostmask, mask)) break;
4011 else if ((discrim->hostmask_type == SUPERSET)
4012 && (match_ircglobs(mask, discrim->hostmask))) break;
4013 else if ((discrim->hostmask_type == LASTQUIT)
4014 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
4016 if (i==hi->masks->used) return 0;
4018 if (discrim->nickmask) {
4019 struct nick_info *nick = hi->nicks;
4021 if (match_ircglob(nick->nick, discrim->nickmask)) break;
4024 if (!nick) return 0;
4030 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
4032 dict_iterator_t it, next;
4033 unsigned int matched;
4035 for (it = dict_first(nickserv_handle_dict), matched = 0;
4036 it && (matched < discrim->limit);
4038 next = iter_next(it);
4039 if (nickserv_discrim_match(discrim, iter_data(it))) {
4040 dsf(source, iter_data(it), discrim);
4048 search_print_func(struct userNode *source, struct handle_info *match, struct nickserv_discrim *discrim)
4050 if(discrim->show_fields) {
4052 if(discrim->output_table) {
4053 discrim->output_table->contents[++discrim->output_table_pos] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
4055 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
4056 discrim->output_table->contents[discrim->output_table_pos][i++] = match->handle;
4057 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
4058 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->email_addr ? match->email_addr : "*");
4059 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN) {
4061 char seenBuf[INTERVALLEN];
4064 } else if(match->lastseen == 0) {
4067 seen = intervalString(seenBuf, now - match->lastseen, source->handle_info);
4069 discrim->output_table_free_fields |= 1 << i;
4070 discrim->output_table->contents[discrim->output_table_pos][i++] = strdup(seen);
4072 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
4073 discrim->output_table->contents[discrim->output_table_pos][i++] = strtab(match->opserv_level);
4074 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ID)
4075 discrim->output_table->contents[discrim->output_table_pos][i++] = strtab(match->id);
4076 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
4077 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->fakehost ? match->fakehost : "*");
4078 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
4079 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->website ? match->website : "*");
4080 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL) {
4081 if(discrim->devnull_name)
4082 discrim->output_table->contents[discrim->output_table_pos][i++] = discrim->devnull_name;
4084 struct devnull_class *devnull = devnull_find_id(match->devnull_id);
4085 discrim->output_table->contents[discrim->output_table_pos][i++] = (devnull ? devnull->name : "*");
4090 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
4094 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match), UNUSED_ARG(struct nickserv_discrim *discrim))
4099 search_unregister_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
4101 if (oper_has_access(source, nickserv, match->opserv_level, 0))
4102 nickserv_unregister_handle(match, source);
4106 nickserv_sort_accounts_by_access(const void *a, const void *b)
4108 const struct handle_info *hi_a = *(const struct handle_info**)a;
4109 const struct handle_info *hi_b = *(const struct handle_info**)b;
4110 if (hi_a->opserv_level != hi_b->opserv_level)
4111 return hi_b->opserv_level - hi_a->opserv_level;
4112 return irccasecmp(hi_a->handle, hi_b->handle);
4116 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
4118 struct handle_info_list hil;
4119 struct helpfile_table tbl;
4124 memset(&hil, 0, sizeof(hil));
4125 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
4126 struct handle_info *hi = iter_data(it);
4127 if (hi->opserv_level)
4128 handle_info_list_append(&hil, hi);
4130 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
4131 tbl.length = hil.used + 1;
4133 tbl.flags = TABLE_NO_FREE;
4134 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4135 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
4138 for (ii = 0; ii < hil.used; ) {
4139 ary = malloc(tbl.width * sizeof(ary[0]));
4140 ary[0] = hil.list[ii]->handle;
4141 ary[1] = strtab(hil.list[ii]->opserv_level);
4142 tbl.contents[++ii] = ary;
4144 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4145 reply("MSG_MATCH_COUNT", hil.used);
4146 for (ii = 0; ii < hil.used; ii++)
4147 free(tbl.contents[ii]);
4152 static NICKSERV_FUNC(cmd_search)
4154 struct nickserv_discrim *discrim;
4155 discrim_search_func action;
4156 struct svccmd *subcmd;
4157 unsigned int matches;
4160 NICKSERV_MIN_PARMS(3);
4161 sprintf(buf, "search %s", argv[1]);
4162 subcmd = dict_find(nickserv_service->commands, buf, NULL);
4163 if (!irccasecmp(argv[1], "print"))
4164 action = search_print_func;
4165 else if (!irccasecmp(argv[1], "count"))
4166 action = search_count_func;
4167 else if (!irccasecmp(argv[1], "unregister"))
4168 action = search_unregister_func;
4170 reply("NSMSG_INVALID_ACTION", argv[1]);
4174 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
4177 discrim = nickserv_discrim_create(user, argc-2, argv+2);
4181 if (action == search_print_func)
4182 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
4183 else if (action == search_count_func)
4184 discrim->limit = INT_MAX;
4186 matches = nickserv_discrim_search(discrim, action, user);
4188 if(discrim->show_fields) {
4191 for(ii = 0; ii < NICKSERV_DISCRIM_FIELD_COUNT; ii++) {
4192 if(discrim->show_fields & (1 << ii)) width++;
4194 discrim->output_table = malloc(sizeof(discrim->output_table[0]));
4195 discrim->output_table->length = matches+1;
4196 discrim->output_table->width = width;
4197 discrim->output_table->flags = TABLE_NO_FREE;
4198 discrim->output_table->contents = malloc(discrim->output_table->length * sizeof(discrim->output_table->contents[0]));
4199 discrim->output_table->contents[0] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
4202 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
4203 discrim->output_table->contents[0][ii++] = "Auth";
4204 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
4205 discrim->output_table->contents[0][ii++] = "EMail";
4206 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN)
4207 discrim->output_table->contents[0][ii++] = "Seen";
4208 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
4209 discrim->output_table->contents[0][ii++] = "Access";
4210 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ID)
4211 discrim->output_table->contents[0][ii++] = "ID";
4212 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
4213 discrim->output_table->contents[0][ii++] = "Fakehost";
4214 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
4215 discrim->output_table->contents[0][ii++] = "Website";
4216 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
4217 discrim->output_table->contents[0][ii++] = "DevNull";
4219 nickserv_discrim_search(discrim, action, user);
4221 table_send(nickserv, user->nick, 0, NULL, *discrim->output_table);
4223 for(ii = 1; ii < discrim->output_table->length; ++ii) {
4225 for(ij = 0; ij < NICKSERV_DISCRIM_FIELD_COUNT; ij++) {
4226 if(discrim->output_table_free_fields & (1 << ij))
4227 free((char*)discrim->output_table->contents[ii][ij]);
4229 free(discrim->output_table->contents[ii]);
4231 free(discrim->output_table->contents[0]);
4232 free(discrim->output_table->contents);
4233 free(discrim->output_table);
4236 reply("MSG_MATCH_COUNT", matches);
4238 reply("MSG_NO_MATCHES");
4245 static MODCMD_FUNC(cmd_checkpass)
4247 struct handle_info *hi;
4249 NICKSERV_MIN_PARMS(3);
4250 if (!(hi = get_handle_info(argv[1]))) {
4251 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4254 if (checkpass(argv[2], hi->passwd))
4255 reply("CHECKPASS_YES");
4257 reply("CHECKPASS_NO");
4262 static MODCMD_FUNC(cmd_checkemail)
4264 struct handle_info *hi;
4266 NICKSERV_MIN_PARMS(3);
4267 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
4270 if (!hi->email_addr)
4271 reply("CHECKEMAIL_NOT_SET");
4272 else if (!irccasecmp(argv[2], hi->email_addr))
4273 reply("CHECKEMAIL_YES");
4275 reply("CHECKEMAIL_NO");
4280 nickserv_db_read_authlog(UNUSED_ARG(const char *key), void *data, void *extra)
4282 struct record_data *rd = data;
4283 struct handle_info *hi = extra;
4285 struct authlogEntry *authlog;
4286 authlog = malloc(sizeof(*authlog));
4288 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGIN_TIME, RECDB_QSTRING);
4289 authlog->login_time = str ? strtoul(str, NULL, 0) : 0;
4291 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGOUT_TIME, RECDB_QSTRING);
4292 authlog->logout_time = str ? strtoul(str, NULL, 0) : 0;
4294 str = database_get_data(rd->d.object, KEY_AUTHLOG_HOSTMASK, RECDB_QSTRING);
4295 authlog->hostmask = str ? strdup(str) : NULL;
4297 str = database_get_data(rd->d.object, KEY_AUTHLOG_QUIT_REASON, RECDB_QSTRING);
4298 authlog->quit_reason = str ? strdup(str) : NULL;
4300 authlog->user = NULL;
4302 authlog->next = NULL;
4304 //append it to the end of the list...
4305 struct authlogEntry *authlog_entry;
4307 hi->authlog = authlog;
4309 for(authlog_entry = hi->authlog; authlog_entry; authlog_entry = authlog_entry->next) {
4310 if(!authlog_entry->next) {
4311 authlog_entry->next = authlog;
4320 nickserv_db_read_handle(const char *handle, dict_t obj)
4323 struct string_list *masks, *slist;
4324 struct handle_info *hi;
4325 struct userNode *authed_users;
4326 struct userData *channel_list;
4331 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
4332 id = str ? strtoul(str, NULL, 0) : 0;
4333 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
4335 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
4338 if ((hi = get_handle_info(handle))) {
4339 authed_users = hi->users;
4340 channel_list = hi->channels;
4342 hi->channels = NULL;
4343 dict_remove(nickserv_handle_dict, hi->handle);
4345 authed_users = NULL;
4346 channel_list = NULL;
4348 hi = register_handle(handle, str, id);
4350 hi->users = authed_users;
4351 while (authed_users) {
4352 authed_users->handle_info = hi;
4353 authed_users = authed_users->next_authed;
4356 hi->channels = channel_list;
4357 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
4358 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
4359 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
4360 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
4361 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
4362 hi->language = language_find(str ? str : "C");
4363 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
4364 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
4365 str = database_get_data(obj, KEY_STAFF_LEVEL, RECDB_QSTRING);
4366 hi->staff_level = str ? strtoul(str, NULL, 0) : 0;
4367 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
4369 hi->infoline = strdup(str);
4370 str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING);
4372 hi->website = strdup(str);
4373 str = database_get_data(obj, KEY_DEVNULL, RECDB_QSTRING);
4374 hi->devnull_id = str ? strtoul(str, NULL, 0) : 0;
4375 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
4376 hi->registered = str ? strtoul(str, NULL, 0) : now;
4377 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
4378 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
4379 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
4380 hi->karma = str ? strtoul(str, NULL, 0) : 0;
4381 /* We want to read the nicks even if disable_nicks is set. This is so
4382 * that we don't lose the nick data entirely. */
4383 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
4385 for (ii=0; ii<slist->used; ii++)
4386 register_nick(slist->list[ii], hi);
4388 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
4390 for (ii=0; str[ii]; ii++)
4391 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
4393 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
4394 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
4395 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
4396 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
4397 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
4398 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
4399 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
4401 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
4403 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
4404 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
4406 nickserv_set_email_addr(hi, str);
4407 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
4409 hi->epithet = strdup(str);
4410 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
4412 hi->fakehost = strdup(str);
4413 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
4415 hi->fakeident = strdup(str);
4416 /* Read the "cookie" sub-database (if it exists). */
4417 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
4419 const char *data, *type, *expires, *cookie_str;
4420 struct handle_cookie *cookie;
4422 cookie = calloc(1, sizeof(*cookie));
4423 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
4424 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
4425 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
4426 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
4427 if (!type || !expires || !cookie_str) {
4428 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
4431 if (!irccasecmp(type, KEY_ACTIVATION))
4432 cookie->type = ACTIVATION;
4433 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
4434 cookie->type = PASSWORD_CHANGE;
4435 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
4436 cookie->type = EMAIL_CHANGE;
4437 else if (!irccasecmp(type, KEY_ALLOWAUTH))
4438 cookie->type = ALLOWAUTH;
4440 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
4443 cookie->expires = strtoul(expires, NULL, 0);
4444 if (cookie->expires < now)
4447 cookie->data = strdup(data);
4448 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
4452 nickserv_bake_cookie(cookie);
4454 nickserv_free_cookie(cookie);
4456 /* Read the "notes" sub-database (if it exists). */
4457 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
4460 struct handle_note *last_note;
4461 struct handle_note *note;
4464 for (it = dict_first(subdb); it; it = iter_next(it)) {
4465 const char *expires;
4469 const char *note_id;
4472 note_id = iter_key(it);
4473 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
4475 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
4478 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
4479 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
4480 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
4481 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
4482 if (!setter || !text || !set) {
4483 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
4486 note = calloc(1, sizeof(*note) + strlen(text));
4488 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
4489 note->set = strtoul(set, NULL, 10);
4490 note->id = strtoul(note_id, NULL, 10);
4491 safestrncpy(note->setter, setter, sizeof(note->setter));
4492 strcpy(note->note, text);
4494 last_note->next = note;
4500 if ((subdb = database_get_data(obj, KEY_AUTHLOG, RECDB_OBJECT)))
4501 dict_foreach(subdb, nickserv_db_read_authlog, hi);
4505 nickserv_saxdb_read(dict_t db) {
4507 struct record_data *rd;
4509 for (it=dict_first(db); it; it=iter_next(it)) {
4511 nickserv_db_read_handle(iter_key(it), rd->d.object);
4516 static NICKSERV_FUNC(cmd_mergedb)
4518 struct timeval start, stop;
4521 NICKSERV_MIN_PARMS(2);
4522 gettimeofday(&start, NULL);
4523 if (!(db = parse_database(argv[1]))) {
4524 reply("NSMSG_DB_UNREADABLE", argv[1]);
4527 nickserv_saxdb_read(db);
4529 gettimeofday(&stop, NULL);
4530 stop.tv_sec -= start.tv_sec;
4531 stop.tv_usec -= start.tv_usec;
4532 if (stop.tv_usec < 0) {
4534 stop.tv_usec += 1000000;
4536 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
4541 expire_handles(UNUSED_ARG(void *data))
4543 dict_iterator_t it, next;
4544 unsigned long expiry;
4545 struct handle_info *hi;
4547 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4548 next = iter_next(it);
4550 if ((hi->opserv_level > 0)
4552 || HANDLE_FLAGGED(hi, FROZEN)
4553 || HANDLE_FLAGGED(hi, NODELETE)) {
4556 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4557 if ((now - hi->lastseen) > expiry) {
4558 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
4559 nickserv_unregister_handle(hi, NULL);
4563 if (nickserv_conf.handle_expire_frequency)
4564 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4568 nickserv_load_dict(const char *fname)
4572 if (!(file = fopen(fname, "r"))) {
4573 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4576 while (fgets(line, sizeof(line), file)) {
4579 if (line[strlen(line)-1] == '\n')
4580 line[strlen(line)-1] = 0;
4581 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4584 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4587 static enum reclaim_action
4588 reclaim_action_from_string(const char *str) {
4590 return RECLAIM_NONE;
4591 else if (!irccasecmp(str, "warn"))
4592 return RECLAIM_WARN;
4593 else if (!irccasecmp(str, "svsnick"))
4594 return RECLAIM_SVSNICK;
4595 else if (!irccasecmp(str, "kill"))
4596 return RECLAIM_KILL;
4598 return RECLAIM_NONE;
4602 nickserv_conf_read(void)
4604 dict_t conf_node, child;
4608 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4609 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4612 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4614 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4615 if (nickserv_conf.valid_handle_regex_set)
4616 regfree(&nickserv_conf.valid_handle_regex);
4618 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4619 nickserv_conf.valid_handle_regex_set = !err;
4620 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4622 nickserv_conf.valid_handle_regex_set = 0;
4624 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4625 if (nickserv_conf.valid_nick_regex_set)
4626 regfree(&nickserv_conf.valid_nick_regex);
4628 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4629 nickserv_conf.valid_nick_regex_set = !err;
4630 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4632 nickserv_conf.valid_nick_regex_set = 0;
4634 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4636 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4637 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4638 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4639 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4640 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4641 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4642 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4643 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4644 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4645 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4646 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4647 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4648 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4649 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4650 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4651 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4652 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4653 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4654 str = database_get_data(conf_node, KEY_MODSTAFF_LEVEL, RECDB_QSTRING);
4655 nickserv_conf.modstaff_level = str ? strtoul(str, NULL, 0) : 800;
4656 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4657 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4658 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4659 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4660 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4661 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4662 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
4663 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
4664 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4666 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4667 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4668 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4670 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4671 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4672 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4674 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4675 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4676 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4677 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4678 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4679 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4680 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4681 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4682 str = database_get_data(conf_node, KEY_MAX_AUTHLOG_LEN, RECDB_QSTRING);
4683 nickserv_conf.max_authlog_len = str ? strtoul(str, NULL, 0) : 30;
4684 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4685 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4686 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4689 nickserv_conf.ounregister_flags = 0;
4691 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4694 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4696 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4698 nickserv_conf.handle_ts_mode = TS_IGNORE;
4699 else if (!irccasecmp(str, "ircu"))
4700 nickserv_conf.handle_ts_mode = TS_IRCU;
4702 nickserv_conf.handle_ts_mode = TS_IGNORE;
4703 if (!nickserv_conf.disable_nicks) {
4704 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4705 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4706 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4707 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4708 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4709 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4710 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4711 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4713 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4714 for (it=dict_first(child); it; it=iter_next(it)) {
4715 const char *key = iter_key(it), *value;
4719 if (!strncasecmp(key, "uc_", 3))
4720 flag = toupper(key[3]);
4721 else if (!strncasecmp(key, "lc_", 3))
4722 flag = tolower(key[3]);
4726 if ((pos = handle_inverse_flags[flag])) {
4727 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4728 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4731 if (nickserv_conf.weak_password_dict)
4732 dict_delete(nickserv_conf.weak_password_dict);
4733 nickserv_conf.weak_password_dict = dict_new();
4734 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4735 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4736 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4737 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4739 nickserv_load_dict(str);
4740 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4741 if (nickserv && str)
4742 NickChange(nickserv, str, 0);
4743 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4744 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4745 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4746 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4747 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4748 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4749 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4750 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4751 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4752 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4753 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4754 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4755 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4756 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4757 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4758 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4759 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4760 titlehost_suffix = str ? str : "example.net";
4761 str = conf_get_data("server/network", RECDB_QSTRING);
4762 nickserv_conf.network_name = str ? str : "some IRC network";
4763 if (!nickserv_conf.auth_policer_params) {
4764 nickserv_conf.auth_policer_params = policer_params_new();
4765 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4766 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4768 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4769 for (it=dict_first(child); it; it=iter_next(it))
4770 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4774 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4776 char newnick[NICKLEN+1];
4785 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4787 case RECLAIM_SVSNICK:
4789 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4790 } while (GetUserH(newnick));
4791 irc_svsnick(nickserv, user, newnick);
4794 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4795 DelUser(user, nickserv, 1, msg);
4801 nickserv_reclaim_p(void *data) {
4802 struct userNode *user = data;
4803 struct nick_info *ni = get_nick_info(user->nick);
4805 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4809 check_user_nick(struct userNode *user) {
4810 //check if this user is a pending LOC user
4811 if(pendingLOCUsers) {
4812 struct pendingLOCUser *pending, *next, *prev = NULL;
4814 for(pending = pendingLOCUsers; pending; pending = next) {
4815 next = pending->next;
4817 if(user->handle_info == pending->handle_info) {
4818 pending->authlog->user = user;
4819 free((char*) pending->authlog->hostmask);
4820 pending->authlog->hostmask = generate_hostmask(user, GENMASK_USENICK|GENMASK_STRICT_IDENT|GENMASK_NO_HIDING|GENMASK_STRICT_HOST);
4822 } else if(now - pending->time > 10)
4828 pendingLOCUsers = next;
4833 struct nick_info *ni;
4834 user->modes &= ~FLAGS_REGNICK;
4835 if (!(ni = get_nick_info(user->nick)))
4837 if (user->handle_info == ni->owner) {
4838 user->modes |= FLAGS_REGNICK;
4842 if (nickserv_conf.warn_nick_owned)
4843 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4844 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4846 if (nickserv_conf.auto_reclaim_delay)
4847 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4849 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4853 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4855 struct handle_info *hi = NULL;
4858 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4859 if ((hi == NULL) && (serial != 0)) {
4861 inttobase64(id, serial, IDLEN);
4862 hi = dict_find(nickserv_id_dict, id, NULL);
4866 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4867 && (timestamp != hi->registered)) {
4870 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4873 set_user_handle_info(user, hi, 0);
4875 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4880 handle_nick_change(struct userNode *user, const char *old_nick)
4882 struct handle_info *hi;
4884 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4885 dict_remove(nickserv_allow_auth_dict, old_nick);
4886 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4888 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4889 check_user_nick(user);
4893 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), const char *why)
4895 if(user->handle_info) {
4896 //check if theres an open authlog entry
4897 struct authlogEntry *authlog;
4898 for(authlog = user->handle_info->authlog; authlog; authlog = authlog->next) {
4899 if(authlog->user == user) {
4900 authlog->user = NULL;
4901 authlog->logout_time = now;
4902 authlog->quit_reason = strdup(why);
4907 dict_remove(nickserv_allow_auth_dict, user->nick);
4908 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4909 set_user_handle_info(user, NULL, 0);
4912 static struct modcmd *
4913 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4915 if (min_level > 0) {
4917 sprintf(buf, "%u", min_level);
4918 if (must_be_qualified) {
4919 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4921 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4923 } else if (min_level == 0) {
4924 if (must_be_qualified) {
4925 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4927 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4930 if (must_be_qualified) {
4931 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4933 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4939 nickserv_db_cleanup(void)
4941 unreg_del_user_func(nickserv_remove_user);
4942 userList_clean(&curr_helpers);
4943 policer_params_delete(nickserv_conf.auth_policer_params);
4944 dict_delete(nickserv_handle_dict);
4945 dict_delete(nickserv_nick_dict);
4946 dict_delete(nickserv_opt_dict);
4947 dict_delete(nickserv_allow_auth_dict);
4948 dict_delete(nickserv_email_dict);
4949 dict_delete(nickserv_id_dict);
4950 dict_delete(nickserv_conf.weak_password_dict);
4951 free(auth_func_list);
4952 free(unreg_func_list);
4954 free(allowauth_func_list);
4955 free(handle_merge_func_list);
4956 free(failpw_func_list);
4957 if (nickserv_conf.valid_handle_regex_set)
4958 regfree(&nickserv_conf.valid_handle_regex);
4959 if (nickserv_conf.valid_nick_regex_set)
4960 regfree(&nickserv_conf.valid_nick_regex);
4961 struct pendingLOCUser *pending, *next;
4962 for(pending = pendingLOCUsers; pending; pending = next) {
4963 next = pending->next;
4966 pendingLOCUsers = NULL;
4970 init_nickserv(const char *nick)
4973 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4974 reg_new_user_func(check_user_nick);
4975 reg_nick_change_func(handle_nick_change);
4976 reg_del_user_func(nickserv_remove_user);
4977 reg_account_func(handle_account);
4979 /* set up handle_inverse_flags */
4980 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4981 for (i=0; handle_flags[i]; i++) {
4982 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4983 flag_access_levels[i] = 0;
4986 conf_register_reload(nickserv_conf_read);
4987 nickserv_opt_dict = dict_new();
4988 nickserv_email_dict = dict_new();
4989 dict_set_free_keys(nickserv_email_dict, free);
4990 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4992 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4993 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4994 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4995 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4996 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4997 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4998 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4999 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
5000 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
5001 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
5002 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
5003 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
5004 nickserv_define_func("SET", cmd_set, -1, 1, 0);
5005 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
5006 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
5007 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
5008 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
5009 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
5010 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
5011 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
5012 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
5013 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
5014 if (!nickserv_conf.disable_nicks) {
5015 /* nick management commands */
5016 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
5017 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
5018 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
5019 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
5020 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
5021 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
5023 if (nickserv_conf.email_enabled) {
5024 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
5025 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
5026 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
5027 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
5028 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
5029 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
5031 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
5032 /* miscellaneous commands */
5033 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
5034 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
5035 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
5036 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
5037 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
5038 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
5039 nickserv_define_func("AUTHLOG", cmd_authlog, -1, 1, 0);
5040 nickserv_define_func("OAUTHLOG", cmd_oauthlog, 0, 1, 0);
5042 dict_insert(nickserv_opt_dict, "INFO", opt_info);
5043 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
5044 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
5045 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
5046 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
5047 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
5048 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
5049 dict_insert(nickserv_opt_dict, "PASS", opt_password);
5050 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
5051 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
5052 dict_insert(nickserv_opt_dict, "WEBSITE", opt_website);
5053 dict_insert(nickserv_opt_dict, "DEVNULL", opt_devnull);
5054 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
5055 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
5056 dict_insert(nickserv_opt_dict, "STAFF", opt_staff_level);
5057 dict_insert(nickserv_opt_dict, "STAFF_LEVEL", opt_staff_level);
5058 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
5059 if (titlehost_suffix) {
5060 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
5061 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
5062 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
5064 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
5065 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
5066 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
5067 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
5069 nickserv_handle_dict = dict_new();
5070 dict_set_free_keys(nickserv_handle_dict, free);
5071 dict_set_free_data(nickserv_handle_dict, free_handle_info);
5073 nickserv_id_dict = dict_new();
5074 dict_set_free_keys(nickserv_id_dict, free);
5076 nickserv_nick_dict = dict_new();
5077 dict_set_free_data(nickserv_nick_dict, free);
5079 nickserv_allow_auth_dict = dict_new();
5081 userList_init(&curr_helpers);
5084 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
5085 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
5086 nickserv_service = service_register(nickserv);
5088 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
5089 reg_exit_func(nickserv_db_cleanup);
5090 if(nickserv_conf.handle_expire_frequency)
5091 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
5092 message_register_table(msgtab);