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", "***");
2819 static OPTION_FUNC(opt_flags)
2822 unsigned int ii, flen;
2825 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2830 nickserv_apply_flags(user, hi, argv[1]);
2832 for (ii = flen = 0; handle_flags[ii]; ii++)
2833 if (hi->flags & (1 << ii))
2834 flags[flen++] = handle_flags[ii];
2837 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2839 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2843 static OPTION_FUNC(opt_email)
2847 if (!is_valid_email_addr(argv[1])) {
2848 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2851 if ((str = mail_prohibited_address(argv[1]))) {
2852 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2855 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2856 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2858 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2860 nickserv_set_email_addr(hi, argv[1]);
2862 nickserv_eat_cookie(hi->cookie);
2863 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2866 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2870 static OPTION_FUNC(opt_maxlogins)
2872 unsigned char maxlogins;
2874 maxlogins = strtoul(argv[1], NULL, 0);
2875 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2876 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2879 hi->maxlogins = maxlogins;
2881 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2882 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2886 static OPTION_FUNC(opt_language)
2888 struct language *lang;
2890 lang = language_find(argv[1]);
2891 if (irccasecmp(lang->name, argv[1]))
2892 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2893 hi->language = lang;
2895 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2899 static OPTION_FUNC(opt_karma)
2902 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2907 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2908 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2909 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2910 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2912 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2916 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2921 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2922 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2924 if ((user->handle_info->opserv_level < target->opserv_level)
2925 || ((user->handle_info->opserv_level == target->opserv_level)
2926 && (user->handle_info->opserv_level < 1000))) {
2927 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2930 if ((user->handle_info->opserv_level < new_level)
2931 || ((user->handle_info->opserv_level == new_level)
2932 && (user->handle_info->opserv_level < 1000))) {
2933 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2936 if (user->handle_info == target) {
2937 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2940 if (target->opserv_level == new_level)
2942 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2943 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2944 target->opserv_level = new_level;
2948 static OPTION_FUNC(opt_level)
2953 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2957 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2958 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2962 static OPTION_FUNC(opt_epithet)
2965 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2969 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2970 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2973 if ((epithet[0] == '*') && !epithet[1])
2976 hi->epithet = strdup(epithet);
2980 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2982 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2986 static OPTION_FUNC(opt_title)
2991 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2995 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2997 if (strchr(title, '.')) {
2998 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
3001 if ((strlen(user->handle_info->handle) + strlen(title) +
3002 strlen(titlehost_suffix) + 2) > HOSTLEN) {
3003 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
3008 if (!strcmp(title, "*")) {
3009 hi->fakehost = NULL;
3011 hi->fakehost = malloc(strlen(title)+2);
3012 hi->fakehost[0] = '.';
3013 strcpy(hi->fakehost+1, title);
3015 apply_fakehost(hi, NULL);
3016 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
3017 title = hi->fakehost + 1;
3021 title = user_find_message(user, "MSG_NONE");
3022 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
3026 static OPTION_FUNC(opt_fakehost)
3028 char mask[USERLEN + HOSTLEN + 2];
3032 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3036 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
3037 if(strlen(argv[1]) >= sizeof(mask)) {
3038 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
3042 safestrncpy(mask, argv[1], sizeof(mask));
3044 if ((host = strrchr(mask, '@')) && host != mask) {
3045 /* If ident@host was used and the user doesn't have access to set idents, do not change anything. */
3046 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3058 if (ident && strlen(ident) > USERLEN) {
3059 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3063 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
3064 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
3068 if (host && host[0]) {
3070 if (!strcmp(host, "*"))
3071 hi->fakehost = NULL;
3073 hi->fakehost = strdup(host);
3074 host = hi->fakehost;
3077 host = generate_fakehost(hi);
3080 free(hi->fakeident);
3081 if (!strcmp(ident, "*"))
3082 hi->fakeident = NULL;
3084 hi->fakeident = strdup(ident);
3085 ident = hi->fakeident;
3088 ident = generate_fakeident(hi, NULL);
3090 apply_fakehost(hi, NULL);
3092 host = generate_fakehost(hi);
3093 ident = generate_fakeident(hi, NULL);
3096 host = (char *) user_find_message(user, "MSG_NONE");
3098 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
3100 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
3104 static OPTION_FUNC(opt_fakeident)
3109 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3113 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3115 if (strlen(ident) > USERLEN) {
3116 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3119 free(hi->fakeident);
3120 if (!strcmp(ident, "*"))
3121 hi->fakeident = NULL;
3123 hi->fakeident = strdup(ident);
3124 ident = hi->fakeident;
3125 apply_fakehost(hi, NULL);
3127 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
3129 ident = user_find_message(user, "MSG_NONE");
3130 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
3134 static NICKSERV_FUNC(cmd_reclaim)
3136 struct nick_info *ni;
3137 struct userNode *victim;
3139 NICKSERV_MIN_PARMS(2);
3140 ni = dict_find(nickserv_nick_dict, argv[1], 0);
3142 reply("NSMSG_UNKNOWN_NICK", argv[1]);
3145 if (ni->owner != user->handle_info) {
3146 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
3149 victim = GetUserH(ni->nick);
3151 reply("MSG_NICK_UNKNOWN", ni->nick);
3154 if (victim == user) {
3155 reply("NSMSG_NICK_USER_YOU");
3158 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
3159 switch (nickserv_conf.reclaim_action) {
3160 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
3161 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
3162 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
3163 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
3168 static NICKSERV_FUNC(cmd_unregnick)
3171 struct handle_info *hi;
3172 struct nick_info *ni;
3174 hi = user->handle_info;
3175 nick = (argc < 2) ? user->nick : (const char*)argv[1];
3176 ni = dict_find(nickserv_nick_dict, nick, NULL);
3178 reply("NSMSG_UNKNOWN_NICK", nick);
3181 if (hi != ni->owner) {
3182 reply("NSMSG_NOT_YOUR_NICK", nick);
3185 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3190 static NICKSERV_FUNC(cmd_ounregnick)
3192 struct nick_info *ni;
3194 NICKSERV_MIN_PARMS(2);
3195 if (!(ni = get_nick_info(argv[1]))) {
3196 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
3199 if (!oper_outranks(user, ni->owner))
3201 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3206 static NICKSERV_FUNC(cmd_unregister)
3208 struct handle_info *hi;
3211 NICKSERV_MIN_PARMS(2);
3212 hi = user->handle_info;
3215 if (checkpass(passwd, hi->passwd)) {
3216 nickserv_unregister_handle(hi, user);
3219 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3220 reply("NSMSG_PASSWORD_INVALID");
3225 static NICKSERV_FUNC(cmd_ounregister)
3227 struct handle_info *hi;
3228 char reason[MAXLEN];
3231 NICKSERV_MIN_PARMS(2);
3232 if (!(hi = get_victim_oper(user, argv[1])))
3235 if (HANDLE_FLAGGED(hi, NODELETE)) {
3236 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
3240 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
3242 ((hi->flags & nickserv_conf.ounregister_flags)
3244 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
3245 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
3249 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
3250 global_message(MESSAGE_RECIPIENT_STAFF, reason);
3251 nickserv_unregister_handle(hi, user);
3255 static NICKSERV_FUNC(cmd_status)
3257 if (nickserv_conf.disable_nicks) {
3258 reply("NSMSG_GLOBAL_STATS_NONICK",
3259 dict_size(nickserv_handle_dict));
3261 if (user->handle_info) {
3263 struct nick_info *ni;
3264 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
3265 reply("NSMSG_HANDLE_STATS", cnt);
3267 reply("NSMSG_HANDLE_NONE");
3269 reply("NSMSG_GLOBAL_STATS",
3270 dict_size(nickserv_handle_dict),
3271 dict_size(nickserv_nick_dict));
3276 static NICKSERV_FUNC(cmd_ghost)
3278 struct userNode *target;
3279 char reason[MAXLEN];
3281 NICKSERV_MIN_PARMS(2);
3282 if (!(target = GetUserH(argv[1]))) {
3283 reply("MSG_NICK_UNKNOWN", argv[1]);
3286 if (target == user) {
3287 reply("NSMSG_CANNOT_GHOST_SELF");
3290 if (!target->handle_info || (target->handle_info != user->handle_info)) {
3291 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
3294 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
3295 DelUser(target, nickserv, 1, reason);
3296 reply("NSMSG_GHOST_KILLED", argv[1]);
3300 static NICKSERV_FUNC(cmd_vacation)
3302 HANDLE_SET_FLAG(user->handle_info, FROZEN);
3303 reply("NSMSG_ON_VACATION");
3307 static NICKSERV_FUNC(cmd_addnote)
3309 struct handle_info *hi;
3310 unsigned long duration;
3313 struct handle_note *prev;
3314 struct handle_note *note;
3316 /* Parse parameters and figure out values for note's fields. */
3317 NICKSERV_MIN_PARMS(4);
3318 hi = get_victim_oper(user, argv[1]);
3321 if(!strcmp(argv[2], "0"))
3323 else if(!(duration = ParseInterval(argv[2])))
3325 reply("MSG_INVALID_DURATION", argv[2]);
3328 if (duration > 2*365*86400) {
3329 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
3332 unsplit_string(argv + 3, argc - 3, text);
3333 WALK_NOTES(hi, prev, note) {}
3334 id = prev ? (prev->id + 1) : 1;
3336 /* Create the new note structure. */
3337 note = calloc(1, sizeof(*note) + strlen(text));
3339 note->expires = duration ? (now + duration) : 0;
3342 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
3343 strcpy(note->note, text);
3348 reply("NSMSG_NOTE_ADDED", id, hi->handle);
3352 static NICKSERV_FUNC(cmd_delnote)
3354 struct handle_info *hi;
3355 struct handle_note *prev;
3356 struct handle_note *note;
3359 NICKSERV_MIN_PARMS(3);
3360 hi = get_victim_oper(user, argv[1]);
3363 id = strtoul(argv[2], NULL, 10);
3364 WALK_NOTES(hi, prev, note) {
3365 if (id == note->id) {
3367 prev->next = note->next;
3369 hi->notes = note->next;
3371 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3375 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3380 nickserv_saxdb_write(struct saxdb_context *ctx) {
3382 struct handle_info *hi;
3385 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3387 assert(hi->id != 0);
3388 saxdb_start_record(ctx, iter_key(it), 0);
3390 struct handle_cookie *cookie = hi->cookie;
3393 switch (cookie->type) {
3394 case ACTIVATION: type = KEY_ACTIVATION; break;
3395 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3396 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3397 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3398 default: type = NULL; break;
3401 saxdb_start_record(ctx, KEY_COOKIE, 0);
3402 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3403 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3405 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3406 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3407 saxdb_end_record(ctx);
3411 struct handle_note *prev, *note;
3412 saxdb_start_record(ctx, KEY_NOTES, 0);
3413 WALK_NOTES(hi, prev, note) {
3414 snprintf(flags, sizeof(flags), "%d", note->id);
3415 saxdb_start_record(ctx, flags, 0);
3417 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3418 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3419 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3420 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3421 saxdb_end_record(ctx);
3423 saxdb_end_record(ctx);
3426 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3428 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3430 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3432 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3436 for (ii=flen=0; handle_flags[ii]; ++ii)
3437 if (hi->flags & (1 << ii))
3438 flags[flen++] = handle_flags[ii];
3440 saxdb_write_string(ctx, KEY_FLAGS, flags);
3442 saxdb_write_int(ctx, KEY_ID, hi->id);
3444 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3446 saxdb_write_string(ctx, KEY_DEVNULL, hi->devnull);
3448 saxdb_write_string(ctx, KEY_WEBSITE, hi->website);
3449 if (hi->last_quit_host[0])
3450 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3451 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3453 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3454 if (hi->masks->used)
3455 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3457 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3459 struct string_list *slist;
3460 struct nick_info *ni;
3462 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3463 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3464 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3468 if (hi->opserv_level)
3469 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3470 if (hi->language != lang_C)
3471 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3472 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3473 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3474 if (hi->screen_width)
3475 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3476 if (hi->table_width)
3477 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3478 flags[0] = hi->userlist_style;
3480 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3482 saxdb_start_record(ctx, KEY_AUTHLOG, 0);
3483 struct authlogEntry *authlog;
3485 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
3486 saxdb_start_record(ctx, strtab(++i), 0);
3487 saxdb_write_int(ctx, KEY_AUTHLOG_LOGIN_TIME, authlog->login_time);
3488 saxdb_write_int(ctx, KEY_AUTHLOG_LOGOUT_TIME, authlog->logout_time);
3489 saxdb_write_string(ctx, KEY_AUTHLOG_HOSTMASK, authlog->hostmask);
3490 if(authlog->quit_reason)
3491 saxdb_write_string(ctx, KEY_AUTHLOG_QUIT_REASON, authlog->quit_reason);
3492 saxdb_end_record(ctx);
3494 saxdb_end_record(ctx); //END KEY_AUTHLOG
3496 saxdb_end_record(ctx);
3501 static handle_merge_func_t *handle_merge_func_list;
3502 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3505 reg_handle_merge_func(handle_merge_func_t func)
3507 if (handle_merge_func_used == handle_merge_func_size) {
3508 if (handle_merge_func_size) {
3509 handle_merge_func_size <<= 1;
3510 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3512 handle_merge_func_size = 8;
3513 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3516 handle_merge_func_list[handle_merge_func_used++] = func;
3519 static NICKSERV_FUNC(cmd_merge)
3521 struct handle_info *hi_from, *hi_to;
3522 struct userNode *last_user;
3523 struct userData *cList, *cListNext;
3524 unsigned int ii, jj, n;
3525 char buffer[MAXLEN];
3527 NICKSERV_MIN_PARMS(3);
3529 if (!(hi_from = get_victim_oper(user, argv[1])))
3531 if (!(hi_to = get_victim_oper(user, argv[2])))
3533 if (hi_to == hi_from) {
3534 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3538 for (n=0; n<handle_merge_func_used; n++)
3539 handle_merge_func_list[n](user, hi_to, hi_from);
3541 /* Append "from" handle's nicks to "to" handle's nick list. */
3543 struct nick_info *last_ni;
3544 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3545 last_ni->next = hi_from->nicks;
3547 while (hi_from->nicks) {
3548 hi_from->nicks->owner = hi_to;
3549 hi_from->nicks = hi_from->nicks->next;
3552 /* Merge the hostmasks. */
3553 for (ii=0; ii<hi_from->masks->used; ii++) {
3554 char *mask = hi_from->masks->list[ii];
3555 for (jj=0; jj<hi_to->masks->used; jj++)
3556 if (match_ircglobs(hi_to->masks->list[jj], mask))
3558 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3559 string_list_append(hi_to->masks, strdup(mask));
3562 /* Merge the lists of authed users. */
3564 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3565 last_user->next_authed = hi_from->users;
3567 hi_to->users = hi_from->users;
3569 /* Repoint the old "from" handle's users. */
3570 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3571 last_user->handle_info = hi_to;
3573 hi_from->users = NULL;
3575 /* Merge channel userlists. */
3576 for (cList=hi_from->channels; cList; cList=cListNext) {
3577 struct userData *cList2;
3578 cListNext = cList->u_next;
3579 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3580 if (cList->channel == cList2->channel)
3582 if (cList2 && (cList2->access >= cList->access)) {
3583 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);
3584 /* keep cList2 in hi_to; remove cList from hi_from */
3585 del_channel_user(cList, 1);
3588 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);
3589 /* remove the lower-ranking cList2 from hi_to */
3590 del_channel_user(cList2, 1);
3592 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3594 /* cList needs to be moved from hi_from to hi_to */
3595 cList->handle = hi_to;
3596 /* Remove from linked list for hi_from */
3597 assert(!cList->u_prev);
3598 hi_from->channels = cList->u_next;
3600 cList->u_next->u_prev = cList->u_prev;
3601 /* Add to linked list for hi_to */
3602 cList->u_prev = NULL;
3603 cList->u_next = hi_to->channels;
3604 if (hi_to->channels)
3605 hi_to->channels->u_prev = cList;
3606 hi_to->channels = cList;
3610 /* Do they get an OpServ level promotion? */
3611 if (hi_from->opserv_level > hi_to->opserv_level)
3612 hi_to->opserv_level = hi_from->opserv_level;
3614 /* What about last seen time? */
3615 if (hi_from->lastseen > hi_to->lastseen)
3616 hi_to->lastseen = hi_from->lastseen;
3618 /* New karma is the sum of the two original karmas. */
3619 hi_to->karma += hi_from->karma;
3621 /* Does a fakehost carry over? (This intentionally doesn't set it
3622 * for users previously attached to hi_to. They'll just have to
3625 if (hi_from->fakehost && !hi_to->fakehost)
3626 hi_to->fakehost = strdup(hi_from->fakehost);
3627 if (hi_from->fakeident && !hi_to->fakeident)
3628 hi_to->fakeident = strdup(hi_from->fakeident);
3630 /* Notify of success. */
3631 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3632 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3633 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3635 /* Unregister the "from" handle. */
3636 nickserv_unregister_handle(hi_from, NULL);
3641 #define NICKSERV_DISCRIM_FIELDS_AUTH 0x01
3642 #define NICKSERV_DISCRIM_FIELDS_EMAIL 0x02
3643 #define NICKSERV_DISCRIM_FIELDS_SEEN 0x04
3644 #define NICKSERV_DISCRIM_FIELDS_ACCESS 0x08
3645 #define NICKSERV_DISCRIM_FIELDS_FAKEHOST 0x10
3646 #define NICKSERV_DISCRIM_FIELDS_WEBSITE 0x20
3647 #define NICKSERV_DISCRIM_FIELDS_DEVNULL 0x40
3649 #define NICKSERV_DISCRIM_FIELD_COUNT 7
3651 struct nickserv_discrim {
3652 unsigned int show_fields;
3653 struct helpfile_table *output_table;
3654 int output_table_pos;
3655 unsigned int output_table_free_fields;
3657 unsigned long flags_on, flags_off;
3658 unsigned long min_registered, max_registered;
3659 unsigned long lastseen;
3661 int min_level, max_level;
3662 int min_karma, max_karma;
3663 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3664 const char *nickmask;
3665 const char *hostmask;
3666 const char *fakehostmask;
3667 const char *fakeidentmask;
3668 const char *website;
3669 const char *devnullclass;
3670 const char *handlemask;
3671 const char *emailmask;
3674 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi, struct nickserv_discrim *discrim);
3676 struct discrim_apply_info {
3677 struct nickserv_discrim *discrim;
3678 discrim_search_func func;
3679 struct userNode *source;
3680 unsigned int matched;
3683 static struct nickserv_discrim *
3684 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3687 struct nickserv_discrim *discrim;
3689 discrim = malloc(sizeof(*discrim));
3690 memset(discrim, 0, sizeof(*discrim));
3691 discrim->min_level = 0;
3692 discrim->max_level = INT_MAX;
3693 discrim->limit = 50;
3694 discrim->min_registered = 0;
3695 discrim->max_registered = ULONG_MAX;
3696 discrim->lastseen = ULONG_MAX;
3697 discrim->min_karma = INT_MIN;
3698 discrim->max_karma = INT_MAX;
3700 for (i=0; i<argc; i++) {
3701 if (i == argc - 1) {
3702 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3705 if (!irccasecmp(argv[i], "limit")) {
3706 discrim->limit = strtoul(argv[++i], NULL, 0);
3707 } else if (!irccasecmp(argv[i], "flags")) {
3708 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3709 } else if (!irccasecmp(argv[i], "fields")) {
3710 char *fields = argv[++i];
3711 char *delimiter = strstr(fields, ",");
3715 if(!irccasecmp(fields, "auth"))
3716 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_AUTH;
3717 else if(!irccasecmp(fields, "email"))
3718 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_EMAIL;
3719 else if(!irccasecmp(fields, "seen"))
3720 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_SEEN;
3721 else if(!irccasecmp(fields, "access"))
3722 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_ACCESS;
3723 else if(!irccasecmp(fields, "fakehost"))
3724 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_FAKEHOST;
3725 else if(!irccasecmp(fields, "website") && IsBot(user))
3726 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_WEBSITE;
3727 else if(!irccasecmp(fields, "devnull"))
3728 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_DEVNULL;
3730 send_message(user, nickserv, "MSG_INVALID_FIELD", fields);
3735 fields = delimiter+1;
3737 delimiter = strstr(fields, ",");
3743 } else if (!irccasecmp(argv[i], "registered")) {
3744 const char *cmp = argv[++i];
3745 if (cmp[0] == '<') {
3746 if (cmp[1] == '=') {
3747 discrim->min_registered = now - ParseInterval(cmp+2);
3749 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3751 } else if (cmp[0] == '=') {
3752 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3753 } else if (cmp[0] == '>') {
3754 if (cmp[1] == '=') {
3755 discrim->max_registered = now - ParseInterval(cmp+2);
3757 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3760 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3762 } else if (!irccasecmp(argv[i], "seen")) {
3763 discrim->lastseen = now - ParseInterval(argv[++i]);
3764 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3765 discrim->nickmask = argv[++i];
3766 } else if (!irccasecmp(argv[i], "hostmask")) {
3768 if (!irccasecmp(argv[i], "exact")) {
3769 if (i == argc - 1) {
3770 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3773 discrim->hostmask_type = EXACT;
3774 } else if (!irccasecmp(argv[i], "subset")) {
3775 if (i == argc - 1) {
3776 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3779 discrim->hostmask_type = SUBSET;
3780 } else if (!irccasecmp(argv[i], "superset")) {
3781 if (i == argc - 1) {
3782 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3785 discrim->hostmask_type = SUPERSET;
3786 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3787 if (i == argc - 1) {
3788 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3791 discrim->hostmask_type = LASTQUIT;
3794 discrim->hostmask_type = SUPERSET;
3796 discrim->hostmask = argv[++i];
3797 } else if (!irccasecmp(argv[i], "fakehost")) {
3798 if (!irccasecmp(argv[++i], "*")) {
3799 discrim->fakehostmask = 0;
3801 discrim->fakehostmask = argv[i];
3803 } else if (!irccasecmp(argv[i], "fakeident")) {
3804 if (!irccasecmp(argv[++i], "*")) {
3805 discrim->fakeidentmask = 0;
3807 discrim->fakeidentmask = argv[i];
3809 } else if (!irccasecmp(argv[i], "website")) {
3810 if (!irccasecmp(argv[++i], "*")) {
3811 discrim->website = 0;
3813 discrim->website = argv[i];
3815 } else if (!irccasecmp(argv[i], "devnull")) {
3816 if (!irccasecmp(argv[++i], "*")) {
3817 discrim->devnullclass = 0;
3819 discrim->devnullclass = argv[i];
3821 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3822 if (!irccasecmp(argv[++i], "*")) {
3823 discrim->handlemask = 0;
3825 discrim->handlemask = argv[i];
3827 } else if (!irccasecmp(argv[i], "email")) {
3828 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3829 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3831 } else if (!irccasecmp(argv[++i], "*")) {
3832 discrim->emailmask = 0;
3834 discrim->emailmask = argv[i];
3836 } else if (!irccasecmp(argv[i], "access")) {
3837 const char *cmp = argv[++i];
3838 if (cmp[0] == '<') {
3839 if (discrim->min_level == 0) discrim->min_level = 1;
3840 if (cmp[1] == '=') {
3841 discrim->max_level = strtoul(cmp+2, NULL, 0);
3843 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3845 } else if (cmp[0] == '=') {
3846 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3847 } else if (cmp[0] == '>') {
3848 if (cmp[1] == '=') {
3849 discrim->min_level = strtoul(cmp+2, NULL, 0);
3851 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3854 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3856 } else if (!irccasecmp(argv[i], "karma")) {
3857 const char *cmp = argv[++i];
3858 if (cmp[0] == '<') {
3859 if (cmp[1] == '=') {
3860 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3862 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3864 } else if (cmp[0] == '=') {
3865 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3866 } else if (cmp[0] == '>') {
3867 if (cmp[1] == '=') {
3868 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3870 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3873 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3876 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3887 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3889 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3890 || (discrim->flags_off & hi->flags)
3891 || (discrim->min_registered > hi->registered)
3892 || (discrim->max_registered < hi->registered)
3893 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3894 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3895 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3896 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3897 || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website)))
3898 || (discrim->devnullclass && (!hi->devnull || !match_ircglob(hi->devnull, discrim->devnullclass)))
3899 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3900 || (discrim->min_level > hi->opserv_level)
3901 || (discrim->max_level < hi->opserv_level)
3902 || (discrim->min_karma > hi->karma)
3903 || (discrim->max_karma < hi->karma)
3907 if (discrim->hostmask) {
3909 for (i=0; i<hi->masks->used; i++) {
3910 const char *mask = hi->masks->list[i];
3911 if ((discrim->hostmask_type == SUBSET)
3912 && (match_ircglobs(discrim->hostmask, mask))) break;
3913 else if ((discrim->hostmask_type == EXACT)
3914 && !irccasecmp(discrim->hostmask, mask)) break;
3915 else if ((discrim->hostmask_type == SUPERSET)
3916 && (match_ircglobs(mask, discrim->hostmask))) break;
3917 else if ((discrim->hostmask_type == LASTQUIT)
3918 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3920 if (i==hi->masks->used) return 0;
3922 if (discrim->nickmask) {
3923 struct nick_info *nick = hi->nicks;
3925 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3928 if (!nick) return 0;
3934 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3936 dict_iterator_t it, next;
3937 unsigned int matched;
3939 for (it = dict_first(nickserv_handle_dict), matched = 0;
3940 it && (matched < discrim->limit);
3942 next = iter_next(it);
3943 if (nickserv_discrim_match(discrim, iter_data(it))) {
3944 dsf(source, iter_data(it), discrim);
3952 search_print_func(struct userNode *source, struct handle_info *match, struct nickserv_discrim *discrim)
3954 if(discrim->show_fields) {
3956 if(discrim->output_table) {
3957 discrim->output_table->contents[++discrim->output_table_pos] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
3959 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
3960 discrim->output_table->contents[discrim->output_table_pos][i++] = match->handle;
3961 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
3962 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->email_addr ? match->email_addr : "*");
3963 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN) {
3965 char seenBuf[INTERVALLEN];
3968 } else if(match->lastseen == 0) {
3971 seen = intervalString(seenBuf, now - match->lastseen, source->handle_info);
3973 discrim->output_table_free_fields |= 1 << i;
3974 discrim->output_table->contents[discrim->output_table_pos][i++] = strdup(seen);
3976 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
3977 discrim->output_table->contents[discrim->output_table_pos][i++] = strtab(match->opserv_level);
3978 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
3979 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->fakehost ? match->fakehost : "*");
3980 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
3981 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->website ? match->website : "*");
3982 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
3983 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->devnull ? match->devnull : "*");
3987 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3991 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match), UNUSED_ARG(struct nickserv_discrim *discrim))
3996 search_unregister_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
3998 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3999 nickserv_unregister_handle(match, source);
4003 nickserv_sort_accounts_by_access(const void *a, const void *b)
4005 const struct handle_info *hi_a = *(const struct handle_info**)a;
4006 const struct handle_info *hi_b = *(const struct handle_info**)b;
4007 if (hi_a->opserv_level != hi_b->opserv_level)
4008 return hi_b->opserv_level - hi_a->opserv_level;
4009 return irccasecmp(hi_a->handle, hi_b->handle);
4013 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
4015 struct handle_info_list hil;
4016 struct helpfile_table tbl;
4021 memset(&hil, 0, sizeof(hil));
4022 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
4023 struct handle_info *hi = iter_data(it);
4024 if (hi->opserv_level)
4025 handle_info_list_append(&hil, hi);
4027 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
4028 tbl.length = hil.used + 1;
4030 tbl.flags = TABLE_NO_FREE;
4031 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4032 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
4035 for (ii = 0; ii < hil.used; ) {
4036 ary = malloc(tbl.width * sizeof(ary[0]));
4037 ary[0] = hil.list[ii]->handle;
4038 ary[1] = strtab(hil.list[ii]->opserv_level);
4039 tbl.contents[++ii] = ary;
4041 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4042 reply("MSG_MATCH_COUNT", hil.used);
4043 for (ii = 0; ii < hil.used; ii++)
4044 free(tbl.contents[ii]);
4049 static NICKSERV_FUNC(cmd_search)
4051 struct nickserv_discrim *discrim;
4052 discrim_search_func action;
4053 struct svccmd *subcmd;
4054 unsigned int matches;
4057 NICKSERV_MIN_PARMS(3);
4058 sprintf(buf, "search %s", argv[1]);
4059 subcmd = dict_find(nickserv_service->commands, buf, NULL);
4060 if (!irccasecmp(argv[1], "print"))
4061 action = search_print_func;
4062 else if (!irccasecmp(argv[1], "count"))
4063 action = search_count_func;
4064 else if (!irccasecmp(argv[1], "unregister"))
4065 action = search_unregister_func;
4067 reply("NSMSG_INVALID_ACTION", argv[1]);
4071 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
4074 discrim = nickserv_discrim_create(user, argc-2, argv+2);
4078 if (action == search_print_func)
4079 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
4080 else if (action == search_count_func)
4081 discrim->limit = INT_MAX;
4083 matches = nickserv_discrim_search(discrim, action, user);
4085 if(discrim->show_fields) {
4088 for(ii = 0; ii < NICKSERV_DISCRIM_FIELD_COUNT; ii++) {
4089 if(discrim->show_fields & (1 << ii)) width++;
4091 discrim->output_table = malloc(sizeof(discrim->output_table[0]));
4092 discrim->output_table->length = matches+1;
4093 discrim->output_table->width = width;
4094 discrim->output_table->flags = TABLE_NO_FREE;
4095 discrim->output_table->contents = malloc(discrim->output_table->length * sizeof(discrim->output_table->contents[0]));
4096 discrim->output_table->contents[0] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
4099 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
4100 discrim->output_table->contents[0][ii++] = "Auth";
4101 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
4102 discrim->output_table->contents[0][ii++] = "EMail";
4103 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN)
4104 discrim->output_table->contents[0][ii++] = "Seen";
4105 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
4106 discrim->output_table->contents[0][ii++] = "Access";
4107 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
4108 discrim->output_table->contents[0][ii++] = "Fakehost";
4109 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
4110 discrim->output_table->contents[0][ii++] = "Website";
4111 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
4112 discrim->output_table->contents[0][ii++] = "DevNull";
4114 nickserv_discrim_search(discrim, action, user);
4116 table_send(nickserv, user->nick, 0, NULL, *discrim->output_table);
4118 for(ii = 1; ii < discrim->output_table->length; ++ii) {
4120 for(ij = 0; ij < NICKSERV_DISCRIM_FIELD_COUNT; ij++) {
4121 if(discrim->output_table_free_fields & (1 << ij))
4122 free((char*)discrim->output_table->contents[ii][ij]);
4124 free(discrim->output_table->contents[ii]);
4126 free(discrim->output_table->contents[0]);
4127 free(discrim->output_table->contents);
4128 free(discrim->output_table);
4131 reply("MSG_MATCH_COUNT", matches);
4133 reply("MSG_NO_MATCHES");
4140 static MODCMD_FUNC(cmd_checkpass)
4142 struct handle_info *hi;
4144 NICKSERV_MIN_PARMS(3);
4145 if (!(hi = get_handle_info(argv[1]))) {
4146 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4149 if (checkpass(argv[2], hi->passwd))
4150 reply("CHECKPASS_YES");
4152 reply("CHECKPASS_NO");
4157 static MODCMD_FUNC(cmd_checkemail)
4159 struct handle_info *hi;
4161 NICKSERV_MIN_PARMS(3);
4162 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
4165 if (!hi->email_addr)
4166 reply("CHECKEMAIL_NOT_SET");
4167 else if (!irccasecmp(argv[2], hi->email_addr))
4168 reply("CHECKEMAIL_YES");
4170 reply("CHECKEMAIL_NO");
4175 nickserv_db_read_authlog(UNUSED_ARG(const char *key), void *data, void *extra)
4177 struct record_data *rd = data;
4178 struct handle_info *hi = extra;
4180 struct authlogEntry *authlog;
4181 authlog = malloc(sizeof(*authlog));
4183 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGIN_TIME, RECDB_QSTRING);
4184 authlog->login_time = str ? strtoul(str, NULL, 0) : 0;
4186 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGOUT_TIME, RECDB_QSTRING);
4187 authlog->logout_time = str ? strtoul(str, NULL, 0) : 0;
4189 str = database_get_data(rd->d.object, KEY_AUTHLOG_HOSTMASK, RECDB_QSTRING);
4190 authlog->hostmask = str ? strdup(str) : NULL;
4192 str = database_get_data(rd->d.object, KEY_AUTHLOG_QUIT_REASON, RECDB_QSTRING);
4193 authlog->quit_reason = str ? strdup(str) : NULL;
4195 authlog->next = NULL;
4197 //append it to the end of the list...
4198 struct authlogEntry *authlog_entry;
4200 hi->authlog = authlog;
4202 for(authlog_entry = hi->authlog; authlog_entry; authlog_entry = authlog_entry->next) {
4203 if(!authlog_entry->next) {
4204 authlog_entry->next = authlog;
4213 nickserv_db_read_handle(const char *handle, dict_t obj)
4216 struct string_list *masks, *slist;
4217 struct handle_info *hi;
4218 struct userNode *authed_users;
4219 struct userData *channel_list;
4224 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
4225 id = str ? strtoul(str, NULL, 0) : 0;
4226 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
4228 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
4231 if ((hi = get_handle_info(handle))) {
4232 authed_users = hi->users;
4233 channel_list = hi->channels;
4235 hi->channels = NULL;
4236 dict_remove(nickserv_handle_dict, hi->handle);
4238 authed_users = NULL;
4239 channel_list = NULL;
4241 hi = register_handle(handle, str, id);
4243 hi->users = authed_users;
4244 while (authed_users) {
4245 authed_users->handle_info = hi;
4246 authed_users = authed_users->next_authed;
4249 hi->channels = channel_list;
4250 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
4251 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
4252 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
4253 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
4254 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
4255 hi->language = language_find(str ? str : "C");
4256 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
4257 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
4258 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
4260 hi->infoline = strdup(str);
4261 str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING);
4263 hi->website = strdup(str);
4264 str = database_get_data(obj, KEY_DEVNULL, RECDB_QSTRING);
4266 hi->devnull = strdup(str);
4267 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
4268 hi->registered = str ? strtoul(str, NULL, 0) : now;
4269 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
4270 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
4271 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
4272 hi->karma = str ? strtoul(str, NULL, 0) : 0;
4273 /* We want to read the nicks even if disable_nicks is set. This is so
4274 * that we don't lose the nick data entirely. */
4275 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
4277 for (ii=0; ii<slist->used; ii++)
4278 register_nick(slist->list[ii], hi);
4280 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
4282 for (ii=0; str[ii]; ii++)
4283 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
4285 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
4286 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
4287 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
4288 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
4289 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
4290 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
4291 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
4293 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
4295 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
4296 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
4298 nickserv_set_email_addr(hi, str);
4299 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
4301 hi->epithet = strdup(str);
4302 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
4304 hi->fakehost = strdup(str);
4305 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
4307 hi->fakeident = strdup(str);
4308 /* Read the "cookie" sub-database (if it exists). */
4309 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
4311 const char *data, *type, *expires, *cookie_str;
4312 struct handle_cookie *cookie;
4314 cookie = calloc(1, sizeof(*cookie));
4315 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
4316 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
4317 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
4318 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
4319 if (!type || !expires || !cookie_str) {
4320 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
4323 if (!irccasecmp(type, KEY_ACTIVATION))
4324 cookie->type = ACTIVATION;
4325 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
4326 cookie->type = PASSWORD_CHANGE;
4327 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
4328 cookie->type = EMAIL_CHANGE;
4329 else if (!irccasecmp(type, KEY_ALLOWAUTH))
4330 cookie->type = ALLOWAUTH;
4332 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
4335 cookie->expires = strtoul(expires, NULL, 0);
4336 if (cookie->expires < now)
4339 cookie->data = strdup(data);
4340 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
4344 nickserv_bake_cookie(cookie);
4346 nickserv_free_cookie(cookie);
4348 /* Read the "notes" sub-database (if it exists). */
4349 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
4352 struct handle_note *last_note;
4353 struct handle_note *note;
4356 for (it = dict_first(subdb); it; it = iter_next(it)) {
4357 const char *expires;
4361 const char *note_id;
4364 note_id = iter_key(it);
4365 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
4367 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
4370 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
4371 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
4372 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
4373 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
4374 if (!setter || !text || !set) {
4375 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
4378 note = calloc(1, sizeof(*note) + strlen(text));
4380 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
4381 note->set = strtoul(set, NULL, 10);
4382 note->id = strtoul(note_id, NULL, 10);
4383 safestrncpy(note->setter, setter, sizeof(note->setter));
4384 strcpy(note->note, text);
4386 last_note->next = note;
4392 if ((subdb = database_get_data(obj, KEY_AUTHLOG, RECDB_OBJECT)))
4393 dict_foreach(subdb, nickserv_db_read_authlog, hi);
4397 nickserv_saxdb_read(dict_t db) {
4399 struct record_data *rd;
4401 for (it=dict_first(db); it; it=iter_next(it)) {
4403 nickserv_db_read_handle(iter_key(it), rd->d.object);
4408 static NICKSERV_FUNC(cmd_mergedb)
4410 struct timeval start, stop;
4413 NICKSERV_MIN_PARMS(2);
4414 gettimeofday(&start, NULL);
4415 if (!(db = parse_database(argv[1]))) {
4416 reply("NSMSG_DB_UNREADABLE", argv[1]);
4419 nickserv_saxdb_read(db);
4421 gettimeofday(&stop, NULL);
4422 stop.tv_sec -= start.tv_sec;
4423 stop.tv_usec -= start.tv_usec;
4424 if (stop.tv_usec < 0) {
4426 stop.tv_usec += 1000000;
4428 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
4433 expire_handles(UNUSED_ARG(void *data))
4435 dict_iterator_t it, next;
4436 unsigned long expiry;
4437 struct handle_info *hi;
4439 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4440 next = iter_next(it);
4442 if ((hi->opserv_level > 0)
4444 || HANDLE_FLAGGED(hi, FROZEN)
4445 || HANDLE_FLAGGED(hi, NODELETE)) {
4448 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4449 if ((now - hi->lastseen) > expiry) {
4450 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
4451 nickserv_unregister_handle(hi, NULL);
4455 if (nickserv_conf.handle_expire_frequency)
4456 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4460 nickserv_load_dict(const char *fname)
4464 if (!(file = fopen(fname, "r"))) {
4465 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4468 while (fgets(line, sizeof(line), file)) {
4471 if (line[strlen(line)-1] == '\n')
4472 line[strlen(line)-1] = 0;
4473 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4476 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4479 static enum reclaim_action
4480 reclaim_action_from_string(const char *str) {
4482 return RECLAIM_NONE;
4483 else if (!irccasecmp(str, "warn"))
4484 return RECLAIM_WARN;
4485 else if (!irccasecmp(str, "svsnick"))
4486 return RECLAIM_SVSNICK;
4487 else if (!irccasecmp(str, "kill"))
4488 return RECLAIM_KILL;
4490 return RECLAIM_NONE;
4494 nickserv_conf_read(void)
4496 dict_t conf_node, child;
4500 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4501 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4504 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4506 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4507 if (nickserv_conf.valid_handle_regex_set)
4508 regfree(&nickserv_conf.valid_handle_regex);
4510 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4511 nickserv_conf.valid_handle_regex_set = !err;
4512 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4514 nickserv_conf.valid_handle_regex_set = 0;
4516 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4517 if (nickserv_conf.valid_nick_regex_set)
4518 regfree(&nickserv_conf.valid_nick_regex);
4520 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4521 nickserv_conf.valid_nick_regex_set = !err;
4522 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4524 nickserv_conf.valid_nick_regex_set = 0;
4526 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4528 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4529 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4530 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4531 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4532 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4533 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4534 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4535 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4536 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4537 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4538 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4539 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4540 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4541 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4542 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4543 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4544 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4545 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4546 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4547 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4548 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4549 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4550 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4551 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4552 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
4553 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
4554 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4556 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4557 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4558 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4560 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4561 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4562 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4564 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4565 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4566 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4567 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4568 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4569 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4570 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4571 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4572 str = database_get_data(conf_node, KEY_MAX_AUTHLOG_LEN, RECDB_QSTRING);
4573 nickserv_conf.max_authlog_len = str ? strtoul(str, NULL, 0) : 30;
4574 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4575 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4576 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4579 nickserv_conf.ounregister_flags = 0;
4581 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4584 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4586 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4588 nickserv_conf.handle_ts_mode = TS_IGNORE;
4589 else if (!irccasecmp(str, "ircu"))
4590 nickserv_conf.handle_ts_mode = TS_IRCU;
4592 nickserv_conf.handle_ts_mode = TS_IGNORE;
4593 if (!nickserv_conf.disable_nicks) {
4594 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4595 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4596 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4597 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4598 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4599 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4600 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4601 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4603 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4604 for (it=dict_first(child); it; it=iter_next(it)) {
4605 const char *key = iter_key(it), *value;
4609 if (!strncasecmp(key, "uc_", 3))
4610 flag = toupper(key[3]);
4611 else if (!strncasecmp(key, "lc_", 3))
4612 flag = tolower(key[3]);
4616 if ((pos = handle_inverse_flags[flag])) {
4617 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4618 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4621 if (nickserv_conf.weak_password_dict)
4622 dict_delete(nickserv_conf.weak_password_dict);
4623 nickserv_conf.weak_password_dict = dict_new();
4624 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4625 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4626 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4627 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4629 nickserv_load_dict(str);
4630 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4631 if (nickserv && str)
4632 NickChange(nickserv, str, 0);
4633 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4634 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4635 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4636 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4637 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4638 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4639 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4640 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4641 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4642 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4643 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4644 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4645 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4646 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4647 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4648 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4649 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4650 titlehost_suffix = str ? str : "example.net";
4651 str = conf_get_data("server/network", RECDB_QSTRING);
4652 nickserv_conf.network_name = str ? str : "some IRC network";
4653 if (!nickserv_conf.auth_policer_params) {
4654 nickserv_conf.auth_policer_params = policer_params_new();
4655 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4656 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4658 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4659 for (it=dict_first(child); it; it=iter_next(it))
4660 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4664 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4666 char newnick[NICKLEN+1];
4675 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4677 case RECLAIM_SVSNICK:
4679 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4680 } while (GetUserH(newnick));
4681 irc_svsnick(nickserv, user, newnick);
4684 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4685 DelUser(user, nickserv, 1, msg);
4691 nickserv_reclaim_p(void *data) {
4692 struct userNode *user = data;
4693 struct nick_info *ni = get_nick_info(user->nick);
4695 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4699 check_user_nick(struct userNode *user) {
4700 //check if this user is a pending LOC user
4701 if(pendingLOCUsers) {
4702 struct pendingLOCUser *pending, *next, *prev = NULL;
4703 for(pending = pendingLOCUsers; pending; pending = next) {
4704 next = pending->next;
4705 if(user->handle_info == pending->handle_info) {
4706 pending->authlog->user = user;
4707 free((char*) pending->authlog->hostmask);
4708 pending->authlog->hostmask = generate_hostmask(user, GENMASK_USENICK|GENMASK_STRICT_IDENT|GENMASK_NO_HIDING|GENMASK_STRICT_HOST);
4712 pendingLOCUsers = next;
4715 if(now - pending->time > 10) {
4719 pendingLOCUsers = next;
4724 struct nick_info *ni;
4725 user->modes &= ~FLAGS_REGNICK;
4726 if (!(ni = get_nick_info(user->nick)))
4728 if (user->handle_info == ni->owner) {
4729 user->modes |= FLAGS_REGNICK;
4733 if (nickserv_conf.warn_nick_owned)
4734 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4735 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4737 if (nickserv_conf.auto_reclaim_delay)
4738 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4740 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4744 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4746 struct handle_info *hi = NULL;
4749 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4750 if ((hi == NULL) && (serial != 0)) {
4752 inttobase64(id, serial, IDLEN);
4753 hi = dict_find(nickserv_id_dict, id, NULL);
4757 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4758 && (timestamp != hi->registered)) {
4761 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4764 set_user_handle_info(user, hi, 0);
4766 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4771 handle_nick_change(struct userNode *user, const char *old_nick)
4773 struct handle_info *hi;
4775 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4776 dict_remove(nickserv_allow_auth_dict, old_nick);
4777 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4779 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4780 check_user_nick(user);
4784 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), const char *why)
4786 if(user->handle_info) {
4787 //check if theres an open authlog entry
4788 struct authlogEntry *authlog;
4789 for(authlog = user->handle_info->authlog; authlog; authlog = authlog->next) {
4790 if(authlog->user == user) {
4791 authlog->user = NULL;
4792 authlog->logout_time = now;
4793 authlog->quit_reason = strdup(why);
4798 dict_remove(nickserv_allow_auth_dict, user->nick);
4799 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4800 set_user_handle_info(user, NULL, 0);
4803 static struct modcmd *
4804 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4806 if (min_level > 0) {
4808 sprintf(buf, "%u", min_level);
4809 if (must_be_qualified) {
4810 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4812 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4814 } else if (min_level == 0) {
4815 if (must_be_qualified) {
4816 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4818 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4821 if (must_be_qualified) {
4822 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4824 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4830 nickserv_db_cleanup(void)
4832 unreg_del_user_func(nickserv_remove_user);
4833 userList_clean(&curr_helpers);
4834 policer_params_delete(nickserv_conf.auth_policer_params);
4835 dict_delete(nickserv_handle_dict);
4836 dict_delete(nickserv_nick_dict);
4837 dict_delete(nickserv_opt_dict);
4838 dict_delete(nickserv_allow_auth_dict);
4839 dict_delete(nickserv_email_dict);
4840 dict_delete(nickserv_id_dict);
4841 dict_delete(nickserv_conf.weak_password_dict);
4842 free(auth_func_list);
4843 free(unreg_func_list);
4845 free(allowauth_func_list);
4846 free(handle_merge_func_list);
4847 free(failpw_func_list);
4848 if (nickserv_conf.valid_handle_regex_set)
4849 regfree(&nickserv_conf.valid_handle_regex);
4850 if (nickserv_conf.valid_nick_regex_set)
4851 regfree(&nickserv_conf.valid_nick_regex);
4852 struct pendingLOCUser *pending, *next;
4853 for(pending = pendingLOCUsers; pending; pending = next) {
4854 next = pending->next;
4857 pendingLOCUsers = NULL;
4861 init_nickserv(const char *nick)
4864 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4865 reg_new_user_func(check_user_nick);
4866 reg_nick_change_func(handle_nick_change);
4867 reg_del_user_func(nickserv_remove_user);
4868 reg_account_func(handle_account);
4870 /* set up handle_inverse_flags */
4871 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4872 for (i=0; handle_flags[i]; i++) {
4873 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4874 flag_access_levels[i] = 0;
4877 conf_register_reload(nickserv_conf_read);
4878 nickserv_opt_dict = dict_new();
4879 nickserv_email_dict = dict_new();
4880 dict_set_free_keys(nickserv_email_dict, free);
4881 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4883 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4884 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4885 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4886 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4887 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4888 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4889 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4890 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4891 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4892 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4893 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4894 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4895 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4896 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4897 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4898 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4899 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4900 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4901 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4902 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4903 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4904 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4905 if (!nickserv_conf.disable_nicks) {
4906 /* nick management commands */
4907 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4908 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4909 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4910 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4911 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4912 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4914 if (nickserv_conf.email_enabled) {
4915 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4916 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4917 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4918 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4919 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4920 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4922 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4923 /* miscellaneous commands */
4924 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4925 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4926 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4927 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4928 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4929 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4930 nickserv_define_func("AUTHLOG", cmd_authlog, -1, 1, 0);
4931 nickserv_define_func("OAUTHLOG", cmd_oauthlog, 0, 1, 0);
4933 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4934 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4935 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4936 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4937 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4938 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4939 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
4940 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4941 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4942 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4943 dict_insert(nickserv_opt_dict, "WEBSITE", opt_website);
4944 dict_insert(nickserv_opt_dict, "DEVNULL", opt_devnull);
4945 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4946 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4947 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4948 if (titlehost_suffix) {
4949 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4950 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4951 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4953 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4954 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4955 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4956 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4958 nickserv_handle_dict = dict_new();
4959 dict_set_free_keys(nickserv_handle_dict, free);
4960 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4962 nickserv_id_dict = dict_new();
4963 dict_set_free_keys(nickserv_id_dict, free);
4965 nickserv_nick_dict = dict_new();
4966 dict_set_free_data(nickserv_nick_dict, free);
4968 nickserv_allow_auth_dict = dict_new();
4970 userList_init(&curr_helpers);
4973 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4974 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4975 nickserv_service = service_register(nickserv);
4977 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4978 reg_exit_func(nickserv_db_cleanup);
4979 if(nickserv_conf.handle_expire_frequency)
4980 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4981 message_register_table(msgtab);