1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2008 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for gag_create(), opserv_bad_channel() */
33 # include "rx/rxposix.h"
36 #define NICKSERV_CONF_NAME "services/nickserv"
38 #define KEY_DISABLE_NICKS "disable_nicks"
39 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
40 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
41 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
42 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
43 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
44 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
45 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
46 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
47 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
48 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
49 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
50 #define KEY_MODOPER_LEVEL "modoper_level"
51 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
52 #define KEY_SET_TITLE_LEVEL "set_title_level"
53 #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
54 #define KEY_SET_FAKEIDENT_LEVEL "set_fakeident_level"
55 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
56 #define KEY_FLAG_LEVELS "flag_levels"
57 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
58 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
59 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
60 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
61 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
62 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
63 #define KEY_DICT_FILE "dict_file"
64 #define KEY_NICK "nick"
65 #define KEY_LANGUAGE "language"
66 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
67 #define KEY_AUTOGAG_DURATION "autogag_duration"
68 #define KEY_AUTH_POLICER "auth_policer"
69 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
70 #define KEY_EMAIL_ENABLED "email_enabled"
71 #define KEY_EMAIL_REQUIRED "email_required"
72 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
73 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
74 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
75 #define KEY_OUNREGISTER_INACTIVE "ounregister_inactive"
76 #define KEY_OUNREGISTER_FLAGS "ounregister_flags"
77 #define KEY_HANDLE_TS_MODE "account_timestamp_mode"
78 #define KEY_MAX_AUTHLOG_LEN "max_authlog_len"
81 #define KEY_PASSWD "passwd"
82 #define KEY_NICKS "nicks"
83 #define KEY_MASKS "masks"
84 #define KEY_OPSERV_LEVEL "opserv_level"
85 #define KEY_FLAGS "flags"
86 #define KEY_REGISTER_ON "register"
87 #define KEY_LAST_SEEN "lastseen"
88 #define KEY_INFO "info"
89 #define KEY_DEVNULL "devnull"
90 #define KEY_WEBSITE "website"
91 #define KEY_USERLIST_STYLE "user_style"
92 #define KEY_SCREEN_WIDTH "screen_width"
93 #define KEY_LAST_AUTHED_HOST "last_authed_host"
94 #define KEY_LAST_QUIT_HOST "last_quit_host"
95 #define KEY_EMAIL_ADDR "email_addr"
96 #define KEY_COOKIE "cookie"
97 #define KEY_COOKIE_DATA "data"
98 #define KEY_COOKIE_TYPE "type"
99 #define KEY_COOKIE_EXPIRES "expires"
100 #define KEY_ACTIVATION "activation"
101 #define KEY_PASSWORD_CHANGE "password change"
102 #define KEY_EMAIL_CHANGE "email change"
103 #define KEY_ALLOWAUTH "allowauth"
104 #define KEY_EPITHET "epithet"
105 #define KEY_TABLE_WIDTH "table_width"
106 #define KEY_MAXLOGINS "maxlogins"
107 #define KEY_FAKEHOST "fakehost"
108 #define KEY_FAKEIDENT "fakeident"
109 #define KEY_NOTES "notes"
110 #define KEY_NOTE_EXPIRES "expires"
111 #define KEY_NOTE_SET "set"
112 #define KEY_NOTE_SETTER "setter"
113 #define KEY_NOTE_NOTE "note"
114 #define KEY_KARMA "karma"
115 #define KEY_AUTHLOG "authlog"
116 #define KEY_AUTHLOG_LOGIN_TIME "login_time"
117 #define KEY_AUTHLOG_LOGOUT_TIME "logout_time"
118 #define KEY_AUTHLOG_HOSTMASK "hostmask"
119 #define KEY_AUTHLOG_QUIT_REASON "quit_reason"
121 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
123 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
124 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
125 typedef OPTION_FUNC(option_func_t);
127 DEFINE_LIST(handle_info_list, struct handle_info*)
129 #define NICKSERV_MIN_PARMS(N) do { \
131 reply("MSG_MISSING_PARAMS", argv[0]); \
132 svccmd_send_help(user, nickserv, cmd); \
136 struct userNode *nickserv;
137 struct userList curr_helpers;
138 const char *handle_flags = HANDLE_FLAGS;
140 static struct module *nickserv_module;
141 static struct service *nickserv_service;
142 static struct log_type *NS_LOG;
143 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
144 static dict_t nickserv_id_dict; /* contains struct handle_info* */
145 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
146 static dict_t nickserv_opt_dict; /* contains option_func_t* */
147 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
148 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
149 static char handle_inverse_flags[256];
150 static unsigned int flag_access_levels[32];
151 static const struct message_entry msgtab[] = {
152 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
153 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
154 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
155 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
156 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
157 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
158 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
159 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
160 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
161 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
162 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
163 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
164 { "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." },
165 { "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." },
166 { "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." },
167 { "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." },
168 { "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." },
169 { "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." },
170 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
171 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
172 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
173 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
174 { "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." },
175 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
176 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
177 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
178 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
179 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
180 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
181 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
182 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
183 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
184 { "NSMSG_ATE_COOKIE_FOREIGN", "I ate the cookie for account $b%s$b. It may now have another." },
185 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
186 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
187 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
188 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
189 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
190 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
191 { "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)" },
192 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
193 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
194 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
195 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
196 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
197 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
198 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
199 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
200 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
201 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
202 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
203 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
204 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
205 { "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." },
206 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
207 { "NSMSG_FAKEIDENT_INVALID", "Fake idents must be shorter than %d characters." },
208 { "NSMSG_FAKEMASK_INVALID", "Fake ident@hosts must be shorter than %d characters." },
209 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
210 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
211 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
212 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
213 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
214 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
215 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
216 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
217 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
218 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
219 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
220 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
221 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
222 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
223 { "NSMSG_HANDLEINFO_DEVNULL", " DevNull Class: %s" },
224 { "NSMSG_HANDLEINFO_WEBSITE", " Website: %s" },
225 { "NSMSG_HANDLEINFO_ACCESS", " Access: %i" },
226 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
227 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
228 { "NSMSG_HANDLEINFO_FAKEIDENT", " Fake ident: %s" },
229 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
230 { "NSMSG_HANDLEINFO_FAKEIDENTHOST", " Fake host: %s@%s" },
231 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
232 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
233 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
234 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
235 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
236 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
237 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
238 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
239 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
240 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
241 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
242 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
243 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
244 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
245 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
246 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
247 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
248 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
249 { "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)." },
250 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
251 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
252 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
253 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
254 { "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." },
255 { "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." },
256 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
257 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
258 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
259 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
260 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
261 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
262 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
263 { "NSMSG_PASS_SUCCESS", "Password changed." },
264 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
265 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
266 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
267 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
268 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
269 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
270 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
271 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
272 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
273 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
274 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
275 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
276 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
277 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
278 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
279 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
280 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
281 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
282 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
283 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
284 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
285 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
286 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
287 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
288 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
289 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
290 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
291 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
292 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
293 { "NSMSG_NO_ACCESS", "Access denied." },
294 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
295 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
296 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
297 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
298 { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
299 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
300 { "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." },
301 { "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." },
302 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
303 { "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." },
304 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
305 { "NSMSG_SEARCH_MATCH", "Match: %s" },
306 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
307 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
308 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
309 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
310 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
311 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
312 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
313 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
314 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
315 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
316 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
317 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
318 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
319 { "NSMSG_SET_DEVNULL", "$bDEVNULL: $b%s" },
320 { "NSMSG_SET_AUTOHIDE", "$bAUTOHIDE: $b%s" },
321 { "NSMSG_SET_WEBSITE", "$bWEBSITE: $b%s" },
322 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
323 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
324 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
325 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
326 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
327 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
328 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
329 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
330 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
331 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
332 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
333 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
334 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
335 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
336 { "NSMSG_SET_FAKEIDENT", "$bFAKEIDENT: $b%s" },
337 { "NSMSG_SET_FAKEIDENTHOST", "$bFAKEHOST: $b%s@%s" },
338 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
339 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
340 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
341 { "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." },
342 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
343 { "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." },
344 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
345 { "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." },
346 { "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." },
347 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
348 { "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." },
349 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
350 { "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." },
351 { "CHECKPASS_YES", "Yes." },
352 { "CHECKPASS_NO", "No." },
353 { "CHECKEMAIL_NOT_SET", "No email set." },
354 { "CHECKEMAIL_YES", "Yes." },
355 { "CHECKEMAIL_NO", "No." },
359 enum reclaim_action {
365 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
366 static void nickserv_reclaim_p(void *data);
367 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
369 enum handle_ts_mode {
375 unsigned int disable_nicks : 1;
376 unsigned int valid_handle_regex_set : 1;
377 unsigned int valid_nick_regex_set : 1;
378 unsigned int autogag_enabled : 1;
379 unsigned int email_enabled : 1;
380 unsigned int email_required : 1;
381 unsigned int default_hostmask : 1;
382 unsigned int warn_nick_owned : 1;
383 unsigned int warn_clone_auth : 1;
384 unsigned long nicks_per_handle;
385 unsigned long password_min_length;
386 unsigned long password_min_digits;
387 unsigned long password_min_upper;
388 unsigned long password_min_lower;
389 unsigned long db_backup_frequency;
390 unsigned long handle_expire_frequency;
391 unsigned long autogag_duration;
392 unsigned long email_visible_level;
393 unsigned long cookie_timeout;
394 unsigned long handle_expire_delay;
395 unsigned long nochan_handle_expire_delay;
396 unsigned long modoper_level;
397 unsigned long set_epithet_level;
398 unsigned long set_title_level;
399 unsigned long set_fakehost_level;
400 unsigned long set_fakeident_level;
401 unsigned long handles_per_email;
402 unsigned long email_search_level;
403 const char *network_name;
404 regex_t valid_handle_regex;
405 regex_t valid_nick_regex;
406 dict_t weak_password_dict;
407 struct policer_params *auth_policer_params;
408 enum reclaim_action reclaim_action;
409 enum reclaim_action auto_reclaim_action;
410 enum handle_ts_mode handle_ts_mode;
411 unsigned long auto_reclaim_delay;
412 unsigned char default_maxlogins;
413 unsigned char hard_maxlogins;
414 unsigned long ounregister_inactive;
415 unsigned long ounregister_flags;
416 unsigned int max_authlog_len;
419 struct pendingLOCUser {
420 struct handle_info *handle_info;
422 struct authlogEntry *authlog;
423 struct pendingLOCUser *next;
426 const char *titlehost_suffix = NULL;
427 static struct pendingLOCUser *pendingLOCUsers = NULL;
429 /* We have 2^32 unique account IDs to use. */
430 unsigned long int highest_id = 0;
432 #define WALK_NOTES(HANDLE, PREV, NOTE) \
433 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
434 if (NOTE->expires && NOTE->expires < now) { \
435 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
437 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
441 canonicalize_hostmask(char *mask)
443 char *out = mask, *temp;
444 if ((temp = strchr(mask, '!'))) {
446 while (*temp) *out++ = *temp++;
452 static struct handle_info *
453 register_handle(const char *handle, const char *passwd, unsigned long id)
455 struct handle_info *hi;
457 char id_base64[IDLEN + 1];
460 /* Assign a unique account ID to the account; note that 0 is
461 an invalid account ID. 1 is therefore the first account ID. */
463 id = 1 + highest_id++;
465 /* Note: highest_id is and must always be the highest ID. */
466 if (id > highest_id) {
470 inttobase64(id_base64, id, IDLEN);
472 /* Make sure an account with the same ID doesn't exist. If a
473 duplicate is found, log some details and assign a new one.
474 This should be impossible, but it never hurts to expect it. */
475 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
476 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
481 hi = calloc(1, sizeof(*hi));
482 hi->userlist_style = HI_DEFAULT_STYLE;
483 hi->handle = strdup(handle);
484 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
486 dict_insert(nickserv_handle_dict, hi->handle, hi);
491 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
497 register_nick(const char *nick, struct handle_info *owner)
499 struct nick_info *ni;
500 ni = malloc(sizeof(struct nick_info));
501 safestrncpy(ni->nick, nick, sizeof(ni->nick));
503 ni->next = owner->nicks;
505 dict_insert(nickserv_nick_dict, ni->nick, ni);
509 delete_nick(struct nick_info *ni)
511 struct nick_info *last, *next;
512 struct userNode *user;
513 /* Check to see if we should mark a user as unregistered. */
514 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
515 user->modes &= ~FLAGS_REGNICK;
518 /* Remove ni from the nick_info linked list. */
519 if (ni == ni->owner->nicks) {
520 ni->owner->nicks = ni->next;
522 last = ni->owner->nicks;
528 last->next = next->next;
530 dict_remove(nickserv_nick_dict, ni->nick);
533 static unreg_func_t *unreg_func_list;
534 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
537 reg_unreg_func(unreg_func_t func)
539 if (unreg_func_used == unreg_func_size) {
540 if (unreg_func_size) {
541 unreg_func_size <<= 1;
542 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
545 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
548 unreg_func_list[unreg_func_used++] = func;
552 nickserv_free_cookie(void *data)
554 struct handle_cookie *cookie = data;
555 if (cookie->hi) cookie->hi->cookie = NULL;
556 if (cookie->data) free(cookie->data);
561 free_handle_info(void *vhi)
563 struct handle_info *hi = vhi;
566 inttobase64(id, hi->id, IDLEN);
567 dict_remove(nickserv_id_dict, id);
569 free_string_list(hi->masks);
573 delete_nick(hi->nicks);
581 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
582 nickserv_free_cookie(hi->cookie);
585 struct handle_note *note = hi->notes;
586 hi->notes = note->next;
589 if (hi->email_addr) {
590 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
591 handle_info_list_remove(hil, hi);
593 dict_remove(nickserv_email_dict, hi->email_addr);
595 struct authlogEntry *authlog, *next;
596 for(authlog = hi->authlog; authlog; authlog = next) {
597 next = authlog->next;
598 struct pendingLOCUser *pending, *prev_pending = NULL;
599 for(pending = pendingLOCUsers; pending; pending = pending->next) {
600 if(pending->authlog == authlog) {
602 prev_pending->next = pending->next;
604 pendingLOCUsers = pending->next;
608 prev_pending = pending;
610 free((char *) authlog->hostmask);
611 if(authlog->quit_reason)
612 free((char *) authlog->quit_reason);
618 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
621 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
625 for (n=0; n<unreg_func_used; n++)
626 unreg_func_list[n](notify, hi);
628 set_user_handle_info(hi->users, NULL, 0);
630 if (nickserv_conf.disable_nicks)
631 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
633 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
635 dict_remove(nickserv_handle_dict, hi->handle);
639 get_handle_info(const char *handle)
641 return dict_find(nickserv_handle_dict, handle, 0);
645 get_nick_info(const char *nick)
647 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
651 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
656 for (nn=0; nn<channel->members.used; ++nn) {
657 mn = channel->members.list[nn];
658 if ((mn->user != except) && (mn->user->handle_info == handle))
665 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
666 if (!user->handle_info) {
668 send_message(user, bot, "MSG_AUTHENTICATE");
672 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
674 send_message(user, bot, "NSMSG_NO_ACCESS");
678 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
680 send_message(user, bot, "MSG_OPER_SUSPENDED");
684 if (user->handle_info->opserv_level < min_level) {
686 send_message(user, bot, "NSMSG_NO_ACCESS");
694 is_valid_handle(const char *handle)
696 struct userNode *user;
697 /* cant register a juped nick/service nick as handle, to prevent confusion */
698 user = GetUserH(handle);
699 if (user && IsLocal(user))
701 /* check against maximum length */
702 if (strlen(handle) > NICKSERV_HANDLE_LEN)
704 /* for consistency, only allow account names that could be nicks */
705 if (!is_valid_nick(handle))
707 /* disallow account names that look like bad words */
708 if (opserv_bad_channel(handle))
710 /* test either regex or containing all valid chars */
711 if (nickserv_conf.valid_handle_regex_set) {
712 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
715 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
716 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
720 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
725 is_registerable_nick(const char *nick)
727 /* make sure it could be used as an account name */
728 if (!is_valid_handle(nick))
731 if (strlen(nick) > NICKLEN)
733 /* test either regex or as valid handle */
734 if (nickserv_conf.valid_nick_regex_set) {
735 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
738 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
739 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
747 is_valid_email_addr(const char *org_email)
749 char email[strlen(org_email)+1];
750 strcpy(email, org_email);
751 //validate email address
752 //1st check: there need to be one @
753 char *p1 = strchr(email, '@');
754 if(!p1 || strchr(p1+1, '@')) return 0;
756 //2nd check: username (bevore @) must be at least 1 char long and out of part_chars
757 char *part_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._%+-";
759 if(p1 - email == 0) return 0;
760 for(i = 0; i < (p1 - email); i++) {
761 if(!strchr(part_chars, email[i])) return 0;
763 //3rd check: there need to be at least 1 dot in the domain part and all characters out of part_chars
764 part_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-";
773 } else if(!strchr(part_chars, *p1))
780 //4th check: TLD must be <= 5 chars, no special chars
795 visible_email_addr(struct userNode *user, struct handle_info *hi)
797 if (hi->email_addr) {
798 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
799 return hi->email_addr;
809 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
811 struct handle_info *hi;
812 struct userNode *target;
816 if (!(hi = get_handle_info(++name))) {
817 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
822 if (!(target = GetUserH(name))) {
823 send_message(user, service, "MSG_NICK_UNKNOWN", name);
826 if (IsLocal(target)) {
827 if (IsService(target))
828 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
830 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
833 if (!(hi = target->handle_info)) {
834 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
842 oper_outranks(struct userNode *user, struct handle_info *hi) {
843 if (user->handle_info->opserv_level > hi->opserv_level)
845 if (user->handle_info->opserv_level == hi->opserv_level) {
846 if ((user->handle_info->opserv_level == 1000)
847 || (user->handle_info == hi)
848 || ((user->handle_info->opserv_level == 0)
849 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
850 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
854 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
858 static struct handle_info *
859 get_victim_oper(struct userNode *user, const char *target)
861 struct handle_info *hi;
862 if (!(hi = smart_get_handle_info(nickserv, user, target)))
864 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
865 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
868 return oper_outranks(user, hi) ? hi : NULL;
872 valid_user_for(struct userNode *user, struct handle_info *hi)
876 /* If no hostmasks on the account, allow it. */
877 if (!hi->masks->used || IsDummy(user))
879 /* If any hostmask matches, allow it. */
880 for (ii=0; ii<hi->masks->used; ii++)
881 if (user_matches_glob(user, hi->masks->list[ii], 0))
883 /* If they are allowauthed to this account, allow it (removing the aa). */
884 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
885 dict_remove(nickserv_allow_auth_dict, user->nick);
888 /* The user is not allowed to use this account. */
893 is_secure_password(const char *handle, const char *pass, struct userNode *user)
896 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
900 if (len < nickserv_conf.password_min_length) {
902 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
905 if (!irccasecmp(pass, handle)) {
907 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
910 dict_find(nickserv_conf.weak_password_dict, pass, &p);
913 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
916 for (i=0; i<len; i++) {
917 if (isdigit(pass[i]))
919 if (isupper(pass[i]))
921 if (islower(pass[i]))
924 if ((cnt_lower < nickserv_conf.password_min_lower)
925 || (cnt_upper < nickserv_conf.password_min_upper)
926 || (cnt_digits < nickserv_conf.password_min_digits)) {
928 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
934 static auth_func_t *auth_func_list;
935 static unsigned int auth_func_size = 0, auth_func_used = 0;
938 reg_auth_func(auth_func_t func)
940 if (auth_func_used == auth_func_size) {
941 if (auth_func_size) {
942 auth_func_size <<= 1;
943 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
946 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
949 auth_func_list[auth_func_used++] = func;
952 static handle_rename_func_t *rf_list;
953 static unsigned int rf_list_size, rf_list_used;
956 reg_handle_rename_func(handle_rename_func_t func)
958 if (rf_list_used == rf_list_size) {
961 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
964 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
967 rf_list[rf_list_used++] = func;
971 generate_fakehost(struct handle_info *handle)
973 extern const char *hidden_host_suffix;
974 static char buffer[HOSTLEN+1];
976 if (!handle->fakehost) {
977 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
979 } else if (handle->fakehost[0] == '.') {
980 /* A leading dot indicates the stored value is actually a title. */
981 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, titlehost_suffix);
983 } else if (handle->fakehost[0] == '$') {
984 /* A leading $ indicates the stored value begins with the user handle. */
985 snprintf(buffer, sizeof(buffer), "%s%s", handle->handle, handle->fakehost+1);
988 return handle->fakehost;
992 generate_fakeident(struct handle_info *handle, struct userNode *user)
994 static char buffer[USERLEN+1];
996 if (!handle->fakeident) {
999 safestrncpy(buffer, user->ident, sizeof(buffer));
1002 return handle->fakeident;
1006 apply_fakehost(struct handle_info *handle, struct userNode *user)
1008 struct userNode *target;
1009 char *fakehost, *fakeident;
1014 fakehost = generate_fakehost(handle);
1017 fakeident = generate_fakeident(handle, user);
1018 assign_fakehost(user, fakehost, fakeident, 0, 1);
1022 for (target = handle->users; target; target = target->next_authed) {
1023 fakeident = generate_fakeident(handle, target);
1024 assign_fakehost(target, fakehost, fakeident, 0, 1);
1029 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
1032 struct handle_info *old_info;
1034 /* This can happen if somebody uses COOKIE while authed, or if
1035 * they re-auth to their current handle (which is silly, but users
1036 * are like that). */
1037 if (user->handle_info == hi)
1040 if (user->handle_info) {
1041 struct userNode *other;
1044 userList_remove(&curr_helpers, user);
1046 /* remove from next_authed linked list */
1047 if (user->handle_info->users == user) {
1048 user->handle_info->users = user->next_authed;
1049 } else if (user->handle_info->users != NULL) {
1050 for (other = user->handle_info->users;
1051 other->next_authed != user;
1052 other = other->next_authed) ;
1053 other->next_authed = user->next_authed;
1055 /* No users authed to the account - can happen if they get
1056 * killed for authing. */
1058 /* if nobody left on old handle, and they're not an oper, remove !god */
1059 if (!user->handle_info->users && !user->handle_info->opserv_level)
1060 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
1061 /* record them as being last seen at this time */
1062 user->handle_info->lastseen = now;
1063 /* and record their hostmask */
1064 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
1066 old_info = user->handle_info;
1067 user->handle_info = hi;
1068 if (hi && !hi->users && !hi->opserv_level)
1069 HANDLE_CLEAR_FLAG(hi, HELPING);
1070 for (n=0; n<auth_func_used; n++) {
1071 auth_func_list[n](user, old_info);
1076 struct nick_info *ni;
1078 HANDLE_CLEAR_FLAG(hi, FROZEN);
1079 if (nickserv_conf.warn_clone_auth) {
1080 struct userNode *other;
1081 for (other = hi->users; other; other = other->next_authed)
1082 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
1084 user->next_authed = hi->users;
1087 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
1088 userList_append(&curr_helpers, user);
1090 if (hi->fakehost || hi->fakeident || old_info)
1091 apply_fakehost(hi, user);
1094 if (!nickserv_conf.disable_nicks) {
1095 struct nick_info *ni2;
1096 for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
1097 if (!irccasecmp(user->nick, ni2->nick)) {
1098 user->modes |= FLAGS_REGNICK;
1103 StampUser(user, hi->handle, hi->registered, hi->id);
1106 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
1107 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
1109 /* We cannot clear the user's account ID, unfortunately. */
1110 user->next_authed = NULL;
1114 static struct handle_info*
1115 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
1117 struct handle_info *hi;
1118 struct nick_info *ni;
1119 char crypted[MD5_CRYPT_LENGTH];
1121 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
1122 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
1126 if (!is_secure_password(handle, passwd, user))
1129 cryptpass(passwd, crypted);
1130 hi = register_handle(handle, crypted, 0);
1131 hi->masks = alloc_string_list(1);
1133 hi->language = lang_C;
1134 hi->registered = now;
1136 hi->flags = HI_DEFAULT_FLAGS;
1137 if (settee && !no_auth)
1138 set_user_handle_info(settee, hi, 1);
1141 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1142 else if (nickserv_conf.disable_nicks)
1143 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1144 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1145 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1147 register_nick(user->nick, hi);
1148 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1150 if (settee && (user != settee))
1151 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1156 nickserv_bake_cookie(struct handle_cookie *cookie)
1158 cookie->hi->cookie = cookie;
1159 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1163 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1165 struct handle_cookie *cookie;
1166 char subject[128], body[4096], *misc;
1167 const char *netname, *fmt;
1171 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1175 cookie = calloc(1, sizeof(*cookie));
1177 cookie->type = type;
1178 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1179 cookie->expires = now + nickserv_conf.cookie_timeout;
1180 inttobase64(cookie->cookie, rand(), 5);
1181 inttobase64(cookie->cookie+5, rand(), 5);
1183 netname = nickserv_conf.network_name;
1186 switch (cookie->type) {
1188 hi->passwd[0] = 0; /* invalidate password */
1189 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1190 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1191 snprintf(subject, sizeof(subject), fmt, netname);
1192 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1193 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1196 case PASSWORD_CHANGE:
1197 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1198 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1199 snprintf(subject, sizeof(subject), fmt, netname);
1200 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1201 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1204 misc = hi->email_addr;
1205 hi->email_addr = cookie->data;
1207 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1208 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1209 snprintf(subject, sizeof(subject), fmt, netname);
1210 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1211 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1212 mail_send(nickserv, hi, subject, body, 1);
1213 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1214 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1216 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1217 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1218 snprintf(subject, sizeof(subject), fmt, netname);
1219 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1220 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1221 mail_send(nickserv, hi, subject, body, 1);
1224 hi->email_addr = misc;
1227 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1228 snprintf(subject, sizeof(subject), fmt, netname);
1229 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1230 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1231 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1234 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1238 mail_send(nickserv, hi, subject, body, first_time);
1239 nickserv_bake_cookie(cookie);
1243 nickserv_eat_cookie(struct handle_cookie *cookie)
1245 cookie->hi->cookie = NULL;
1246 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1247 nickserv_free_cookie(cookie);
1251 nickserv_free_email_addr(void *data)
1253 handle_info_list_clean(data);
1258 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1260 struct handle_info_list *hil;
1261 /* Remove from old handle_info_list ... */
1262 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1263 handle_info_list_remove(hil, hi);
1264 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1265 hi->email_addr = NULL;
1267 /* Add to the new list.. */
1268 if (new_email_addr) {
1269 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1270 hil = calloc(1, sizeof(*hil));
1271 hil->tag = strdup(new_email_addr);
1272 handle_info_list_init(hil);
1273 dict_insert(nickserv_email_dict, hil->tag, hil);
1275 handle_info_list_append(hil, hi);
1276 hi->email_addr = hil->tag;
1280 static NICKSERV_FUNC(cmd_register)
1283 struct handle_info *hi;
1284 const char *email_addr, *password;
1287 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1288 /* Require the first handle registered to belong to someone +o. */
1289 reply("NSMSG_REQUIRE_OPER");
1293 if (user->handle_info) {
1294 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1298 if (IsRegistering(user)) {
1299 reply("NSMSG_ALREADY_REGISTERING");
1303 if (IsStamped(user)) {
1304 /* Unauthenticated users might still have been stamped
1305 previously and could therefore have a hidden host;
1306 do not allow them to register a new account. */
1307 reply("NSMSG_STAMPED_REGISTER");
1311 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1313 if (!is_valid_handle(argv[1])) {
1314 reply("NSMSG_BAD_HANDLE", argv[1]);
1318 if ((argc >= 4) && nickserv_conf.email_enabled) {
1319 struct handle_info_list *hil;
1322 /* Remember email address. */
1323 email_addr = argv[3];
1325 /* Check that the email address looks valid.. */
1326 if (!is_valid_email_addr(email_addr)) {
1327 reply("NSMSG_BAD_EMAIL_ADDR");
1331 /* .. and that we are allowed to send to it. */
1332 if ((str = mail_prohibited_address(email_addr))) {
1333 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1337 /* If we do email verify, make sure we don't spam the address. */
1338 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1340 for (nn=0; nn<hil->used; nn++) {
1341 if (hil->list[nn]->cookie) {
1342 reply("NSMSG_EMAIL_UNACTIVATED");
1346 if (hil->used >= nickserv_conf.handles_per_email) {
1347 reply("NSMSG_EMAIL_OVERUSED");
1360 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1362 /* Add any masks they should get. */
1363 if (nickserv_conf.default_hostmask) {
1364 string_list_append(hi->masks, strdup("*@*"));
1366 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1367 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1368 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1371 /* If they're the first to register, give them level 1000. */
1372 if (dict_size(nickserv_handle_dict) == 1) {
1373 hi->opserv_level = 1000;
1374 reply("NSMSG_ROOT_HANDLE", argv[1]);
1377 /* Set their email address. */
1379 nickserv_set_email_addr(hi, email_addr);
1381 /* If they need to do email verification, tell them. */
1383 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1385 /* Set registering flag.. */
1386 user->modes |= FLAGS_REGISTERING;
1391 static NICKSERV_FUNC(cmd_oregister)
1394 struct userNode *settee;
1395 struct handle_info *hi;
1396 const char *pass, *email;
1398 NICKSERV_MIN_PARMS(3);
1403 if (!is_valid_handle(argv[1])) {
1404 reply("NSMSG_BAD_HANDLE", argv[1]);
1408 if (argc < 5 || !nickserv_conf.email_enabled) {
1413 if (!is_valid_email_addr(email)) {
1414 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
1417 if ((str = mail_prohibited_address(email))) {
1418 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", email, str);
1423 if (argc < 4 || !strcmp(argv[3], "*")) {
1426 } else if (strchr(argv[3], '@')) {
1427 mask = canonicalize_hostmask(strdup(argv[3]));
1429 settee = GetUserH(argv[4]);
1431 reply("MSG_NICK_UNKNOWN", argv[4]);
1438 } else if ((settee = GetUserH(argv[3]))) {
1439 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1441 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1444 if (settee && settee->handle_info) {
1445 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1449 if (!(hi = nickserv_register(user, settee, argv[1], pass, 0))) {
1454 string_list_append(hi->masks, mask);
1456 nickserv_set_email_addr(hi, email);
1460 static NICKSERV_FUNC(cmd_handleinfo)
1463 unsigned int i, pos=0, herelen;
1464 struct userNode *target, *next_un;
1465 struct handle_info *hi;
1466 const char *nsmsg_none;
1470 if (!(hi = user->handle_info)) {
1471 reply("NSMSG_MUST_AUTH");
1474 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1478 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1479 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1480 feh = hi->registered;
1481 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1484 intervalString(buff, now - hi->lastseen, user->handle_info);
1485 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1487 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1490 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1491 if ((oper_has_access(user, cmd->parent->bot, 200, 1)) || IsNetworkHelper(user))
1492 reply("NSMSG_HANDLEINFO_DEVNULL", (hi->devnull ? hi->devnull : nsmsg_none));
1493 if (user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1494 reply("NSMSG_HANDLEINFO_WEBSITE", (hi->website ? hi->website : nsmsg_none));
1495 if(hi->opserv_level > 0 && user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1496 reply("NSMSG_HANDLEINFO_ACCESS", hi->opserv_level);
1497 if (HANDLE_FLAGGED(hi, FROZEN))
1498 reply("NSMSG_HANDLEINFO_VACATION");
1500 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1501 struct do_not_register *dnr;
1502 if ((dnr = chanserv_is_dnr(NULL, hi)))
1503 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1504 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1506 } else if (hi != user->handle_info)
1510 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1512 if (nickserv_conf.email_enabled)
1513 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1517 switch (hi->cookie->type) {
1518 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1519 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1520 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1521 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1522 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1527 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1528 reply("NSMSG_HANDLEINFO_ID", hi->id);
1530 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1532 reply("NSMSG_HANDLEINFO_NO_NOTES");
1534 struct handle_note *prev, *note;
1536 WALK_NOTES(hi, prev, note) {
1537 char set_time[INTERVALLEN];
1538 intervalString(set_time, now - note->set, user->handle_info);
1539 if (note->expires) {
1540 char exp_time[INTERVALLEN];
1541 intervalString(exp_time, note->expires - now, user->handle_info);
1542 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1544 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1551 unsigned long flen = 1;
1552 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1554 for (i=0, flen=1; handle_flags[i]; i++)
1555 if (hi->flags & 1 << i)
1556 flags[flen++] = handle_flags[i];
1558 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1560 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1563 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1564 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1565 || (hi->opserv_level > 0)) {
1566 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1569 if (hi->fakeident && hi->fakehost)
1570 reply("NSMSG_HANDLEINFO_FAKEIDENTHOST", hi->fakeident, hi->fakehost);
1571 else if (hi->fakeident)
1572 reply("NSMSG_HANDLEINFO_FAKEIDENT", hi->fakeident);
1573 else if (hi->fakehost)
1574 reply("NSMSG_HANDLEINFO_FAKEHOST", hi->fakehost);
1576 if (hi->last_quit_host[0])
1577 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1579 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1581 if (nickserv_conf.disable_nicks) {
1582 /* nicks disabled; don't show anything about registered nicks */
1583 } else if (hi->nicks) {
1584 struct nick_info *ni, *next_ni;
1585 for (ni = hi->nicks; ni; ni = next_ni) {
1586 herelen = strlen(ni->nick);
1587 if (pos + herelen + 1 > ArrayLength(buff)) {
1589 goto print_nicks_buff;
1593 memcpy(buff+pos, ni->nick, herelen);
1594 pos += herelen; buff[pos++] = ' ';
1598 reply("NSMSG_HANDLEINFO_NICKS", buff);
1603 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1606 if (hi->masks->used) {
1607 for (i=0; i < hi->masks->used; i++) {
1608 herelen = strlen(hi->masks->list[i]);
1609 if (pos + herelen + 1 > ArrayLength(buff)) {
1611 goto print_mask_buff;
1613 memcpy(buff+pos, hi->masks->list[i], herelen);
1614 pos += herelen; buff[pos++] = ' ';
1615 if (i+1 == hi->masks->used) {
1618 reply("NSMSG_HANDLEINFO_MASKS", buff);
1623 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1627 struct userData *chan, *next;
1630 for (chan = hi->channels; chan; chan = next) {
1631 next = chan->u_next;
1632 name = chan->channel->channel->name;
1633 herelen = strlen(name);
1634 if (pos + herelen + 7 > ArrayLength(buff)) {
1636 goto print_chans_buff;
1638 if (IsUserSuspended(chan))
1640 pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
1644 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1649 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1652 for (target = hi->users; target; target = next_un) {
1653 herelen = strlen(target->nick);
1654 if (pos + herelen + 1 > ArrayLength(buff)) {
1656 goto print_cnick_buff;
1658 next_un = target->next_authed;
1660 memcpy(buff+pos, target->nick, herelen);
1661 pos += herelen; buff[pos++] = ' ';
1665 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1670 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1673 static NICKSERV_FUNC(cmd_userinfo)
1675 struct userNode *target;
1677 NICKSERV_MIN_PARMS(2);
1678 if (!(target = GetUserH(argv[1]))) {
1679 reply("MSG_NICK_UNKNOWN", argv[1]);
1682 if (target->handle_info)
1683 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1685 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1689 static NICKSERV_FUNC(cmd_nickinfo)
1691 struct nick_info *ni;
1693 NICKSERV_MIN_PARMS(2);
1694 if (!(ni = get_nick_info(argv[1]))) {
1695 reply("MSG_NICK_UNKNOWN", argv[1]);
1698 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1702 static NICKSERV_FUNC(cmd_notes)
1704 struct handle_info *hi;
1705 struct handle_note *prev, *note;
1708 NICKSERV_MIN_PARMS(2);
1709 if (!(hi = get_victim_oper(user, argv[1])))
1712 WALK_NOTES(hi, prev, note) {
1713 char set_time[INTERVALLEN];
1714 intervalString(set_time, now - note->set, user->handle_info);
1715 if (note->expires) {
1716 char exp_time[INTERVALLEN];
1717 intervalString(exp_time, note->expires - now, user->handle_info);
1718 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1720 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1724 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1728 static NICKSERV_FUNC(cmd_rename_handle)
1730 struct handle_info *hi;
1731 char msgbuf[MAXLEN], *old_handle;
1734 NICKSERV_MIN_PARMS(3);
1735 if (!(hi = get_victim_oper(user, argv[1])))
1737 if (!is_valid_handle(argv[2])) {
1738 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1741 if (get_handle_info(argv[2])) {
1742 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1745 if (hi->fakehost && hi->fakehost[0] == '.' &&
1746 (strlen(argv[2]) + strlen(hi->fakehost+1) +
1747 strlen(titlehost_suffix) + 2) > HOSTLEN) {
1748 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED_RENAME");
1752 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1753 hi->handle = strdup(argv[2]);
1754 dict_insert(nickserv_handle_dict, hi->handle, hi);
1755 for (nn=0; nn<rf_list_used; nn++)
1756 rf_list[nn](hi, old_handle);
1757 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1758 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1759 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1761 apply_fakehost(hi, NULL);
1765 static failpw_func_t *failpw_func_list;
1766 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1769 reg_failpw_func(failpw_func_t func)
1771 if (failpw_func_used == failpw_func_size) {
1772 if (failpw_func_size) {
1773 failpw_func_size <<= 1;
1774 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1776 failpw_func_size = 8;
1777 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1780 failpw_func_list[failpw_func_used++] = func;
1783 static struct authlogEntry *authlog_add(struct handle_info *hi, struct userNode *user, const char *mask) {
1784 if(!hi || (!user && !mask)) return NULL;
1786 mask = generate_hostmask(user, GENMASK_USENICK|GENMASK_STRICT_IDENT|GENMASK_NO_HIDING|GENMASK_STRICT_HOST);
1787 struct authlogEntry *authlog, *next, *prev = NULL;
1788 authlog = malloc(sizeof(*authlog));
1789 authlog->login_time = now;
1790 authlog->logout_time = 0;
1791 authlog->hostmask = mask;
1792 authlog->quit_reason = NULL;
1793 authlog->user = user;
1794 authlog->next = hi->authlog;
1795 hi->authlog = authlog;
1797 for(authlog = hi->authlog; authlog; authlog = next) {
1799 next = authlog->next;
1800 if(i > nickserv_conf.max_authlog_len) {
1801 struct pendingLOCUser *pending, *prev_pending = NULL;
1802 for(pending = pendingLOCUsers; pending; pending = pending->next) {
1803 if(pending->authlog == authlog) {
1805 prev_pending->next = pending->next;
1807 pendingLOCUsers = pending->next;
1811 prev_pending = pending;
1813 free((char *) authlog->hostmask);
1814 if(authlog->quit_reason)
1815 free((char *) authlog->quit_reason);
1817 prev->next = authlog->next;
1819 hi->authlog = authlog->next;
1827 static NICKSERV_FUNC(cmd_auth)
1829 int pw_arg, used, maxlogins;
1830 struct handle_info *hi;
1832 struct userNode *other;
1834 if (user->handle_info) {
1835 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1838 if (IsStamped(user)) {
1839 /* Unauthenticated users might still have been stamped
1840 previously and could therefore have a hidden host;
1841 do not allow them to authenticate. */
1842 reply("NSMSG_STAMPED_AUTH");
1846 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1848 } else if (argc == 2) {
1849 if (nickserv_conf.disable_nicks) {
1850 if (!(hi = get_handle_info(user->nick))) {
1851 reply("NSMSG_HANDLE_NOT_FOUND");
1855 /* try to look up their handle from their nick */
1856 struct nick_info *ni;
1857 ni = get_nick_info(user->nick);
1859 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1866 reply("MSG_MISSING_PARAMS", argv[0]);
1867 svccmd_send_help(user, nickserv, cmd);
1871 reply("NSMSG_HANDLE_NOT_FOUND");
1874 /* Responses from here on look up the language used by the handle they asked about. */
1875 passwd = argv[pw_arg];
1876 if (!valid_user_for(user, hi)) {
1877 if (hi->email_addr && nickserv_conf.email_enabled)
1878 send_message_type(4, user, cmd->parent->bot,
1879 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1882 send_message_type(4, user, cmd->parent->bot,
1883 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1885 argv[pw_arg] = "BADMASK";
1888 if (!checkpass(passwd, hi->passwd)) {
1890 send_message_type(4, user, cmd->parent->bot,
1891 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1892 argv[pw_arg] = "BADPASS";
1893 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1894 if (nickserv_conf.autogag_enabled) {
1895 if (!user->auth_policer.params) {
1896 user->auth_policer.last_req = now;
1897 user->auth_policer.params = nickserv_conf.auth_policer_params;
1899 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1901 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1902 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1903 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1905 argv[pw_arg] = "GAGGED";
1910 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1911 send_message_type(4, user, cmd->parent->bot,
1912 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1913 argv[pw_arg] = "SUSPENDED";
1916 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1917 for (used = 0, other = hi->users; other; other = other->next_authed) {
1918 if (++used >= maxlogins) {
1919 send_message_type(4, user, cmd->parent->bot,
1920 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1922 argv[pw_arg] = "MAXLOGINS";
1926 if (HANDLE_FLAGGED(hi, AUTOHIDE)) {
1927 //ok we have a fakehost set... but we need to set mode +x
1928 irc_svsmode(nickserv,user,"+x");
1931 set_user_handle_info(user, hi, 1);
1932 if (nickserv_conf.email_required && !hi->email_addr)
1933 reply("NSMSG_PLEASE_SET_EMAIL");
1934 if (!is_secure_password(hi->handle, passwd, NULL))
1935 reply("NSMSG_WEAK_PASSWORD");
1936 if (hi->passwd[0] != '$')
1937 cryptpass(passwd, hi->passwd);
1938 if (!hi->masks->used) {
1940 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1941 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1942 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1944 authlog_add(hi, user, NULL);
1945 argv[pw_arg] = "****";
1946 reply("NSMSG_AUTH_SUCCESS");
1950 struct handle_info *checklogin(const char *user, const char *pass, const char *numeric, const char *hostmask, const char *ipmask)
1952 struct handle_info *hi;
1953 unsigned int match = 0, ii = 0;
1954 hi = dict_find(nickserv_handle_dict, user, NULL);
1957 /* If no hostmasks on the account, allow it. */
1958 if (hi->masks->used) {
1959 /* If any hostmask matches, allow it. */
1960 for (ii=0; ii<hi->masks->used; ii++)
1961 if (match_ircglob(hostmask, hi->masks->list[ii]) || match_ircglob(ipmask, hi->masks->list[ii])) {
1968 if(!checkpass(pass, hi->passwd))
1970 if (HANDLE_FLAGGED(hi, SUSPENDED))
1972 char *ptr = malloc(strlen(hostmask)+1);
1973 strcpy(ptr, hostmask);
1974 struct authlogEntry *authlog = authlog_add(hi, NULL, ptr);
1975 struct pendingLOCUser *pending;
1976 if(authlog && (pending = malloc(sizeof(*pending)))) {
1977 pending->handle_info = hi;
1978 pending->time = now;
1979 pending->authlog = authlog;
1980 pending->next = pendingLOCUsers;
1981 pendingLOCUsers = pending;
1986 char *getfakehost(const char *user)
1988 struct handle_info *hi;
1989 hi = dict_find(nickserv_handle_dict, user, NULL);
1992 return generate_fakehost(hi);
1995 static allowauth_func_t *allowauth_func_list;
1996 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1999 reg_allowauth_func(allowauth_func_t func)
2001 if (allowauth_func_used == allowauth_func_size) {
2002 if (allowauth_func_size) {
2003 allowauth_func_size <<= 1;
2004 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
2006 allowauth_func_size = 8;
2007 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
2010 allowauth_func_list[allowauth_func_used++] = func;
2013 static int cmd_authlog_func(struct userNode *user, struct svccmd *cmd, struct handle_info *hi);
2015 static MODCMD_FUNC(cmd_authlog)
2017 return cmd_authlog_func(user, cmd, user->handle_info);
2020 static MODCMD_FUNC(cmd_oauthlog) {
2021 struct handle_info *hi;
2023 NICKSERV_MIN_PARMS(1);
2025 if (!(hi = get_victim_oper(user, argv[1])))
2028 return cmd_authlog_func(user, cmd, hi);
2031 static int cmd_authlog_func(struct userNode *user, struct svccmd *cmd, struct handle_info *hi) {
2032 struct helpfile_table tbl;
2033 struct authlogEntry *authlog;
2036 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
2043 tbl.flags = TABLE_NO_FREE;
2044 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
2045 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
2046 tbl.contents[0][0] = "Hostmask";
2047 tbl.contents[0][1] = "Login";
2048 tbl.contents[0][2] = "Logout";
2049 tbl.contents[0][3] = "Quit Reason";
2052 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
2054 free(tbl.contents[0]);
2060 char intervalBuf[INTERVALLEN];
2062 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
2063 tbl.contents[++i] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
2064 tbl.contents[i][0] = authlog->hostmask;
2065 str = intervalString(intervalBuf, now - authlog->login_time, hi);
2066 ptr = malloc(strlen(str)+1);
2068 tbl.contents[i][1] = ptr;
2069 if(authlog->logout_time)
2070 str = intervalString(intervalBuf, now - authlog->logout_time, hi);
2071 else if(!authlog->user)
2074 sprintf(intervalBuf, "Never (%s)", authlog->user->nick);
2077 ptr = malloc(strlen(str)+1);
2079 tbl.contents[i][2] = ptr;
2080 tbl.contents[i][3] = (authlog->quit_reason ? authlog->quit_reason : "-");
2083 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
2084 for(i = 1; i < tbl.length; ++i)
2086 free((char *) tbl.contents[i][1]);
2087 free((char *) tbl.contents[i][2]);
2088 free(tbl.contents[i]);
2090 free(tbl.contents[0]);
2096 static NICKSERV_FUNC(cmd_allowauth)
2098 struct userNode *target;
2099 struct handle_info *hi;
2102 NICKSERV_MIN_PARMS(2);
2103 if (!(target = GetUserH(argv[1]))) {
2104 reply("MSG_NICK_UNKNOWN", argv[1]);
2107 if (target->handle_info) {
2108 reply("NSMSG_USER_PREV_AUTH", target->nick);
2111 if (IsStamped(target)) {
2112 /* Unauthenticated users might still have been stamped
2113 previously and could therefore have a hidden host;
2114 do not allow them to authenticate to an account. */
2115 reply("NSMSG_USER_PREV_STAMP", target->nick);
2120 else if (!(hi = get_handle_info(argv[2]))) {
2121 reply("MSG_HANDLE_UNKNOWN", argv[2]);
2125 if (hi->opserv_level > user->handle_info->opserv_level) {
2126 reply("MSG_USER_OUTRANKED", hi->handle);
2129 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
2130 || (hi->opserv_level > 0))
2131 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
2132 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
2135 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
2136 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
2137 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
2138 if (nickserv_conf.email_enabled)
2139 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
2141 if (dict_remove(nickserv_allow_auth_dict, target->nick))
2142 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
2144 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
2146 for (n=0; n<allowauth_func_used; n++)
2147 allowauth_func_list[n](user, target, hi);
2151 static NICKSERV_FUNC(cmd_authcookie)
2153 struct handle_info *hi;
2155 NICKSERV_MIN_PARMS(2);
2156 if (user->handle_info) {
2157 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2160 if (IsStamped(user)) {
2161 /* Unauthenticated users might still have been stamped
2162 previously and could therefore have a hidden host;
2163 do not allow them to authenticate to an account. */
2164 reply("NSMSG_STAMPED_AUTHCOOKIE");
2167 if (!(hi = get_handle_info(argv[1]))) {
2168 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2171 if (!hi->email_addr) {
2172 reply("MSG_SET_EMAIL_ADDR");
2175 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
2179 static NICKSERV_FUNC(cmd_delcookie)
2181 struct handle_info *hi;
2183 hi = user->handle_info;
2185 reply("NSMSG_NO_COOKIE");
2188 switch (hi->cookie->type) {
2191 reply("NSMSG_MUST_TIME_OUT");
2194 nickserv_eat_cookie(hi->cookie);
2195 reply("NSMSG_ATE_COOKIE");
2201 static NICKSERV_FUNC(cmd_odelcookie)
2203 struct handle_info *hi;
2205 NICKSERV_MIN_PARMS(2);
2207 if (!(hi = get_victim_oper(user, argv[1])))
2211 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
2215 nickserv_eat_cookie(hi->cookie);
2216 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
2221 static NICKSERV_FUNC(cmd_resetpass)
2223 struct handle_info *hi;
2224 char crypted[MD5_CRYPT_LENGTH];
2226 NICKSERV_MIN_PARMS(3);
2227 if (user->handle_info) {
2228 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2231 if (IsStamped(user)) {
2232 /* Unauthenticated users might still have been stamped
2233 previously and could therefore have a hidden host;
2234 do not allow them to activate an account. */
2235 reply("NSMSG_STAMPED_RESETPASS");
2238 if (!(hi = get_handle_info(argv[1]))) {
2239 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2242 if (!hi->email_addr) {
2243 reply("MSG_SET_EMAIL_ADDR");
2246 cryptpass(argv[2], crypted);
2248 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
2252 static NICKSERV_FUNC(cmd_cookie)
2254 struct handle_info *hi;
2257 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
2260 NICKSERV_MIN_PARMS(3);
2261 if (!(hi = get_handle_info(argv[1]))) {
2262 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2268 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2269 reply("NSMSG_HANDLE_SUSPENDED");
2274 reply("NSMSG_NO_COOKIE");
2278 /* Check validity of operation before comparing cookie to
2279 * prohibit guessing by authed users. */
2280 if (user->handle_info
2281 && (hi->cookie->type != EMAIL_CHANGE)
2282 && (hi->cookie->type != PASSWORD_CHANGE)) {
2283 reply("NSMSG_CANNOT_COOKIE");
2287 if (strcmp(cookie, hi->cookie->cookie)) {
2288 reply("NSMSG_BAD_COOKIE");
2292 switch (hi->cookie->type) {
2294 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2295 set_user_handle_info(user, hi, 1);
2296 reply("NSMSG_HANDLE_ACTIVATED");
2298 case PASSWORD_CHANGE:
2299 set_user_handle_info(user, hi, 1);
2300 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2301 reply("NSMSG_PASSWORD_CHANGED");
2304 nickserv_set_email_addr(hi, hi->cookie->data);
2305 reply("NSMSG_EMAIL_CHANGED");
2308 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2309 set_user_handle_info(user, hi, 1);
2310 nickserv_addmask(user, hi, mask);
2311 reply("NSMSG_AUTH_SUCCESS");
2316 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2317 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2321 nickserv_eat_cookie(hi->cookie);
2326 static NICKSERV_FUNC(cmd_oregnick) {
2328 struct handle_info *target;
2329 struct nick_info *ni;
2331 NICKSERV_MIN_PARMS(3);
2332 if (!(target = modcmd_get_handle_info(user, argv[1])))
2335 if (!is_registerable_nick(nick)) {
2336 reply("NSMSG_BAD_NICK", nick);
2339 ni = dict_find(nickserv_nick_dict, nick, NULL);
2341 reply("NSMSG_NICK_EXISTS", nick);
2344 register_nick(nick, target);
2345 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2349 static NICKSERV_FUNC(cmd_regnick) {
2351 struct nick_info *ni;
2353 if (!is_registerable_nick(user->nick)) {
2354 reply("NSMSG_BAD_NICK", user->nick);
2357 /* count their nicks, see if it's too many */
2358 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2359 if (n >= nickserv_conf.nicks_per_handle) {
2360 reply("NSMSG_TOO_MANY_NICKS");
2363 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2365 reply("NSMSG_NICK_EXISTS", user->nick);
2368 register_nick(user->nick, user->handle_info);
2369 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2373 static NICKSERV_FUNC(cmd_pass)
2375 struct handle_info *hi;
2376 const char *old_pass, *new_pass;
2378 NICKSERV_MIN_PARMS(3);
2379 hi = user->handle_info;
2383 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2384 if (!checkpass(old_pass, hi->passwd)) {
2385 argv[1] = "BADPASS";
2386 reply("NSMSG_PASSWORD_INVALID");
2389 cryptpass(new_pass, hi->passwd);
2391 reply("NSMSG_PASS_SUCCESS");
2396 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2399 char *new_mask = canonicalize_hostmask(strdup(mask));
2400 for (i=0; i<hi->masks->used; i++) {
2401 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2402 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2407 string_list_append(hi->masks, new_mask);
2408 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2412 static NICKSERV_FUNC(cmd_addmask)
2415 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2416 int res = nickserv_addmask(user, user->handle_info, mask);
2420 if (!is_gline(argv[1])) {
2421 reply("NSMSG_MASK_INVALID", argv[1]);
2424 return nickserv_addmask(user, user->handle_info, argv[1]);
2428 static NICKSERV_FUNC(cmd_oaddmask)
2430 struct handle_info *hi;
2432 NICKSERV_MIN_PARMS(3);
2433 if (!(hi = get_victim_oper(user, argv[1])))
2435 return nickserv_addmask(user, hi, argv[2]);
2439 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2442 for (i=0; i<hi->masks->used; i++) {
2443 if (!strcmp(del_mask, hi->masks->list[i])) {
2444 char *old_mask = hi->masks->list[i];
2445 if (hi->masks->used == 1 && !force) {
2446 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2449 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2450 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2455 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2459 static NICKSERV_FUNC(cmd_delmask)
2461 NICKSERV_MIN_PARMS(2);
2462 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2465 static NICKSERV_FUNC(cmd_odelmask)
2467 struct handle_info *hi;
2468 NICKSERV_MIN_PARMS(3);
2469 if (!(hi = get_victim_oper(user, argv[1])))
2471 return nickserv_delmask(user, hi, argv[2], 1);
2475 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2476 unsigned int nn, add = 1, pos;
2477 unsigned long added, removed, flag;
2479 for (added=removed=nn=0; str[nn]; nn++) {
2481 case '+': add = 1; break;
2482 case '-': add = 0; break;
2484 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2485 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2488 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2489 /* cheesy avoidance of looking up the flag name.. */
2490 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2493 flag = 1 << (pos - 1);
2495 added |= flag, removed &= ~flag;
2497 removed |= flag, added &= ~flag;
2502 *premoved = removed;
2507 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2509 unsigned long before, after, added, removed;
2510 struct userNode *uNode;
2512 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2513 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2515 hi->flags = (hi->flags | added) & ~removed;
2516 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2518 /* Strip helping flag if they're only a support helper and not
2519 * currently in #support. */
2520 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2521 struct channelList *schannels;
2523 schannels = chanserv_support_channels();
2524 for (ii = 0; ii < schannels->used; ++ii)
2525 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2527 if (ii == schannels->used)
2528 HANDLE_CLEAR_FLAG(hi, HELPING);
2531 if (after && !before) {
2532 /* Add user to current helper list. */
2533 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2534 userList_append(&curr_helpers, uNode);
2535 } else if (!after && before) {
2536 /* Remove user from current helper list. */
2537 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2538 userList_remove(&curr_helpers, uNode);
2545 set_list(struct userNode *user, struct handle_info *hi, int override)
2549 char *set_display[] = {
2550 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2551 "EMAIL", "MAXLOGINS", "LANGUAGE", "DEVNULL"
2554 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2556 /* Do this so options are presented in a consistent order. */
2557 for (i = 0; i < ArrayLength(set_display); ++i)
2558 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2559 opt(user, hi, override, 0, NULL);
2562 static NICKSERV_FUNC(cmd_set)
2564 struct handle_info *hi;
2567 hi = user->handle_info;
2569 set_list(user, hi, 0);
2572 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2573 reply("NSMSG_INVALID_OPTION", argv[1]);
2576 return opt(user, hi, 0, argc-1, argv+1);
2579 static NICKSERV_FUNC(cmd_oset)
2581 struct handle_info *hi;
2582 struct svccmd *subcmd;
2584 char cmdname[MAXLEN];
2586 NICKSERV_MIN_PARMS(2);
2588 if (!(hi = get_victim_oper(user, argv[1])))
2592 set_list(user, hi, 0);
2596 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2597 reply("NSMSG_INVALID_OPTION", argv[2]);
2601 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2602 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2603 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2606 return opt(user, hi, 1, argc-2, argv+2);
2609 static OPTION_FUNC(opt_info)
2613 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2615 hi->infoline = NULL;
2617 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2621 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2622 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2626 static OPTION_FUNC(opt_devnull)
2628 const char *devnull;
2632 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2635 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2639 devnull = unsplit_string(argv+1, argc-1, NULL);
2640 if(devnull_check(devnull) == 1) {
2643 hi->devnull = strdup(devnull);
2648 devnull = hi->devnull ? hi->devnull : user_find_message(user, "MSG_NONE");
2649 send_message(user, nickserv, "NSMSG_SET_DEVNULL", devnull);
2653 void nickserv_devnull_delete(char *name) {
2655 struct handle_info *hi;
2657 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2659 if (hi->devnull && !irccasecmp(name, hi->devnull)) {
2666 void nickserv_devnull_rename(char *oldname, char *newname) {
2668 struct handle_info *hi;
2670 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2672 if (hi->devnull && !irccasecmp(oldname, hi->devnull)) {
2673 hi->devnull = strdup(newname);
2678 static OPTION_FUNC(opt_website)
2680 const char *website;
2683 if (!HANDLE_FLAGGED(user->handle_info, BOT)) {
2684 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2687 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2691 website = unsplit_string(argv+1, argc-1, NULL);
2692 hi->website = strdup(website);
2695 if (HANDLE_FLAGGED(user->handle_info, BOT)) {
2696 website = hi->website ? hi->website : user_find_message(user, "MSG_NONE");
2697 send_message(user, nickserv, "NSMSG_SET_WEBSITE", website);
2702 static OPTION_FUNC(opt_width)
2705 hi->screen_width = strtoul(argv[1], NULL, 0);
2707 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2708 hi->screen_width = MIN_LINE_SIZE;
2709 else if (hi->screen_width > MAX_LINE_SIZE)
2710 hi->screen_width = MAX_LINE_SIZE;
2712 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2716 static OPTION_FUNC(opt_tablewidth)
2719 hi->table_width = strtoul(argv[1], NULL, 0);
2721 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2722 hi->table_width = MIN_LINE_SIZE;
2723 else if (hi->screen_width > MAX_LINE_SIZE)
2724 hi->table_width = MAX_LINE_SIZE;
2726 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2730 static OPTION_FUNC(opt_color)
2733 if (enabled_string(argv[1]))
2734 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2735 else if (disabled_string(argv[1]))
2736 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2738 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2743 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2747 static OPTION_FUNC(opt_privmsg)
2750 if (enabled_string(argv[1]))
2751 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2752 else if (disabled_string(argv[1]))
2753 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2755 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2760 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2764 static OPTION_FUNC(opt_autohide)
2767 if (enabled_string(argv[1]))
2768 HANDLE_SET_FLAG(hi, AUTOHIDE);
2769 else if (disabled_string(argv[1]))
2770 HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
2772 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2777 send_message(user, nickserv, "NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
2781 static OPTION_FUNC(opt_style)
2786 if (!irccasecmp(argv[1], "Zoot"))
2787 hi->userlist_style = HI_STYLE_ZOOT;
2788 else if (!irccasecmp(argv[1], "def"))
2789 hi->userlist_style = HI_STYLE_DEF;
2792 switch (hi->userlist_style) {
2801 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2805 static OPTION_FUNC(opt_password)
2808 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2813 cryptpass(argv[1], hi->passwd);
2815 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2821 static OPTION_FUNC(opt_flags)
2824 unsigned int ii, flen;
2827 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2832 nickserv_apply_flags(user, hi, argv[1]);
2834 for (ii = flen = 0; handle_flags[ii]; ii++)
2835 if (hi->flags & (1 << ii))
2836 flags[flen++] = handle_flags[ii];
2839 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2841 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2845 static OPTION_FUNC(opt_email)
2849 if (!is_valid_email_addr(argv[1])) {
2850 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2853 if ((str = mail_prohibited_address(argv[1]))) {
2854 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2857 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2858 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2860 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2862 nickserv_set_email_addr(hi, argv[1]);
2864 nickserv_eat_cookie(hi->cookie);
2865 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2868 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2872 static OPTION_FUNC(opt_maxlogins)
2874 unsigned char maxlogins;
2876 maxlogins = strtoul(argv[1], NULL, 0);
2877 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2878 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2881 hi->maxlogins = maxlogins;
2883 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2884 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2888 static OPTION_FUNC(opt_language)
2890 struct language *lang;
2892 lang = language_find(argv[1]);
2893 if (irccasecmp(lang->name, argv[1]))
2894 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2895 hi->language = lang;
2897 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2901 static OPTION_FUNC(opt_karma)
2904 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2909 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2910 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2911 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2912 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2914 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2918 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2923 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2924 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2926 if ((user->handle_info->opserv_level < target->opserv_level)
2927 || ((user->handle_info->opserv_level == target->opserv_level)
2928 && (user->handle_info->opserv_level < 1000))) {
2929 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2932 if ((user->handle_info->opserv_level < new_level)
2933 || ((user->handle_info->opserv_level == new_level)
2934 && (user->handle_info->opserv_level < 1000))) {
2935 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2938 if (user->handle_info == target) {
2939 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2942 if (target->opserv_level == new_level)
2944 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2945 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2946 target->opserv_level = new_level;
2950 static OPTION_FUNC(opt_level)
2955 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2959 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2960 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2964 static OPTION_FUNC(opt_epithet)
2967 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2971 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2972 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2975 if ((epithet[0] == '*') && !epithet[1])
2978 hi->epithet = strdup(epithet);
2982 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2984 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2988 static OPTION_FUNC(opt_title)
2993 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2997 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2999 if (strchr(title, '.')) {
3000 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
3003 if ((strlen(user->handle_info->handle) + strlen(title) +
3004 strlen(titlehost_suffix) + 2) > HOSTLEN) {
3005 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
3010 if (!strcmp(title, "*")) {
3011 hi->fakehost = NULL;
3013 hi->fakehost = malloc(strlen(title)+2);
3014 hi->fakehost[0] = '.';
3015 strcpy(hi->fakehost+1, title);
3017 apply_fakehost(hi, NULL);
3018 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
3019 title = hi->fakehost + 1;
3023 title = user_find_message(user, "MSG_NONE");
3024 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
3028 static OPTION_FUNC(opt_fakehost)
3030 char mask[USERLEN + HOSTLEN + 2];
3034 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3038 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
3039 if(strlen(argv[1]) >= sizeof(mask)) {
3040 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
3044 safestrncpy(mask, argv[1], sizeof(mask));
3046 if ((host = strrchr(mask, '@')) && host != mask) {
3047 /* If ident@host was used and the user doesn't have access to set idents, do not change anything. */
3048 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3060 if (ident && strlen(ident) > USERLEN) {
3061 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3065 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
3066 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
3070 if (host && host[0]) {
3072 if (!strcmp(host, "*"))
3073 hi->fakehost = NULL;
3075 hi->fakehost = strdup(host);
3076 host = hi->fakehost;
3079 host = generate_fakehost(hi);
3082 free(hi->fakeident);
3083 if (!strcmp(ident, "*"))
3084 hi->fakeident = NULL;
3086 hi->fakeident = strdup(ident);
3087 ident = hi->fakeident;
3090 ident = generate_fakeident(hi, NULL);
3092 apply_fakehost(hi, NULL);
3094 host = generate_fakehost(hi);
3095 ident = generate_fakeident(hi, NULL);
3098 host = (char *) user_find_message(user, "MSG_NONE");
3100 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
3102 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
3106 static OPTION_FUNC(opt_fakeident)
3111 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3115 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3117 if (strlen(ident) > USERLEN) {
3118 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3121 free(hi->fakeident);
3122 if (!strcmp(ident, "*"))
3123 hi->fakeident = NULL;
3125 hi->fakeident = strdup(ident);
3126 ident = hi->fakeident;
3127 apply_fakehost(hi, NULL);
3129 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
3131 ident = user_find_message(user, "MSG_NONE");
3132 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
3136 static NICKSERV_FUNC(cmd_reclaim)
3138 struct nick_info *ni;
3139 struct userNode *victim;
3141 NICKSERV_MIN_PARMS(2);
3142 ni = dict_find(nickserv_nick_dict, argv[1], 0);
3144 reply("NSMSG_UNKNOWN_NICK", argv[1]);
3147 if (ni->owner != user->handle_info) {
3148 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
3151 victim = GetUserH(ni->nick);
3153 reply("MSG_NICK_UNKNOWN", ni->nick);
3156 if (victim == user) {
3157 reply("NSMSG_NICK_USER_YOU");
3160 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
3161 switch (nickserv_conf.reclaim_action) {
3162 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
3163 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
3164 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
3165 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
3170 static NICKSERV_FUNC(cmd_unregnick)
3173 struct handle_info *hi;
3174 struct nick_info *ni;
3176 hi = user->handle_info;
3177 nick = (argc < 2) ? user->nick : (const char*)argv[1];
3178 ni = dict_find(nickserv_nick_dict, nick, NULL);
3180 reply("NSMSG_UNKNOWN_NICK", nick);
3183 if (hi != ni->owner) {
3184 reply("NSMSG_NOT_YOUR_NICK", nick);
3187 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3192 static NICKSERV_FUNC(cmd_ounregnick)
3194 struct nick_info *ni;
3196 NICKSERV_MIN_PARMS(2);
3197 if (!(ni = get_nick_info(argv[1]))) {
3198 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
3201 if (!oper_outranks(user, ni->owner))
3203 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3208 static NICKSERV_FUNC(cmd_unregister)
3210 struct handle_info *hi;
3213 NICKSERV_MIN_PARMS(2);
3214 hi = user->handle_info;
3217 if (checkpass(passwd, hi->passwd)) {
3218 nickserv_unregister_handle(hi, user);
3221 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3222 reply("NSMSG_PASSWORD_INVALID");
3227 static NICKSERV_FUNC(cmd_ounregister)
3229 struct handle_info *hi;
3230 char reason[MAXLEN];
3233 NICKSERV_MIN_PARMS(2);
3234 if (!(hi = get_victim_oper(user, argv[1])))
3237 if (HANDLE_FLAGGED(hi, NODELETE)) {
3238 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
3242 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
3244 ((hi->flags & nickserv_conf.ounregister_flags)
3246 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
3247 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
3251 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
3252 global_message(MESSAGE_RECIPIENT_STAFF, reason);
3253 nickserv_unregister_handle(hi, user);
3257 static NICKSERV_FUNC(cmd_status)
3259 if (nickserv_conf.disable_nicks) {
3260 reply("NSMSG_GLOBAL_STATS_NONICK",
3261 dict_size(nickserv_handle_dict));
3263 if (user->handle_info) {
3265 struct nick_info *ni;
3266 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
3267 reply("NSMSG_HANDLE_STATS", cnt);
3269 reply("NSMSG_HANDLE_NONE");
3271 reply("NSMSG_GLOBAL_STATS",
3272 dict_size(nickserv_handle_dict),
3273 dict_size(nickserv_nick_dict));
3278 static NICKSERV_FUNC(cmd_ghost)
3280 struct userNode *target;
3281 char reason[MAXLEN];
3283 NICKSERV_MIN_PARMS(2);
3284 if (!(target = GetUserH(argv[1]))) {
3285 reply("MSG_NICK_UNKNOWN", argv[1]);
3288 if (target == user) {
3289 reply("NSMSG_CANNOT_GHOST_SELF");
3292 if (!target->handle_info || (target->handle_info != user->handle_info)) {
3293 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
3296 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
3297 DelUser(target, nickserv, 1, reason);
3298 reply("NSMSG_GHOST_KILLED", argv[1]);
3302 static NICKSERV_FUNC(cmd_vacation)
3304 HANDLE_SET_FLAG(user->handle_info, FROZEN);
3305 reply("NSMSG_ON_VACATION");
3309 static NICKSERV_FUNC(cmd_addnote)
3311 struct handle_info *hi;
3312 unsigned long duration;
3315 struct handle_note *prev;
3316 struct handle_note *note;
3318 /* Parse parameters and figure out values for note's fields. */
3319 NICKSERV_MIN_PARMS(4);
3320 hi = get_victim_oper(user, argv[1]);
3323 if(!strcmp(argv[2], "0"))
3325 else if(!(duration = ParseInterval(argv[2])))
3327 reply("MSG_INVALID_DURATION", argv[2]);
3330 if (duration > 2*365*86400) {
3331 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
3334 unsplit_string(argv + 3, argc - 3, text);
3335 WALK_NOTES(hi, prev, note) {}
3336 id = prev ? (prev->id + 1) : 1;
3338 /* Create the new note structure. */
3339 note = calloc(1, sizeof(*note) + strlen(text));
3341 note->expires = duration ? (now + duration) : 0;
3344 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
3345 strcpy(note->note, text);
3350 reply("NSMSG_NOTE_ADDED", id, hi->handle);
3354 static NICKSERV_FUNC(cmd_delnote)
3356 struct handle_info *hi;
3357 struct handle_note *prev;
3358 struct handle_note *note;
3361 NICKSERV_MIN_PARMS(3);
3362 hi = get_victim_oper(user, argv[1]);
3365 id = strtoul(argv[2], NULL, 10);
3366 WALK_NOTES(hi, prev, note) {
3367 if (id == note->id) {
3369 prev->next = note->next;
3371 hi->notes = note->next;
3373 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3377 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3382 nickserv_saxdb_write(struct saxdb_context *ctx) {
3384 struct handle_info *hi;
3387 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3389 assert(hi->id != 0);
3390 saxdb_start_record(ctx, iter_key(it), 0);
3392 struct handle_cookie *cookie = hi->cookie;
3395 switch (cookie->type) {
3396 case ACTIVATION: type = KEY_ACTIVATION; break;
3397 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3398 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3399 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3400 default: type = NULL; break;
3403 saxdb_start_record(ctx, KEY_COOKIE, 0);
3404 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3405 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3407 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3408 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3409 saxdb_end_record(ctx);
3413 struct handle_note *prev, *note;
3414 saxdb_start_record(ctx, KEY_NOTES, 0);
3415 WALK_NOTES(hi, prev, note) {
3416 snprintf(flags, sizeof(flags), "%d", note->id);
3417 saxdb_start_record(ctx, flags, 0);
3419 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3420 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3421 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3422 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3423 saxdb_end_record(ctx);
3425 saxdb_end_record(ctx);
3428 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3430 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3432 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3434 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3438 for (ii=flen=0; handle_flags[ii]; ++ii)
3439 if (hi->flags & (1 << ii))
3440 flags[flen++] = handle_flags[ii];
3442 saxdb_write_string(ctx, KEY_FLAGS, flags);
3444 saxdb_write_int(ctx, KEY_ID, hi->id);
3446 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3448 saxdb_write_string(ctx, KEY_DEVNULL, hi->devnull);
3450 saxdb_write_string(ctx, KEY_WEBSITE, hi->website);
3451 if (hi->last_quit_host[0])
3452 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3453 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3455 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3456 if (hi->masks->used)
3457 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3459 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3461 struct string_list *slist;
3462 struct nick_info *ni;
3464 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3465 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3466 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3470 if (hi->opserv_level)
3471 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3472 if (hi->language != lang_C)
3473 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3474 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3475 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3476 if (hi->screen_width)
3477 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3478 if (hi->table_width)
3479 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3480 flags[0] = hi->userlist_style;
3482 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3484 saxdb_start_record(ctx, KEY_AUTHLOG, 0);
3485 struct authlogEntry *authlog;
3487 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
3488 saxdb_start_record(ctx, strtab(++i), 0);
3489 saxdb_write_int(ctx, KEY_AUTHLOG_LOGIN_TIME, authlog->login_time);
3490 saxdb_write_int(ctx, KEY_AUTHLOG_LOGOUT_TIME, authlog->logout_time);
3491 saxdb_write_string(ctx, KEY_AUTHLOG_HOSTMASK, authlog->hostmask);
3492 if(authlog->quit_reason)
3493 saxdb_write_string(ctx, KEY_AUTHLOG_QUIT_REASON, authlog->quit_reason);
3494 saxdb_end_record(ctx);
3496 saxdb_end_record(ctx); //END KEY_AUTHLOG
3498 saxdb_end_record(ctx);
3503 static handle_merge_func_t *handle_merge_func_list;
3504 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3507 reg_handle_merge_func(handle_merge_func_t func)
3509 if (handle_merge_func_used == handle_merge_func_size) {
3510 if (handle_merge_func_size) {
3511 handle_merge_func_size <<= 1;
3512 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3514 handle_merge_func_size = 8;
3515 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3518 handle_merge_func_list[handle_merge_func_used++] = func;
3521 static NICKSERV_FUNC(cmd_merge)
3523 struct handle_info *hi_from, *hi_to;
3524 struct userNode *last_user;
3525 struct userData *cList, *cListNext;
3526 unsigned int ii, jj, n;
3527 char buffer[MAXLEN];
3529 NICKSERV_MIN_PARMS(3);
3531 if (!(hi_from = get_victim_oper(user, argv[1])))
3533 if (!(hi_to = get_victim_oper(user, argv[2])))
3535 if (hi_to == hi_from) {
3536 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3540 for (n=0; n<handle_merge_func_used; n++)
3541 handle_merge_func_list[n](user, hi_to, hi_from);
3543 /* Append "from" handle's nicks to "to" handle's nick list. */
3545 struct nick_info *last_ni;
3546 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3547 last_ni->next = hi_from->nicks;
3549 while (hi_from->nicks) {
3550 hi_from->nicks->owner = hi_to;
3551 hi_from->nicks = hi_from->nicks->next;
3554 /* Merge the hostmasks. */
3555 for (ii=0; ii<hi_from->masks->used; ii++) {
3556 char *mask = hi_from->masks->list[ii];
3557 for (jj=0; jj<hi_to->masks->used; jj++)
3558 if (match_ircglobs(hi_to->masks->list[jj], mask))
3560 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3561 string_list_append(hi_to->masks, strdup(mask));
3564 /* Merge the lists of authed users. */
3566 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3567 last_user->next_authed = hi_from->users;
3569 hi_to->users = hi_from->users;
3571 /* Repoint the old "from" handle's users. */
3572 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3573 last_user->handle_info = hi_to;
3575 hi_from->users = NULL;
3577 /* Merge channel userlists. */
3578 for (cList=hi_from->channels; cList; cList=cListNext) {
3579 struct userData *cList2;
3580 cListNext = cList->u_next;
3581 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3582 if (cList->channel == cList2->channel)
3584 if (cList2 && (cList2->access >= cList->access)) {
3585 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);
3586 /* keep cList2 in hi_to; remove cList from hi_from */
3587 del_channel_user(cList, 1);
3590 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);
3591 /* remove the lower-ranking cList2 from hi_to */
3592 del_channel_user(cList2, 1);
3594 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3596 /* cList needs to be moved from hi_from to hi_to */
3597 cList->handle = hi_to;
3598 /* Remove from linked list for hi_from */
3599 assert(!cList->u_prev);
3600 hi_from->channels = cList->u_next;
3602 cList->u_next->u_prev = cList->u_prev;
3603 /* Add to linked list for hi_to */
3604 cList->u_prev = NULL;
3605 cList->u_next = hi_to->channels;
3606 if (hi_to->channels)
3607 hi_to->channels->u_prev = cList;
3608 hi_to->channels = cList;
3612 /* Do they get an OpServ level promotion? */
3613 if (hi_from->opserv_level > hi_to->opserv_level)
3614 hi_to->opserv_level = hi_from->opserv_level;
3616 /* What about last seen time? */
3617 if (hi_from->lastseen > hi_to->lastseen)
3618 hi_to->lastseen = hi_from->lastseen;
3620 /* New karma is the sum of the two original karmas. */
3621 hi_to->karma += hi_from->karma;
3623 /* Does a fakehost carry over? (This intentionally doesn't set it
3624 * for users previously attached to hi_to. They'll just have to
3627 if (hi_from->fakehost && !hi_to->fakehost)
3628 hi_to->fakehost = strdup(hi_from->fakehost);
3629 if (hi_from->fakeident && !hi_to->fakeident)
3630 hi_to->fakeident = strdup(hi_from->fakeident);
3632 /* Notify of success. */
3633 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3634 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3635 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3637 /* Unregister the "from" handle. */
3638 nickserv_unregister_handle(hi_from, NULL);
3643 #define NICKSERV_DISCRIM_FIELDS_AUTH 0x01
3644 #define NICKSERV_DISCRIM_FIELDS_EMAIL 0x02
3645 #define NICKSERV_DISCRIM_FIELDS_SEEN 0x04
3646 #define NICKSERV_DISCRIM_FIELDS_ACCESS 0x08
3647 #define NICKSERV_DISCRIM_FIELDS_FAKEHOST 0x10
3648 #define NICKSERV_DISCRIM_FIELDS_WEBSITE 0x20
3649 #define NICKSERV_DISCRIM_FIELDS_DEVNULL 0x40
3651 #define NICKSERV_DISCRIM_FIELD_COUNT 7
3653 struct nickserv_discrim {
3654 unsigned int show_fields;
3655 struct helpfile_table *output_table;
3656 int output_table_pos;
3657 unsigned int output_table_free_fields;
3659 unsigned long flags_on, flags_off;
3660 unsigned long min_registered, max_registered;
3661 unsigned long lastseen;
3663 int min_level, max_level;
3664 int min_karma, max_karma;
3665 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3666 const char *nickmask;
3667 const char *hostmask;
3668 const char *fakehostmask;
3669 const char *fakeidentmask;
3670 const char *website;
3671 const char *devnullclass;
3672 const char *handlemask;
3673 const char *emailmask;
3676 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi, struct nickserv_discrim *discrim);
3678 struct discrim_apply_info {
3679 struct nickserv_discrim *discrim;
3680 discrim_search_func func;
3681 struct userNode *source;
3682 unsigned int matched;
3685 static struct nickserv_discrim *
3686 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3689 struct nickserv_discrim *discrim;
3691 discrim = malloc(sizeof(*discrim));
3692 memset(discrim, 0, sizeof(*discrim));
3693 discrim->min_level = 0;
3694 discrim->max_level = INT_MAX;
3695 discrim->limit = 50;
3696 discrim->min_registered = 0;
3697 discrim->max_registered = ULONG_MAX;
3698 discrim->lastseen = ULONG_MAX;
3699 discrim->min_karma = INT_MIN;
3700 discrim->max_karma = INT_MAX;
3702 for (i=0; i<argc; i++) {
3703 if (i == argc - 1) {
3704 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3707 if (!irccasecmp(argv[i], "limit")) {
3708 discrim->limit = strtoul(argv[++i], NULL, 0);
3709 } else if (!irccasecmp(argv[i], "flags")) {
3710 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3711 } else if (!irccasecmp(argv[i], "fields")) {
3712 char *fields = argv[++i];
3713 char *delimiter = strstr(fields, ",");
3717 if(!irccasecmp(fields, "auth"))
3718 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_AUTH;
3719 else if(!irccasecmp(fields, "email"))
3720 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_EMAIL;
3721 else if(!irccasecmp(fields, "seen"))
3722 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_SEEN;
3723 else if(!irccasecmp(fields, "access"))
3724 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_ACCESS;
3725 else if(!irccasecmp(fields, "fakehost"))
3726 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_FAKEHOST;
3727 else if(!irccasecmp(fields, "website") && IsBot(user))
3728 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_WEBSITE;
3729 else if(!irccasecmp(fields, "devnull"))
3730 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_DEVNULL;
3732 send_message(user, nickserv, "MSG_INVALID_FIELD", fields);
3737 fields = delimiter+1;
3739 delimiter = strstr(fields, ",");
3745 } else if (!irccasecmp(argv[i], "registered")) {
3746 const char *cmp = argv[++i];
3747 if (cmp[0] == '<') {
3748 if (cmp[1] == '=') {
3749 discrim->min_registered = now - ParseInterval(cmp+2);
3751 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3753 } else if (cmp[0] == '=') {
3754 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3755 } else if (cmp[0] == '>') {
3756 if (cmp[1] == '=') {
3757 discrim->max_registered = now - ParseInterval(cmp+2);
3759 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3762 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3764 } else if (!irccasecmp(argv[i], "seen")) {
3765 discrim->lastseen = now - ParseInterval(argv[++i]);
3766 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3767 discrim->nickmask = argv[++i];
3768 } else if (!irccasecmp(argv[i], "hostmask")) {
3770 if (!irccasecmp(argv[i], "exact")) {
3771 if (i == argc - 1) {
3772 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3775 discrim->hostmask_type = EXACT;
3776 } else if (!irccasecmp(argv[i], "subset")) {
3777 if (i == argc - 1) {
3778 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3781 discrim->hostmask_type = SUBSET;
3782 } else if (!irccasecmp(argv[i], "superset")) {
3783 if (i == argc - 1) {
3784 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3787 discrim->hostmask_type = SUPERSET;
3788 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3789 if (i == argc - 1) {
3790 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3793 discrim->hostmask_type = LASTQUIT;
3796 discrim->hostmask_type = SUPERSET;
3798 discrim->hostmask = argv[++i];
3799 } else if (!irccasecmp(argv[i], "fakehost")) {
3800 if (!irccasecmp(argv[++i], "*")) {
3801 discrim->fakehostmask = 0;
3803 discrim->fakehostmask = argv[i];
3805 } else if (!irccasecmp(argv[i], "fakeident")) {
3806 if (!irccasecmp(argv[++i], "*")) {
3807 discrim->fakeidentmask = 0;
3809 discrim->fakeidentmask = argv[i];
3811 } else if (!irccasecmp(argv[i], "website")) {
3812 if (!irccasecmp(argv[++i], "*")) {
3813 discrim->website = 0;
3815 discrim->website = argv[i];
3817 } else if (!irccasecmp(argv[i], "devnull")) {
3818 if (!irccasecmp(argv[++i], "*")) {
3819 discrim->devnullclass = 0;
3821 discrim->devnullclass = argv[i];
3823 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3824 if (!irccasecmp(argv[++i], "*")) {
3825 discrim->handlemask = 0;
3827 discrim->handlemask = argv[i];
3829 } else if (!irccasecmp(argv[i], "email")) {
3830 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3831 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3833 } else if (!irccasecmp(argv[++i], "*")) {
3834 discrim->emailmask = 0;
3836 discrim->emailmask = argv[i];
3838 } else if (!irccasecmp(argv[i], "access")) {
3839 const char *cmp = argv[++i];
3840 if (cmp[0] == '<') {
3841 if (discrim->min_level == 0) discrim->min_level = 1;
3842 if (cmp[1] == '=') {
3843 discrim->max_level = strtoul(cmp+2, NULL, 0);
3845 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3847 } else if (cmp[0] == '=') {
3848 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3849 } else if (cmp[0] == '>') {
3850 if (cmp[1] == '=') {
3851 discrim->min_level = strtoul(cmp+2, NULL, 0);
3853 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3856 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3858 } else if (!irccasecmp(argv[i], "karma")) {
3859 const char *cmp = argv[++i];
3860 if (cmp[0] == '<') {
3861 if (cmp[1] == '=') {
3862 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3864 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3866 } else if (cmp[0] == '=') {
3867 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3868 } else if (cmp[0] == '>') {
3869 if (cmp[1] == '=') {
3870 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3872 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3875 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3878 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3889 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3891 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3892 || (discrim->flags_off & hi->flags)
3893 || (discrim->min_registered > hi->registered)
3894 || (discrim->max_registered < hi->registered)
3895 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3896 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3897 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3898 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3899 || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website)))
3900 || (discrim->devnullclass && (!hi->devnull || !match_ircglob(hi->devnull, discrim->devnullclass)))
3901 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3902 || (discrim->min_level > hi->opserv_level)
3903 || (discrim->max_level < hi->opserv_level)
3904 || (discrim->min_karma > hi->karma)
3905 || (discrim->max_karma < hi->karma)
3909 if (discrim->hostmask) {
3911 for (i=0; i<hi->masks->used; i++) {
3912 const char *mask = hi->masks->list[i];
3913 if ((discrim->hostmask_type == SUBSET)
3914 && (match_ircglobs(discrim->hostmask, mask))) break;
3915 else if ((discrim->hostmask_type == EXACT)
3916 && !irccasecmp(discrim->hostmask, mask)) break;
3917 else if ((discrim->hostmask_type == SUPERSET)
3918 && (match_ircglobs(mask, discrim->hostmask))) break;
3919 else if ((discrim->hostmask_type == LASTQUIT)
3920 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3922 if (i==hi->masks->used) return 0;
3924 if (discrim->nickmask) {
3925 struct nick_info *nick = hi->nicks;
3927 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3930 if (!nick) return 0;
3936 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3938 dict_iterator_t it, next;
3939 unsigned int matched;
3941 for (it = dict_first(nickserv_handle_dict), matched = 0;
3942 it && (matched < discrim->limit);
3944 next = iter_next(it);
3945 if (nickserv_discrim_match(discrim, iter_data(it))) {
3946 dsf(source, iter_data(it), discrim);
3954 search_print_func(struct userNode *source, struct handle_info *match, struct nickserv_discrim *discrim)
3956 if(discrim->show_fields) {
3958 if(discrim->output_table) {
3959 discrim->output_table->contents[++discrim->output_table_pos] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
3961 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
3962 discrim->output_table->contents[discrim->output_table_pos][i++] = match->handle;
3963 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
3964 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->email_addr ? match->email_addr : "*");
3965 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN) {
3967 char seenBuf[INTERVALLEN];
3970 } else if(match->lastseen == 0) {
3973 seen = intervalString(seenBuf, now - match->lastseen, source->handle_info);
3975 discrim->output_table_free_fields |= 1 << i;
3976 discrim->output_table->contents[discrim->output_table_pos][i++] = strdup(seen);
3978 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
3979 discrim->output_table->contents[discrim->output_table_pos][i++] = strtab(match->opserv_level);
3980 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
3981 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->fakehost ? match->fakehost : "*");
3982 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
3983 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->website ? match->website : "*");
3984 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
3985 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->devnull ? match->devnull : "*");
3989 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3993 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match), UNUSED_ARG(struct nickserv_discrim *discrim))
3998 search_unregister_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
4000 if (oper_has_access(source, nickserv, match->opserv_level, 0))
4001 nickserv_unregister_handle(match, source);
4005 nickserv_sort_accounts_by_access(const void *a, const void *b)
4007 const struct handle_info *hi_a = *(const struct handle_info**)a;
4008 const struct handle_info *hi_b = *(const struct handle_info**)b;
4009 if (hi_a->opserv_level != hi_b->opserv_level)
4010 return hi_b->opserv_level - hi_a->opserv_level;
4011 return irccasecmp(hi_a->handle, hi_b->handle);
4015 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
4017 struct handle_info_list hil;
4018 struct helpfile_table tbl;
4023 memset(&hil, 0, sizeof(hil));
4024 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
4025 struct handle_info *hi = iter_data(it);
4026 if (hi->opserv_level)
4027 handle_info_list_append(&hil, hi);
4029 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
4030 tbl.length = hil.used + 1;
4032 tbl.flags = TABLE_NO_FREE;
4033 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4034 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
4037 for (ii = 0; ii < hil.used; ) {
4038 ary = malloc(tbl.width * sizeof(ary[0]));
4039 ary[0] = hil.list[ii]->handle;
4040 ary[1] = strtab(hil.list[ii]->opserv_level);
4041 tbl.contents[++ii] = ary;
4043 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4044 reply("MSG_MATCH_COUNT", hil.used);
4045 for (ii = 0; ii < hil.used; ii++)
4046 free(tbl.contents[ii]);
4051 static NICKSERV_FUNC(cmd_search)
4053 struct nickserv_discrim *discrim;
4054 discrim_search_func action;
4055 struct svccmd *subcmd;
4056 unsigned int matches;
4059 NICKSERV_MIN_PARMS(3);
4060 sprintf(buf, "search %s", argv[1]);
4061 subcmd = dict_find(nickserv_service->commands, buf, NULL);
4062 if (!irccasecmp(argv[1], "print"))
4063 action = search_print_func;
4064 else if (!irccasecmp(argv[1], "count"))
4065 action = search_count_func;
4066 else if (!irccasecmp(argv[1], "unregister"))
4067 action = search_unregister_func;
4069 reply("NSMSG_INVALID_ACTION", argv[1]);
4073 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
4076 discrim = nickserv_discrim_create(user, argc-2, argv+2);
4080 if (action == search_print_func)
4081 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
4082 else if (action == search_count_func)
4083 discrim->limit = INT_MAX;
4085 matches = nickserv_discrim_search(discrim, action, user);
4087 if(discrim->show_fields) {
4090 for(ii = 0; ii < NICKSERV_DISCRIM_FIELD_COUNT; ii++) {
4091 if(discrim->show_fields & (1 << ii)) width++;
4093 discrim->output_table = malloc(sizeof(discrim->output_table[0]));
4094 discrim->output_table->length = matches+1;
4095 discrim->output_table->width = width;
4096 discrim->output_table->flags = TABLE_NO_FREE;
4097 discrim->output_table->contents = malloc(discrim->output_table->length * sizeof(discrim->output_table->contents[0]));
4098 discrim->output_table->contents[0] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
4101 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
4102 discrim->output_table->contents[0][ii++] = "Auth";
4103 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
4104 discrim->output_table->contents[0][ii++] = "EMail";
4105 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN)
4106 discrim->output_table->contents[0][ii++] = "Seen";
4107 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
4108 discrim->output_table->contents[0][ii++] = "Access";
4109 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
4110 discrim->output_table->contents[0][ii++] = "Fakehost";
4111 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
4112 discrim->output_table->contents[0][ii++] = "Website";
4113 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
4114 discrim->output_table->contents[0][ii++] = "DevNull";
4116 nickserv_discrim_search(discrim, action, user);
4118 table_send(nickserv, user->nick, 0, NULL, *discrim->output_table);
4120 for(ii = 1; ii < discrim->output_table->length; ++ii) {
4122 for(ij = 0; ij < NICKSERV_DISCRIM_FIELD_COUNT; ij++) {
4123 if(discrim->output_table_free_fields & (1 << ij))
4124 free((char*)discrim->output_table->contents[ii][ij]);
4126 free(discrim->output_table->contents[ii]);
4128 free(discrim->output_table->contents[0]);
4129 free(discrim->output_table->contents);
4130 free(discrim->output_table);
4133 reply("MSG_MATCH_COUNT", matches);
4135 reply("MSG_NO_MATCHES");
4142 static MODCMD_FUNC(cmd_checkpass)
4144 struct handle_info *hi;
4146 NICKSERV_MIN_PARMS(3);
4147 if (!(hi = get_handle_info(argv[1]))) {
4148 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4151 if (checkpass(argv[2], hi->passwd))
4152 reply("CHECKPASS_YES");
4154 reply("CHECKPASS_NO");
4159 static MODCMD_FUNC(cmd_checkemail)
4161 struct handle_info *hi;
4163 NICKSERV_MIN_PARMS(3);
4164 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
4167 if (!hi->email_addr)
4168 reply("CHECKEMAIL_NOT_SET");
4169 else if (!irccasecmp(argv[2], hi->email_addr))
4170 reply("CHECKEMAIL_YES");
4172 reply("CHECKEMAIL_NO");
4177 nickserv_db_read_authlog(UNUSED_ARG(const char *key), void *data, void *extra)
4179 struct record_data *rd = data;
4180 struct handle_info *hi = extra;
4182 struct authlogEntry *authlog;
4183 authlog = malloc(sizeof(*authlog));
4185 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGIN_TIME, RECDB_QSTRING);
4186 authlog->login_time = str ? strtoul(str, NULL, 0) : 0;
4188 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGOUT_TIME, RECDB_QSTRING);
4189 authlog->logout_time = str ? strtoul(str, NULL, 0) : 0;
4191 str = database_get_data(rd->d.object, KEY_AUTHLOG_HOSTMASK, RECDB_QSTRING);
4192 authlog->hostmask = str ? strdup(str) : NULL;
4194 str = database_get_data(rd->d.object, KEY_AUTHLOG_QUIT_REASON, RECDB_QSTRING);
4195 authlog->quit_reason = str ? strdup(str) : NULL;
4197 authlog->user = NULL;
4199 authlog->next = NULL;
4201 //append it to the end of the list...
4202 struct authlogEntry *authlog_entry;
4204 hi->authlog = authlog;
4206 for(authlog_entry = hi->authlog; authlog_entry; authlog_entry = authlog_entry->next) {
4207 if(!authlog_entry->next) {
4208 authlog_entry->next = authlog;
4217 nickserv_db_read_handle(const char *handle, dict_t obj)
4220 struct string_list *masks, *slist;
4221 struct handle_info *hi;
4222 struct userNode *authed_users;
4223 struct userData *channel_list;
4228 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
4229 id = str ? strtoul(str, NULL, 0) : 0;
4230 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
4232 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
4235 if ((hi = get_handle_info(handle))) {
4236 authed_users = hi->users;
4237 channel_list = hi->channels;
4239 hi->channels = NULL;
4240 dict_remove(nickserv_handle_dict, hi->handle);
4242 authed_users = NULL;
4243 channel_list = NULL;
4245 hi = register_handle(handle, str, id);
4247 hi->users = authed_users;
4248 while (authed_users) {
4249 authed_users->handle_info = hi;
4250 authed_users = authed_users->next_authed;
4253 hi->channels = channel_list;
4254 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
4255 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
4256 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
4257 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
4258 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
4259 hi->language = language_find(str ? str : "C");
4260 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
4261 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
4262 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
4264 hi->infoline = strdup(str);
4265 str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING);
4267 hi->website = strdup(str);
4268 str = database_get_data(obj, KEY_DEVNULL, RECDB_QSTRING);
4270 hi->devnull = strdup(str);
4271 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
4272 hi->registered = str ? strtoul(str, NULL, 0) : now;
4273 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
4274 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
4275 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
4276 hi->karma = str ? strtoul(str, NULL, 0) : 0;
4277 /* We want to read the nicks even if disable_nicks is set. This is so
4278 * that we don't lose the nick data entirely. */
4279 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
4281 for (ii=0; ii<slist->used; ii++)
4282 register_nick(slist->list[ii], hi);
4284 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
4286 for (ii=0; str[ii]; ii++)
4287 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
4289 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
4290 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
4291 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
4292 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
4293 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
4294 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
4295 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
4297 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
4299 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
4300 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
4302 nickserv_set_email_addr(hi, str);
4303 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
4305 hi->epithet = strdup(str);
4306 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
4308 hi->fakehost = strdup(str);
4309 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
4311 hi->fakeident = strdup(str);
4312 /* Read the "cookie" sub-database (if it exists). */
4313 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
4315 const char *data, *type, *expires, *cookie_str;
4316 struct handle_cookie *cookie;
4318 cookie = calloc(1, sizeof(*cookie));
4319 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
4320 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
4321 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
4322 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
4323 if (!type || !expires || !cookie_str) {
4324 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
4327 if (!irccasecmp(type, KEY_ACTIVATION))
4328 cookie->type = ACTIVATION;
4329 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
4330 cookie->type = PASSWORD_CHANGE;
4331 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
4332 cookie->type = EMAIL_CHANGE;
4333 else if (!irccasecmp(type, KEY_ALLOWAUTH))
4334 cookie->type = ALLOWAUTH;
4336 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
4339 cookie->expires = strtoul(expires, NULL, 0);
4340 if (cookie->expires < now)
4343 cookie->data = strdup(data);
4344 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
4348 nickserv_bake_cookie(cookie);
4350 nickserv_free_cookie(cookie);
4352 /* Read the "notes" sub-database (if it exists). */
4353 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
4356 struct handle_note *last_note;
4357 struct handle_note *note;
4360 for (it = dict_first(subdb); it; it = iter_next(it)) {
4361 const char *expires;
4365 const char *note_id;
4368 note_id = iter_key(it);
4369 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
4371 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
4374 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
4375 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
4376 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
4377 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
4378 if (!setter || !text || !set) {
4379 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
4382 note = calloc(1, sizeof(*note) + strlen(text));
4384 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
4385 note->set = strtoul(set, NULL, 10);
4386 note->id = strtoul(note_id, NULL, 10);
4387 safestrncpy(note->setter, setter, sizeof(note->setter));
4388 strcpy(note->note, text);
4390 last_note->next = note;
4396 if ((subdb = database_get_data(obj, KEY_AUTHLOG, RECDB_OBJECT)))
4397 dict_foreach(subdb, nickserv_db_read_authlog, hi);
4401 nickserv_saxdb_read(dict_t db) {
4403 struct record_data *rd;
4405 for (it=dict_first(db); it; it=iter_next(it)) {
4407 nickserv_db_read_handle(iter_key(it), rd->d.object);
4412 static NICKSERV_FUNC(cmd_mergedb)
4414 struct timeval start, stop;
4417 NICKSERV_MIN_PARMS(2);
4418 gettimeofday(&start, NULL);
4419 if (!(db = parse_database(argv[1]))) {
4420 reply("NSMSG_DB_UNREADABLE", argv[1]);
4423 nickserv_saxdb_read(db);
4425 gettimeofday(&stop, NULL);
4426 stop.tv_sec -= start.tv_sec;
4427 stop.tv_usec -= start.tv_usec;
4428 if (stop.tv_usec < 0) {
4430 stop.tv_usec += 1000000;
4432 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
4437 expire_handles(UNUSED_ARG(void *data))
4439 dict_iterator_t it, next;
4440 unsigned long expiry;
4441 struct handle_info *hi;
4443 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4444 next = iter_next(it);
4446 if ((hi->opserv_level > 0)
4448 || HANDLE_FLAGGED(hi, FROZEN)
4449 || HANDLE_FLAGGED(hi, NODELETE)) {
4452 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4453 if ((now - hi->lastseen) > expiry) {
4454 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
4455 nickserv_unregister_handle(hi, NULL);
4459 if (nickserv_conf.handle_expire_frequency)
4460 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4464 nickserv_load_dict(const char *fname)
4468 if (!(file = fopen(fname, "r"))) {
4469 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4472 while (fgets(line, sizeof(line), file)) {
4475 if (line[strlen(line)-1] == '\n')
4476 line[strlen(line)-1] = 0;
4477 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4480 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4483 static enum reclaim_action
4484 reclaim_action_from_string(const char *str) {
4486 return RECLAIM_NONE;
4487 else if (!irccasecmp(str, "warn"))
4488 return RECLAIM_WARN;
4489 else if (!irccasecmp(str, "svsnick"))
4490 return RECLAIM_SVSNICK;
4491 else if (!irccasecmp(str, "kill"))
4492 return RECLAIM_KILL;
4494 return RECLAIM_NONE;
4498 nickserv_conf_read(void)
4500 dict_t conf_node, child;
4504 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4505 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4508 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4510 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4511 if (nickserv_conf.valid_handle_regex_set)
4512 regfree(&nickserv_conf.valid_handle_regex);
4514 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4515 nickserv_conf.valid_handle_regex_set = !err;
4516 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4518 nickserv_conf.valid_handle_regex_set = 0;
4520 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4521 if (nickserv_conf.valid_nick_regex_set)
4522 regfree(&nickserv_conf.valid_nick_regex);
4524 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4525 nickserv_conf.valid_nick_regex_set = !err;
4526 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4528 nickserv_conf.valid_nick_regex_set = 0;
4530 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4532 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4533 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4534 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4535 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4536 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4537 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4538 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4539 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4540 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4541 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4542 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4543 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4544 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4545 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4546 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4547 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4548 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4549 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4550 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4551 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4552 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4553 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4554 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4555 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4556 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
4557 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
4558 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4560 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4561 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4562 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4564 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4565 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4566 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4568 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4569 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4570 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4571 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4572 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4573 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4574 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4575 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4576 str = database_get_data(conf_node, KEY_MAX_AUTHLOG_LEN, RECDB_QSTRING);
4577 nickserv_conf.max_authlog_len = str ? strtoul(str, NULL, 0) : 30;
4578 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4579 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4580 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4583 nickserv_conf.ounregister_flags = 0;
4585 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4588 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4590 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4592 nickserv_conf.handle_ts_mode = TS_IGNORE;
4593 else if (!irccasecmp(str, "ircu"))
4594 nickserv_conf.handle_ts_mode = TS_IRCU;
4596 nickserv_conf.handle_ts_mode = TS_IGNORE;
4597 if (!nickserv_conf.disable_nicks) {
4598 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4599 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4600 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4601 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4602 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4603 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4604 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4605 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4607 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4608 for (it=dict_first(child); it; it=iter_next(it)) {
4609 const char *key = iter_key(it), *value;
4613 if (!strncasecmp(key, "uc_", 3))
4614 flag = toupper(key[3]);
4615 else if (!strncasecmp(key, "lc_", 3))
4616 flag = tolower(key[3]);
4620 if ((pos = handle_inverse_flags[flag])) {
4621 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4622 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4625 if (nickserv_conf.weak_password_dict)
4626 dict_delete(nickserv_conf.weak_password_dict);
4627 nickserv_conf.weak_password_dict = dict_new();
4628 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4629 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4630 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4631 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4633 nickserv_load_dict(str);
4634 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4635 if (nickserv && str)
4636 NickChange(nickserv, str, 0);
4637 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4638 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4639 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4640 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4641 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4642 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4643 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4644 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4645 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4646 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4647 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4648 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4649 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4650 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4651 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4652 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4653 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4654 titlehost_suffix = str ? str : "example.net";
4655 str = conf_get_data("server/network", RECDB_QSTRING);
4656 nickserv_conf.network_name = str ? str : "some IRC network";
4657 if (!nickserv_conf.auth_policer_params) {
4658 nickserv_conf.auth_policer_params = policer_params_new();
4659 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4660 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4662 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4663 for (it=dict_first(child); it; it=iter_next(it))
4664 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4668 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4670 char newnick[NICKLEN+1];
4679 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4681 case RECLAIM_SVSNICK:
4683 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4684 } while (GetUserH(newnick));
4685 irc_svsnick(nickserv, user, newnick);
4688 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4689 DelUser(user, nickserv, 1, msg);
4695 nickserv_reclaim_p(void *data) {
4696 struct userNode *user = data;
4697 struct nick_info *ni = get_nick_info(user->nick);
4699 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4703 check_user_nick(struct userNode *user) {
4704 //check if this user is a pending LOC user
4705 if(pendingLOCUsers) {
4706 struct pendingLOCUser *pending, *next, *prev = NULL;
4707 for(pending = pendingLOCUsers; pending; pending = next) {
4708 next = pending->next;
4709 if(user->handle_info == pending->handle_info) {
4710 pending->authlog->user = user;
4711 free((char*) pending->authlog->hostmask);
4712 pending->authlog->hostmask = generate_hostmask(user, GENMASK_USENICK|GENMASK_STRICT_IDENT|GENMASK_NO_HIDING|GENMASK_STRICT_HOST);
4716 pendingLOCUsers = next;
4719 if(now - pending->time > 10) {
4723 pendingLOCUsers = next;
4728 struct nick_info *ni;
4729 user->modes &= ~FLAGS_REGNICK;
4730 if (!(ni = get_nick_info(user->nick)))
4732 if (user->handle_info == ni->owner) {
4733 user->modes |= FLAGS_REGNICK;
4737 if (nickserv_conf.warn_nick_owned)
4738 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4739 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4741 if (nickserv_conf.auto_reclaim_delay)
4742 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4744 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4748 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4750 struct handle_info *hi = NULL;
4753 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4754 if ((hi == NULL) && (serial != 0)) {
4756 inttobase64(id, serial, IDLEN);
4757 hi = dict_find(nickserv_id_dict, id, NULL);
4761 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4762 && (timestamp != hi->registered)) {
4765 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4768 set_user_handle_info(user, hi, 0);
4770 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4775 handle_nick_change(struct userNode *user, const char *old_nick)
4777 struct handle_info *hi;
4779 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4780 dict_remove(nickserv_allow_auth_dict, old_nick);
4781 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4783 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4784 check_user_nick(user);
4788 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), const char *why)
4790 if(user->handle_info) {
4791 //check if theres an open authlog entry
4792 struct authlogEntry *authlog;
4793 for(authlog = user->handle_info->authlog; authlog; authlog = authlog->next) {
4794 if(authlog->user == user) {
4795 authlog->user = NULL;
4796 authlog->logout_time = now;
4797 authlog->quit_reason = strdup(why);
4802 dict_remove(nickserv_allow_auth_dict, user->nick);
4803 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4804 set_user_handle_info(user, NULL, 0);
4807 static struct modcmd *
4808 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4810 if (min_level > 0) {
4812 sprintf(buf, "%u", min_level);
4813 if (must_be_qualified) {
4814 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4816 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4818 } else if (min_level == 0) {
4819 if (must_be_qualified) {
4820 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4822 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4825 if (must_be_qualified) {
4826 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4828 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4834 nickserv_db_cleanup(void)
4836 unreg_del_user_func(nickserv_remove_user);
4837 userList_clean(&curr_helpers);
4838 policer_params_delete(nickserv_conf.auth_policer_params);
4839 dict_delete(nickserv_handle_dict);
4840 dict_delete(nickserv_nick_dict);
4841 dict_delete(nickserv_opt_dict);
4842 dict_delete(nickserv_allow_auth_dict);
4843 dict_delete(nickserv_email_dict);
4844 dict_delete(nickserv_id_dict);
4845 dict_delete(nickserv_conf.weak_password_dict);
4846 free(auth_func_list);
4847 free(unreg_func_list);
4849 free(allowauth_func_list);
4850 free(handle_merge_func_list);
4851 free(failpw_func_list);
4852 if (nickserv_conf.valid_handle_regex_set)
4853 regfree(&nickserv_conf.valid_handle_regex);
4854 if (nickserv_conf.valid_nick_regex_set)
4855 regfree(&nickserv_conf.valid_nick_regex);
4856 struct pendingLOCUser *pending, *next;
4857 for(pending = pendingLOCUsers; pending; pending = next) {
4858 next = pending->next;
4861 pendingLOCUsers = NULL;
4865 init_nickserv(const char *nick)
4868 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4869 reg_new_user_func(check_user_nick);
4870 reg_nick_change_func(handle_nick_change);
4871 reg_del_user_func(nickserv_remove_user);
4872 reg_account_func(handle_account);
4874 /* set up handle_inverse_flags */
4875 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4876 for (i=0; handle_flags[i]; i++) {
4877 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4878 flag_access_levels[i] = 0;
4881 conf_register_reload(nickserv_conf_read);
4882 nickserv_opt_dict = dict_new();
4883 nickserv_email_dict = dict_new();
4884 dict_set_free_keys(nickserv_email_dict, free);
4885 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4887 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4888 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4889 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4890 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4891 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4892 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4893 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4894 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4895 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4896 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4897 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4898 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4899 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4900 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4901 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4902 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4903 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4904 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4905 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4906 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4907 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4908 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4909 if (!nickserv_conf.disable_nicks) {
4910 /* nick management commands */
4911 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4912 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4913 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4914 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4915 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4916 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4918 if (nickserv_conf.email_enabled) {
4919 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4920 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4921 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4922 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4923 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4924 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4926 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4927 /* miscellaneous commands */
4928 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4929 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4930 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4931 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4932 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4933 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4934 nickserv_define_func("AUTHLOG", cmd_authlog, -1, 1, 0);
4935 nickserv_define_func("OAUTHLOG", cmd_oauthlog, 0, 1, 0);
4937 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4938 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4939 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4940 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4941 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4942 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4943 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
4944 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4945 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4946 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4947 dict_insert(nickserv_opt_dict, "WEBSITE", opt_website);
4948 dict_insert(nickserv_opt_dict, "DEVNULL", opt_devnull);
4949 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4950 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4951 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4952 if (titlehost_suffix) {
4953 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4954 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4955 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4957 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4958 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4959 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4960 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4962 nickserv_handle_dict = dict_new();
4963 dict_set_free_keys(nickserv_handle_dict, free);
4964 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4966 nickserv_id_dict = dict_new();
4967 dict_set_free_keys(nickserv_id_dict, free);
4969 nickserv_nick_dict = dict_new();
4970 dict_set_free_data(nickserv_nick_dict, free);
4972 nickserv_allow_auth_dict = dict_new();
4974 userList_init(&curr_helpers);
4977 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4978 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4979 nickserv_service = service_register(nickserv);
4981 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4982 reg_exit_func(nickserv_db_cleanup);
4983 if(nickserv_conf.handle_expire_frequency)
4984 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4985 message_register_table(msgtab);