1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of x3.
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() */
34 #define NICKSERV_CONF_NAME "services/nickserv"
36 #define KEY_DISABLE_NICKS "disable_nicks"
37 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
38 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
39 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
40 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
41 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
42 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
43 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
44 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
45 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
46 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
47 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
48 #define KEY_MODOPER_LEVEL "modoper_level"
49 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
50 #define KEY_SET_TITLE_LEVEL "set_title_level"
51 #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
52 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
53 #define KEY_FLAG_LEVELS "flag_levels"
54 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
55 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
56 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
57 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
58 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
59 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
60 #define KEY_DICT_FILE "dict_file"
61 #define KEY_NICK "nick"
62 #define KEY_LANGUAGE "language"
63 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
64 #define KEY_AUTOGAG_DURATION "autogag_duration"
65 #define KEY_AUTH_POLICER "auth_policer"
66 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
67 #define KEY_EMAIL_ENABLED "email_enabled"
68 #define KEY_EMAIL_REQUIRED "email_required"
69 #define KEY_SYNC_LOG "sync_log"
70 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
71 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
72 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
75 #define KEY_PASSWD "passwd"
76 #define KEY_NICKS "nicks"
77 #define KEY_MASKS "masks"
78 #define KEY_OPSERV_LEVEL "opserv_level"
79 #define KEY_FLAGS "flags"
80 #define KEY_REGISTER_ON "register"
81 #define KEY_LAST_SEEN "lastseen"
82 #define KEY_INFO "info"
83 #define KEY_USERLIST_STYLE "user_style"
84 #define KEY_SCREEN_WIDTH "screen_width"
85 #define KEY_LAST_AUTHED_HOST "last_authed_host"
86 #define KEY_LAST_QUIT_HOST "last_quit_host"
87 #define KEY_EMAIL_ADDR "email_addr"
88 #define KEY_COOKIE "cookie"
89 #define KEY_COOKIE_DATA "data"
90 #define KEY_COOKIE_TYPE "type"
91 #define KEY_COOKIE_EXPIRES "expires"
92 #define KEY_ACTIVATION "activation"
93 #define KEY_PASSWORD_CHANGE "password change"
94 #define KEY_EMAIL_CHANGE "email change"
95 #define KEY_ALLOWAUTH "allowauth"
96 #define KEY_EPITHET "epithet"
97 #define KEY_TABLE_WIDTH "table_width"
98 #define KEY_ANNOUNCEMENTS "announcements"
99 #define KEY_MAXLOGINS "maxlogins"
100 #define KEY_FAKEHOST "fakehost"
102 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
104 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
105 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
106 typedef OPTION_FUNC(option_func_t
);
108 DEFINE_LIST(handle_info_list
, struct handle_info
*);
110 #define NICKSERV_MIN_PARMS(N) do { \
112 reply("MSG_MISSING_PARAMS", argv[0]); \
113 svccmd_send_help(user, nickserv, cmd); \
117 struct userNode
*nickserv
;
118 struct userList curr_helpers
;
119 const char *handle_flags
= HANDLE_FLAGS
;
121 static struct module *nickserv_module
;
122 static struct service
*nickserv_service
;
123 static struct log_type
*NS_LOG
;
124 static dict_t nickserv_handle_dict
; /* contains struct handle_info* */
125 static dict_t nickserv_id_dict
; /* contains struct handle_info* */
126 static dict_t nickserv_nick_dict
; /* contains struct nick_info* */
127 static dict_t nickserv_opt_dict
; /* contains option_func_t* */
128 static dict_t nickserv_allow_auth_dict
; /* contains struct handle_info* */
129 static dict_t nickserv_email_dict
; /* contains struct handle_info_list*, indexed by email addr */
130 static char handle_inverse_flags
[256];
131 static unsigned int flag_access_levels
[32];
132 static const struct message_entry msgtab
[] = {
133 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
134 { "NSMSG_HANDLE_TOLONG", "The account name %s is too long. Account names must be %lu charactors or less."},
135 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
136 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
137 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
138 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
139 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
140 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
141 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
142 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
143 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
144 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
145 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
146 { "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." },
147 { "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." },
148 { "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." },
149 { "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." },
150 { "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." },
151 { "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." },
152 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
153 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
154 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
155 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
156 { "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." },
157 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
158 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
159 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
160 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
161 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
162 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
163 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
164 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
165 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
166 { "NSMSG_ATE_FOREIGN_COOKIE", "I ate the cookie for account $b%s$b. It may now have another." },
167 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
168 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
169 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
170 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
171 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
172 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
173 { "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)" },
174 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
175 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
176 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
177 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
178 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
179 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
180 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
181 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
182 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
183 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
184 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
185 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
186 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
187 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
188 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
189 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
190 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
191 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
192 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
193 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
194 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
195 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
196 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
197 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
198 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
199 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
200 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
201 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
202 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
203 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
204 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
205 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
206 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
207 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
208 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
209 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
210 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
211 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
212 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
213 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
214 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
215 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
216 { "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)." },
217 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
218 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
219 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
220 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
221 { "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." },
222 { "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." },
223 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
224 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
225 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
226 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
227 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
228 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
229 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
230 { "NSMSG_PASS_SUCCESS", "Password changed." },
231 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
232 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
233 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
234 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
235 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
236 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
237 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
238 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
239 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
240 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
241 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
242 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
243 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
244 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
245 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
246 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
247 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
248 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
249 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
250 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
251 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
252 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
253 { "NSMSG_NO_ACCESS", "Access denied." },
254 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
255 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
256 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
257 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
258 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T
".%03lu seconds)." },
259 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
260 { "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." },
261 { "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." },
262 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
263 { "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." },
264 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
265 { "NSMSG_SEARCH_MATCH", "Match: %s" },
266 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
267 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
268 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
269 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
270 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
271 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
272 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
273 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
274 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
275 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
276 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
277 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
278 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
279 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
280 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
281 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
282 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
283 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
284 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
285 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
286 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
287 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
288 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
289 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
290 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
291 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
292 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
293 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
294 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
295 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
296 { "NSEMAIL_ACTIVATION_BODY",
297 "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"
299 "To verify your email address and complete the account registration, log on to %1$s and type the following command:\n"
300 "/msg %3$s@%4$s COOKIE %5$s %2$s\n"
301 "This 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"
302 "/msg %3$s@%4$s AUTH %5$s your-password\n"
303 "(Please remember to fill in 'your-password' with the actual password you gave to us when you registered.)\n"
304 "OR configure Login-On-Connect (see http://www.afternet.org/login-on-connect for instructions) to connect pre-logged in every time.\n"
306 "If you did NOT request this account, you do not need to do anything.\n"
307 "Please contact the %1$s staff if you have questions, and be sure to check our website." },
308 { "NSEMAIL_ACTIVATION_BODY_WEB",
309 "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"
311 "To verify your email address and complete the account registration, visit the following URL:\n"
312 "http://www.afternet.org/index.php?option=com_registration&task=activate&username=%5$s&cookie=%2$s\n"
314 "If you did NOT request this account, you do not need to do anything.\n"
315 "Please contact the %1$s staff if you have questions, and be sure to check our website." },
316 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
317 { "NSEMAIL_PASSWORD_CHANGE_BODY",
318 "This email has been sent to verify that you wish to change the password on your account %5$s. Your cookie is %2$s.\n"
319 "To complete the password change, log on to %1$s and type the following command:\n"
320 "/msg %3$s@%4$s COOKIE %5$s %2$s\n"
321 "If you did NOT request your password to be changed, you do not need to do anything.\n"
322 "Please contact the %1$s staff if you have questions." },
323 { "NSEMAIL_PASSWORD_CHANGE_BODY_WEB",
324 "This email has been sent to verify that you wish to change the password on your account %5$s. Your cookie is %2$s.\n"
325 "To complete the password change, click the following URL:\n"
326 "http://www.afternet.org/index.php?option=com_registration&task=passcookie&username=%5$s&cookie=%2$s\n"
327 "If you did NOT request your password to be changed, you do not need to do anything.\n"
328 "Please contact the %1$s staff if you have questions." },
329 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
330 #ifdef stupid_verify_old_email
331 { "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." },
332 { "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." },
334 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
335 { "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." },
336 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
337 { "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." },
338 { "CHECKPASS_YES", "Yes." },
339 { "CHECKPASS_NO", "No." },
343 enum reclaim_action
{
349 static void nickserv_reclaim(struct userNode
*user
, struct nick_info
*ni
, enum reclaim_action action
);
350 static void nickserv_reclaim_p(void *data
);
353 unsigned int disable_nicks
: 1;
354 unsigned int valid_handle_regex_set
: 1;
355 unsigned int valid_nick_regex_set
: 1;
356 unsigned int autogag_enabled
: 1;
357 unsigned int email_enabled
: 1;
358 unsigned int email_required
: 1;
359 unsigned int default_hostmask
: 1;
360 unsigned int warn_nick_owned
: 1;
361 unsigned int warn_clone_auth
: 1;
362 unsigned int sync_log
: 1;
363 unsigned long nicks_per_handle
;
364 unsigned long password_min_length
;
365 unsigned long password_min_digits
;
366 unsigned long password_min_upper
;
367 unsigned long password_min_lower
;
368 unsigned long db_backup_frequency
;
369 unsigned long handle_expire_frequency
;
370 unsigned long autogag_duration
;
371 unsigned long email_visible_level
;
372 unsigned long cookie_timeout
;
373 unsigned long handle_expire_delay
;
374 unsigned long nochan_handle_expire_delay
;
375 unsigned long modoper_level
;
376 unsigned long set_epithet_level
;
377 unsigned long set_title_level
;
378 unsigned long set_fakehost_level
;
379 unsigned long handles_per_email
;
380 unsigned long email_search_level
;
381 const char *network_name
;
382 const char *titlehost_suffix
;
383 regex_t valid_handle_regex
;
384 regex_t valid_nick_regex
;
385 dict_t weak_password_dict
;
386 struct policer_params
*auth_policer_params
;
387 enum reclaim_action reclaim_action
;
388 enum reclaim_action auto_reclaim_action
;
389 unsigned long auto_reclaim_delay
;
390 unsigned char default_maxlogins
;
391 unsigned char hard_maxlogins
;
394 /* We have 2^32 unique account IDs to use. */
395 unsigned long int highest_id
= 0;
398 canonicalize_hostmask(char *mask
)
400 char *out
= mask
, *temp
;
401 if ((temp
= strchr(mask
, '!'))) {
403 while (*temp
) *out
++ = *temp
++;
409 static struct handle_info
*
410 register_handle(const char *handle
, const char *passwd
, UNUSED_ARG(unsigned long id
))
412 struct handle_info
*hi
;
414 #ifdef WITH_PROTOCOL_BAHAMUT
415 char id_base64
[IDLEN
+ 1];
418 /* Assign a unique account ID to the account; note that 0 is
419 an invalid account ID. 1 is therefore the first account ID. */
421 id
= 1 + highest_id
++;
423 /* Note: highest_id is and must always be the highest ID. */
424 if(id
> highest_id
) {
428 inttobase64(id_base64
, id
, IDLEN
);
430 /* Make sure an account with the same ID doesn't exist. If a
431 duplicate is found, log some details and assign a new one.
432 This should be impossible, but it never hurts to expect it. */
433 if ((hi
= dict_find(nickserv_id_dict
, id_base64
, NULL
))) {
434 log_module(NS_LOG
, LOG_WARNING
, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id
, id_base64
, hi
->handle
, handle
);
440 hi
= calloc(1, sizeof(*hi
));
441 hi
->userlist_style
= HI_DEFAULT_STYLE
;
442 hi
->announcements
= '?';
443 hi
->handle
= strdup(handle
);
444 safestrncpy(hi
->passwd
, passwd
, sizeof(hi
->passwd
));
446 dict_insert(nickserv_handle_dict
, hi
->handle
, hi
);
448 #ifdef WITH_PROTOCOL_BAHAMUT
450 dict_insert(nickserv_id_dict
, strdup(id_base64
), hi
);
457 register_nick(const char *nick
, struct handle_info
*owner
)
459 struct nick_info
*ni
;
460 ni
= malloc(sizeof(struct nick_info
));
461 safestrncpy(ni
->nick
, nick
, sizeof(ni
->nick
));
463 ni
->next
= owner
->nicks
;
465 dict_insert(nickserv_nick_dict
, ni
->nick
, ni
);
469 delete_nick(struct nick_info
*ni
)
471 struct nick_info
*last
, *next
;
472 struct userNode
*user
;
473 /* Check to see if we should mark a user as unregistered. */
474 if ((user
= GetUserH(ni
->nick
)) && IsReggedNick(user
)) {
475 user
->modes
&= ~FLAGS_REGNICK
;
478 /* Remove ni from the nick_info linked list. */
479 if (ni
== ni
->owner
->nicks
) {
480 ni
->owner
->nicks
= ni
->next
;
482 last
= ni
->owner
->nicks
;
488 last
->next
= next
->next
;
490 dict_remove(nickserv_nick_dict
, ni
->nick
);
493 static unreg_func_t
*unreg_func_list
;
494 static unsigned int unreg_func_size
= 0, unreg_func_used
= 0;
497 reg_unreg_func(unreg_func_t func
)
499 if (unreg_func_used
== unreg_func_size
) {
500 if (unreg_func_size
) {
501 unreg_func_size
<<= 1;
502 unreg_func_list
= realloc(unreg_func_list
, unreg_func_size
*sizeof(unreg_func_t
));
505 unreg_func_list
= malloc(unreg_func_size
*sizeof(unreg_func_t
));
508 unreg_func_list
[unreg_func_used
++] = func
;
512 nickserv_free_cookie(void *data
)
514 struct handle_cookie
*cookie
= data
;
515 if (cookie
->hi
) cookie
->hi
->cookie
= NULL
;
516 if (cookie
->data
) free(cookie
->data
);
521 free_handle_info(void *vhi
)
523 struct handle_info
*hi
= vhi
;
525 #ifdef WITH_PROTOCOL_BAHAMUT
528 inttobase64(id
, hi
->id
, IDLEN
);
529 dict_remove(nickserv_id_dict
, id
);
532 free_string_list(hi
->masks
);
536 delete_nick(hi
->nicks
);
541 timeq_del(hi
->cookie
->expires
, nickserv_free_cookie
, hi
->cookie
, 0);
542 nickserv_free_cookie(hi
->cookie
);
544 if (hi
->email_addr
) {
545 struct handle_info_list
*hil
= dict_find(nickserv_email_dict
, hi
->email_addr
, NULL
);
546 handle_info_list_remove(hil
, hi
);
548 dict_remove(nickserv_email_dict
, hi
->email_addr
);
553 static void set_user_handle_info(struct userNode
*user
, struct handle_info
*hi
, int stamp
);
556 nickserv_unregister_handle(struct handle_info
*hi
, struct userNode
*notify
)
560 for (n
=0; n
<unreg_func_used
; n
++)
561 unreg_func_list
[n
](notify
, hi
);
563 set_user_handle_info(hi
->users
, NULL
, 0);
565 if (nickserv_conf
.disable_nicks
)
566 send_message(notify
, nickserv
, "NSMSG_UNREGISTER_SUCCESS", hi
->handle
);
568 send_message(notify
, nickserv
, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi
->handle
);
571 if (nickserv_conf
.sync_log
)
572 SyncLog("UNREGISTER %s", hi
->handle
);
574 dict_remove(nickserv_handle_dict
, hi
->handle
);
578 get_handle_info(const char *handle
)
580 return dict_find(nickserv_handle_dict
, handle
, 0);
584 get_nick_info(const char *nick
)
586 return nickserv_conf
.disable_nicks
? 0 : dict_find(nickserv_nick_dict
, nick
, 0);
590 find_handle_in_channel(struct chanNode
*channel
, struct handle_info
*handle
, struct userNode
*except
)
595 for (nn
=0; nn
<channel
->members
.used
; ++nn
) {
596 mn
= channel
->members
.list
[nn
];
597 if ((mn
->user
!= except
) && (mn
->user
->handle_info
== handle
))
604 oper_has_access(struct userNode
*user
, struct userNode
*bot
, unsigned int min_level
, unsigned int quiet
) {
605 if (!user
->handle_info
) {
607 send_message(user
, bot
, "MSG_AUTHENTICATE");
611 if (!IsOper(user
) && (!IsHelping(user
) || min_level
)) {
613 send_message(user
, bot
, "NSMSG_NO_ACCESS");
617 if (HANDLE_FLAGGED(user
->handle_info
, OPER_SUSPENDED
)) {
619 send_message(user
, bot
, "MSG_OPER_SUSPENDED");
623 if (user
->handle_info
->opserv_level
< min_level
) {
625 send_message(user
, bot
, "NSMSG_NO_ACCESS");
633 is_valid_handle(const char *handle
)
635 struct userNode
*user
;
636 /* cant register a juped nick/service nick as handle, to prevent confusion */
637 user
= GetUserH(handle
);
638 if (user
&& IsLocal(user
))
640 /* check against maximum length */
641 if (strlen(handle
) > NICKSERV_HANDLE_LEN
)
643 /* for consistency, only allow account names that could be nicks */
644 if (!is_valid_nick(handle
))
646 /* disallow account names that look like bad words */
647 if (opserv_bad_channel(handle
))
649 /* test either regex or containing all valid chars */
650 if (nickserv_conf
.valid_handle_regex_set
) {
651 int err
= regexec(&nickserv_conf
.valid_handle_regex
, handle
, 0, 0, 0);
654 buff
[regerror(err
, &nickserv_conf
.valid_handle_regex
, buff
, sizeof(buff
))] = 0;
655 log_module(NS_LOG
, LOG_INFO
, "regexec error: %s (%d)", buff
, err
);
659 return !handle
[strspn(handle
, NICKSERV_VALID_CHARS
)];
664 is_registerable_nick(const char *nick
)
666 /* make sure it could be used as an account name */
667 if (!is_valid_handle(nick
))
670 if (strlen(nick
) > NICKLEN
)
672 /* test either regex or as valid handle */
673 if (nickserv_conf
.valid_nick_regex_set
) {
674 int err
= regexec(&nickserv_conf
.valid_nick_regex
, nick
, 0, 0, 0);
677 buff
[regerror(err
, &nickserv_conf
.valid_nick_regex
, buff
, sizeof(buff
))] = 0;
678 log_module(NS_LOG
, LOG_INFO
, "regexec error: %s (%d)", buff
, err
);
686 is_valid_email_addr(const char *email
)
688 return strchr(email
, '@') != NULL
;
692 visible_email_addr(struct userNode
*user
, struct handle_info
*hi
)
694 if (hi
->email_addr
) {
695 if (oper_has_access(user
, nickserv
, nickserv_conf
.email_visible_level
, 1)) {
696 return hi
->email_addr
;
706 smart_get_handle_info(struct userNode
*service
, struct userNode
*user
, const char *name
)
708 struct handle_info
*hi
;
709 struct userNode
*target
;
713 if (!(hi
= get_handle_info(++name
))) {
714 send_message(user
, service
, "MSG_HANDLE_UNKNOWN", name
);
719 if (!(target
= GetUserH(name
))) {
720 send_message(user
, service
, "MSG_NICK_UNKNOWN", name
);
723 if (IsLocal(target
)) {
724 if (IsService(target
))
725 send_message(user
, service
, "NSMSG_USER_IS_SERVICE", target
->nick
);
727 send_message(user
, service
, "MSG_USER_AUTHENTICATE", target
->nick
);
730 if (!(hi
= target
->handle_info
)) {
731 send_message(user
, service
, "MSG_USER_AUTHENTICATE", target
->nick
);
739 oper_outranks(struct userNode
*user
, struct handle_info
*hi
) {
740 if (user
->handle_info
->opserv_level
> hi
->opserv_level
)
742 if (user
->handle_info
->opserv_level
== hi
->opserv_level
) {
743 if ((user
->handle_info
->opserv_level
== 1000)
744 || (user
->handle_info
== hi
)
745 || ((user
->handle_info
->opserv_level
== 0)
746 && !(HANDLE_FLAGGED(hi
, SUPPORT_HELPER
) || HANDLE_FLAGGED(hi
, NETWORK_HELPER
))
747 && HANDLE_FLAGGED(user
->handle_info
, HELPING
))) {
751 send_message(user
, nickserv
, "MSG_USER_OUTRANKED", hi
->handle
);
755 static struct handle_info
*
756 get_victim_oper(struct userNode
*user
, const char *target
)
758 struct handle_info
*hi
;
759 if (!(hi
= smart_get_handle_info(nickserv
, user
, target
)))
761 if (HANDLE_FLAGGED(user
->handle_info
, OPER_SUSPENDED
)) {
762 send_message(user
, nickserv
, "MSG_OPER_SUSPENDED");
765 return oper_outranks(user
, hi
) ? hi
: NULL
;
769 valid_user_for(struct userNode
*user
, struct handle_info
*hi
)
773 /* If no hostmasks on the account, allow it. */
774 if (!hi
->masks
->used
)
776 /* If any hostmask matches, allow it. */
777 for (ii
=0; ii
<hi
->masks
->used
; ii
++)
778 if (user_matches_glob(user
, hi
->masks
->list
[ii
], 0))
780 /* If they are allowauthed to this account, allow it (removing the aa). */
781 if (dict_find(nickserv_allow_auth_dict
, user
->nick
, NULL
) == hi
) {
782 dict_remove(nickserv_allow_auth_dict
, user
->nick
);
785 /* The user is not allowed to use this account. */
790 is_secure_password(const char *handle
, const char *pass
, struct userNode
*user
)
793 unsigned int cnt_digits
= 0, cnt_upper
= 0, cnt_lower
= 0;
795 if (len
< nickserv_conf
.password_min_length
) {
797 send_message(user
, nickserv
, "NSMSG_PASSWORD_SHORT", nickserv_conf
.password_min_length
);
800 if (!irccasecmp(pass
, handle
)) {
802 send_message(user
, nickserv
, "NSMSG_PASSWORD_ACCOUNT");
805 dict_find(nickserv_conf
.weak_password_dict
, pass
, &i
);
808 send_message(user
, nickserv
, "NSMSG_PASSWORD_DICTIONARY");
811 for (i
=0; i
<len
; i
++) {
812 if (isdigit(pass
[i
]))
814 if (isupper(pass
[i
]))
816 if (islower(pass
[i
]))
819 if ((cnt_lower
< nickserv_conf
.password_min_lower
)
820 || (cnt_upper
< nickserv_conf
.password_min_upper
)
821 || (cnt_digits
< nickserv_conf
.password_min_digits
)) {
823 send_message(user
, nickserv
, "NSMSG_PASSWORD_READABLE", nickserv_conf
.password_min_digits
, nickserv_conf
.password_min_upper
, nickserv_conf
.password_min_lower
);
829 static auth_func_t
*auth_func_list
;
830 static unsigned int auth_func_size
= 0, auth_func_used
= 0;
833 reg_auth_func(auth_func_t func
)
835 if (auth_func_used
== auth_func_size
) {
836 if (auth_func_size
) {
837 auth_func_size
<<= 1;
838 auth_func_list
= realloc(auth_func_list
, auth_func_size
*sizeof(auth_func_t
));
841 auth_func_list
= malloc(auth_func_size
*sizeof(auth_func_t
));
844 auth_func_list
[auth_func_used
++] = func
;
847 static handle_rename_func_t
*rf_list
;
848 static unsigned int rf_list_size
, rf_list_used
;
851 reg_handle_rename_func(handle_rename_func_t func
)
853 if (rf_list_used
== rf_list_size
) {
856 rf_list
= realloc(rf_list
, rf_list_size
*sizeof(rf_list
[0]));
859 rf_list
= malloc(rf_list_size
*sizeof(rf_list
[0]));
862 rf_list
[rf_list_used
++] = func
;
866 generate_fakehost(struct handle_info
*handle
)
868 extern const char *hidden_host_suffix
;
869 static char buffer
[HOSTLEN
+1];
871 if (!handle
->fakehost
) {
872 snprintf(buffer
, sizeof(buffer
), "%s.%s", handle
->handle
, hidden_host_suffix
);
874 } else if (handle
->fakehost
[0] == '.') {
875 /* A leading dot indicates the stored value is actually a title. */
876 snprintf(buffer
, sizeof(buffer
), "%s.%s.%s", handle
->handle
, handle
->fakehost
+1, nickserv_conf
.titlehost_suffix
);
879 return handle
->fakehost
;
883 apply_fakehost(struct handle_info
*handle
)
885 struct userNode
*target
;
890 fake
= generate_fakehost(handle
);
891 for (target
= handle
->users
; target
; target
= target
->next_authed
)
892 assign_fakehost(target
, fake
, 1);
896 set_user_handle_info(struct userNode
*user
, struct handle_info
*hi
, int stamp
)
899 struct handle_info
*old_info
;
901 /* This can happen if somebody uses COOKIE while authed, or if
902 * they re-auth to their current handle (which is silly, but users
904 if (user
->handle_info
== hi
)
907 if (user
->handle_info
) {
908 struct userNode
*other
;
911 userList_remove(&curr_helpers
, user
);
913 /* remove from next_authed linked list */
914 if (user
->handle_info
->users
== user
) {
915 user
->handle_info
->users
= user
->next_authed
;
917 for (other
= user
->handle_info
->users
;
918 other
->next_authed
!= user
;
919 other
= other
->next_authed
) ;
920 other
->next_authed
= user
->next_authed
;
922 /* if nobody left on old handle, and they're not an oper, remove !god */
923 if (!user
->handle_info
->users
&& !user
->handle_info
->opserv_level
)
924 HANDLE_CLEAR_FLAG(user
->handle_info
, HELPING
);
925 /* record them as being last seen at this time */
926 user
->handle_info
->lastseen
= now
;
927 /* and record their hostmask */
928 snprintf(user
->handle_info
->last_quit_host
, sizeof(user
->handle_info
->last_quit_host
), "%s@%s", user
->ident
, user
->hostname
);
930 old_info
= user
->handle_info
;
931 user
->handle_info
= hi
;
932 if (hi
&& !hi
->users
&& !hi
->opserv_level
)
933 HANDLE_CLEAR_FLAG(hi
, HELPING
);
934 for (n
=0; n
<auth_func_used
; n
++)
935 auth_func_list
[n
](user
, old_info
);
937 struct nick_info
*ni
;
939 HANDLE_CLEAR_FLAG(hi
, FROZEN
);
940 if (nickserv_conf
.warn_clone_auth
) {
941 struct userNode
*other
;
942 for (other
= hi
->users
; other
; other
= other
->next_authed
)
943 send_message(other
, nickserv
, "NSMSG_CLONE_AUTH", user
->nick
, user
->ident
, user
->hostname
);
945 user
->next_authed
= hi
->users
;
949 userList_append(&curr_helpers
, user
);
951 if (hi
->fakehost
|| old_info
)
955 #ifdef WITH_PROTOCOL_BAHAMUT
956 /* Stamp users with their account ID. */
958 inttobase64(id
, hi
->id
, IDLEN
);
959 #elif WITH_PROTOCOL_P10
960 /* Stamp users with their account name. */
961 char *id
= hi
->handle
;
963 const char *id
= "???";
965 if (!nickserv_conf
.disable_nicks
) {
966 struct nick_info
*ni
;
967 for (ni
= hi
->nicks
; ni
; ni
= ni
->next
) {
968 if (!irccasecmp(user
->nick
, ni
->nick
)) {
969 user
->modes
|= FLAGS_REGNICK
;
974 StampUser(user
, id
, hi
->registered
);
977 if ((ni
= get_nick_info(user
->nick
)) && (ni
->owner
== hi
))
978 timeq_del(0, nickserv_reclaim_p
, user
, TIMEQ_IGNORE_WHEN
);
980 /* We cannot clear the user's account ID, unfortunately. */
981 user
->next_authed
= NULL
;
985 static struct handle_info
*
986 nickserv_register(struct userNode
*user
, struct userNode
*settee
, const char *handle
, const char *passwd
, int no_auth
)
988 struct handle_info
*hi
;
989 struct nick_info
*ni
;
990 char crypted
[MD5_CRYPT_LENGTH
];
992 if ((hi
= dict_find(nickserv_handle_dict
, handle
, NULL
))) {
993 send_message(user
, nickserv
, "NSMSG_HANDLE_EXISTS", handle
);
997 if(strlen(handle
) > 15)
999 send_message(user
, nickserv
, "NSMSG_HANDLE_TOLONG", handle
, 15);
1003 if (!is_secure_password(handle
, passwd
, user
))
1006 cryptpass(passwd
, crypted
);
1007 hi
= register_handle(handle
, crypted
, 0);
1008 hi
->masks
= alloc_string_list(1);
1010 hi
->language
= lang_C
;
1011 hi
->registered
= now
;
1013 hi
->flags
= HI_DEFAULT_FLAGS
;
1014 if (settee
&& !no_auth
)
1015 set_user_handle_info(settee
, hi
, 1);
1018 send_message(user
, nickserv
, "NSMSG_OREGISTER_H_SUCCESS");
1019 else if (nickserv_conf
.disable_nicks
)
1020 send_message(user
, nickserv
, "NSMSG_REGISTER_H_SUCCESS");
1021 else if ((ni
= dict_find(nickserv_nick_dict
, user
->nick
, NULL
)))
1022 send_message(user
, nickserv
, "NSMSG_PARTIAL_REGISTER");
1024 register_nick(user
->nick
, hi
);
1025 send_message(user
, nickserv
, "NSMSG_REGISTER_HN_SUCCESS");
1027 if (settee
&& (user
!= settee
))
1028 send_message(settee
, nickserv
, "NSMSG_OREGISTER_VICTIM", user
->nick
, hi
->handle
);
1033 nickserv_bake_cookie(struct handle_cookie
*cookie
)
1035 cookie
->hi
->cookie
= cookie
;
1036 timeq_add(cookie
->expires
, nickserv_free_cookie
, cookie
);
1039 /* Contributed by the great sneep of afternet ;) */
1040 /* Since this gets used in a URL, we want to avoid stuff that confuses
1041 * email clients such as ] and ?. a-z, 0-9 only.
1043 void genpass(char *str
, int len
)
1048 for(i
= 0; i
< len
; i
++)
1052 c
= (char)((float)rand() / (float)RAND_MAX
* (float)256);
1053 } while(!((c
>= 'a' && c
<= 'z') || (c
>= 'A' && c
<= 'Z') || (c
>= '0' && c
<= '9')));
1061 nickserv_make_cookie(struct userNode
*user
, struct handle_info
*hi
, enum cookie_type type
, const char *cookie_data
, int weblink
)
1063 struct handle_cookie
*cookie
;
1064 char subject
[128], body
[4096], *misc
;
1065 const char *netname
, *fmt
;
1069 send_message(user
, nickserv
, "NSMSG_COOKIE_LIVE", hi
->handle
);
1073 cookie
= calloc(1, sizeof(*cookie
));
1075 cookie
->type
= type
;
1076 cookie
->data
= cookie_data
? strdup(cookie_data
) : NULL
;
1078 cookie
->expires
= now
+ nickserv_conf
.cookie_timeout
;
1079 /* Adding dedicated password gen function for more control -Rubin */
1080 genpass(cookie
->cookie
, 10);
1082 *inttobase64(cookie->cookie, rand(), 5);
1083 *inttobase64(cookie->cookie+5, rand(), 5);
1086 netname
= nickserv_conf
.network_name
;
1089 switch (cookie
->type
) {
1091 hi
->passwd
[0] = 0; /* invalidate password */
1092 send_message(user
, nickserv
, "NSMSG_USE_COOKIE_REGISTER");
1093 fmt
= handle_find_message(hi
, "NSEMAIL_ACTIVATION_SUBJECT");
1094 snprintf(subject
, sizeof(subject
), fmt
, netname
);
1097 fmt
= handle_find_message(hi
, "NSEMAIL_ACTIVATION_BODY_WEB");
1099 fmt
= handle_find_message(hi
, "NSEMAIL_ACTIVATION_BODY");
1101 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
, nickserv
->nick
, self
->name
, hi
->handle
);
1104 case PASSWORD_CHANGE
:
1105 send_message(user
, nickserv
, "NSMSG_USE_COOKIE_RESETPASS");
1106 fmt
= handle_find_message(hi
, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1107 snprintf(subject
, sizeof(subject
), fmt
, netname
);
1109 fmt
= handle_find_message(hi
, "NSEMAIL_PASSWORD_CHANGE_BODY_WEB");
1111 fmt
= handle_find_message(hi
, "NSEMAIL_PASSWORD_CHANGE_BODY");
1112 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
, nickserv
->nick
, self
->name
, hi
->handle
);
1115 misc
= hi
->email_addr
;
1116 hi
->email_addr
= cookie
->data
;
1117 #ifdef stupid_verify_old_email
1119 send_message(user
, nickserv
, "NSMSG_USE_COOKIE_EMAIL_2");
1120 fmt
= handle_find_message(hi
, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1121 snprintf(subject
, sizeof(subject
), fmt
, netname
);
1122 fmt
= handle_find_message(hi
, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1123 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
+COOKIELEN
/2, nickserv
->nick
, self
->name
, hi
->handle
, COOKIELEN
/2);
1124 sendmail(nickserv
, hi
, subject
, body
, 1);
1125 fmt
= handle_find_message(hi
, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1126 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
, nickserv
->nick
, self
->name
, hi
->handle
, COOKIELEN
/2, hi
->email_addr
);
1129 send_message(user
, nickserv
, "NSMSG_USE_COOKIE_EMAIL_1");
1130 fmt
= handle_find_message(hi
, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1131 snprintf(subject
, sizeof(subject
), fmt
, netname
);
1132 fmt
= handle_find_message(hi
, "NSEMAIL_EMAIL_VERIFY_BODY");
1133 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
, nickserv
->nick
, self
->name
, hi
->handle
);
1134 sendmail(nickserv
, hi
, subject
, body
, 1);
1136 #ifdef stupid_verify_old_email
1139 hi
->email_addr
= misc
;
1142 fmt
= handle_find_message(hi
, "NSEMAIL_ALLOWAUTH_SUBJECT");
1143 snprintf(subject
, sizeof(subject
), fmt
, netname
);
1144 fmt
= handle_find_message(hi
, "NSEMAIL_ALLOWAUTH_BODY");
1145 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
, nickserv
->nick
, self
->name
, hi
->handle
);
1146 send_message(user
, nickserv
, "NSMSG_USE_COOKIE_AUTH");
1149 log_module(NS_LOG
, LOG_ERROR
, "Bad cookie type %d in nickserv_make_cookie.", cookie
->type
);
1153 sendmail(nickserv
, hi
, subject
, body
, first_time
);
1154 nickserv_bake_cookie(cookie
);
1158 nickserv_eat_cookie(struct handle_cookie
*cookie
)
1160 cookie
->hi
->cookie
= NULL
;
1161 timeq_del(cookie
->expires
, nickserv_free_cookie
, cookie
, 0);
1162 nickserv_free_cookie(cookie
);
1166 nickserv_free_email_addr(void *data
)
1168 handle_info_list_clean(data
);
1173 nickserv_set_email_addr(struct handle_info
*hi
, const char *new_email_addr
)
1175 struct handle_info_list
*hil
;
1176 /* Remove from old handle_info_list ... */
1177 if (hi
->email_addr
&& (hil
= dict_find(nickserv_email_dict
, hi
->email_addr
, 0))) {
1178 handle_info_list_remove(hil
, hi
);
1179 if (!hil
->used
) dict_remove(nickserv_email_dict
, hil
->tag
);
1180 hi
->email_addr
= NULL
;
1182 /* Add to the new list.. */
1183 if (new_email_addr
) {
1184 if (!(hil
= dict_find(nickserv_email_dict
, new_email_addr
, 0))) {
1185 hil
= calloc(1, sizeof(*hil
));
1186 hil
->tag
= strdup(new_email_addr
);
1187 handle_info_list_init(hil
);
1188 dict_insert(nickserv_email_dict
, hil
->tag
, hil
);
1190 handle_info_list_append(hil
, hi
);
1191 hi
->email_addr
= hil
->tag
;
1195 static NICKSERV_FUNC(cmd_register
)
1197 struct handle_info
*hi
;
1198 const char *email_addr
, *password
;
1199 char syncpass
[MD5_CRYPT_LENGTH
];
1200 int no_auth
, weblink
;
1202 if (!IsOper(user
) && !dict_size(nickserv_handle_dict
)) {
1203 /* Require the first handle registered to belong to someone +o. */
1204 reply("NSMSG_REQUIRE_OPER");
1208 if (user
->handle_info
) {
1209 reply("NSMSG_USE_RENAME", user
->handle_info
->handle
);
1213 if (IsRegistering(user
)) {
1214 reply("NSMSG_ALREADY_REGISTERING");
1218 if (IsStamped(user
)) {
1219 /* Unauthenticated users might still have been stamped
1220 previously and could therefore have a hidden host;
1221 do not allow them to register a new account. */
1222 reply("NSMSG_STAMPED_REGISTER");
1226 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf
.email_required
);
1228 if (!is_valid_handle(argv
[1])) {
1229 reply("NSMSG_BAD_HANDLE", argv
[1]);
1234 if ((argc
>= 4) && nickserv_conf
.email_enabled
) {
1235 struct handle_info_list
*hil
;
1238 /* Remember email address. */
1239 email_addr
= argv
[3];
1241 /* Check that the email address looks valid.. */
1242 if (!is_valid_email_addr(email_addr
)) {
1243 reply("NSMSG_BAD_EMAIL_ADDR");
1247 /* .. and that we are allowed to send to it. */
1248 if ((str
= sendmail_prohibited_address(email_addr
))) {
1249 reply("NSMSG_EMAIL_PROHIBITED", email_addr
, str
);
1253 /* If we do email verify, make sure we don't spam the address. */
1254 if ((hil
= dict_find(nickserv_email_dict
, email_addr
, NULL
))) {
1256 for (nn
=0; nn
<hil
->used
; nn
++) {
1257 if (hil
->list
[nn
]->cookie
) {
1258 reply("NSMSG_EMAIL_UNACTIVATED");
1262 if (hil
->used
>= nickserv_conf
.handles_per_email
) {
1263 reply("NSMSG_EMAIL_OVERUSED");
1276 /* Webregister hack - send URL instead of IRC cookie
1279 if((argc
>= 5) && !strcmp(argv
[4],"WEBLINK"))
1283 if (!(hi
= nickserv_register(user
, user
, argv
[1], password
, no_auth
)))
1285 /* Add any masks they should get. */
1286 if (nickserv_conf
.default_hostmask
) {
1287 string_list_append(hi
->masks
, strdup("*@*"));
1289 string_list_append(hi
->masks
, generate_hostmask(user
, GENMASK_OMITNICK
|GENMASK_NO_HIDING
|GENMASK_ANY_IDENT
));
1290 if (user
->ip
.s_addr
&& user
->hostname
[strspn(user
->hostname
, "0123456789.")])
1291 string_list_append(hi
->masks
, generate_hostmask(user
, GENMASK_OMITNICK
|GENMASK_BYIP
|GENMASK_NO_HIDING
|GENMASK_ANY_IDENT
));
1294 /* If they're the first to register, give them level 1000. */
1295 if (dict_size(nickserv_handle_dict
) == 1) {
1296 hi
->opserv_level
= 1000;
1297 reply("NSMSG_ROOT_HANDLE", argv
[1]);
1300 /* Set their email address. */
1302 nickserv_set_email_addr(hi
, email_addr
);
1304 /* If they need to do email verification, tell them. */
1306 nickserv_make_cookie(user
, hi
, ACTIVATION
, hi
->passwd
, weblink
);
1308 /* Set registering flag.. */
1309 user
->modes
|= FLAGS_REGISTERING
;
1311 if (nickserv_conf
.sync_log
) {
1312 cryptpass(password
, syncpass
);
1314 * An 0 is only sent if theres no email address. Thios should only happen if email functions are
1315 * disabled which they wont be for us. Email Required MUST be set on if you are using this.
1318 SyncLog("REGISTER %s %s %s %s", hi
->handle
, syncpass
, email_addr
? email_addr
: "0", user
->info
);
1324 static NICKSERV_FUNC(cmd_oregister
)
1327 struct userNode
*settee
;
1328 struct handle_info
*hi
;
1330 NICKSERV_MIN_PARMS(4);
1332 if (!is_valid_handle(argv
[1])) {
1333 reply("NSMSG_BAD_HANDLE", argv
[1]);
1337 if (strchr(argv
[3], '@')) {
1338 mask
= canonicalize_hostmask(strdup(argv
[3]));
1340 settee
= GetUserH(argv
[4]);
1342 reply("MSG_NICK_UNKNOWN", argv
[4]);
1349 } else if ((settee
= GetUserH(argv
[3]))) {
1350 mask
= generate_hostmask(settee
, GENMASK_OMITNICK
|GENMASK_NO_HIDING
|GENMASK_ANY_IDENT
);
1352 reply("NSMSG_REGISTER_BAD_NICKMASK", argv
[3]);
1355 if (settee
&& settee
->handle_info
) {
1356 reply("NSMSG_USER_PREV_AUTH", settee
->nick
);
1360 if (!(hi
= nickserv_register(user
, settee
, argv
[1], argv
[2], 0))) {
1364 string_list_append(hi
->masks
, mask
);
1368 static NICKSERV_FUNC(cmd_handleinfo
)
1371 unsigned int i
, pos
=0, herelen
;
1372 struct userNode
*target
, *next_un
;
1373 struct handle_info
*hi
;
1374 const char *nsmsg_none
;
1377 if (!(hi
= user
->handle_info
)) {
1378 reply("NSMSG_MUST_AUTH");
1381 } else if (!(hi
= modcmd_get_handle_info(user
, argv
[1]))) {
1385 nsmsg_none
= handle_find_message(hi
, "MSG_NONE");
1386 reply("NSMSG_HANDLEINFO_ON", hi
->handle
);
1387 #ifdef WITH_PROTOCOL_BAHAMUT
1388 reply("NSMSG_HANDLEINFO_ID", hi
->id
);
1390 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi
->registered
));
1393 intervalString(buff
, now
- hi
->lastseen
, user
->handle_info
);
1394 reply("NSMSG_HANDLEINFO_LASTSEEN", buff
);
1396 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1399 reply("NSMSG_HANDLEINFO_INFOLINE", (hi
->infoline
? hi
->infoline
: nsmsg_none
));
1400 if (HANDLE_FLAGGED(hi
, FROZEN
))
1401 reply("NSMSG_HANDLEINFO_VACATION");
1403 if (oper_has_access(user
, cmd
->parent
->bot
, 0, 1)) {
1404 struct do_not_register
*dnr
;
1405 if ((dnr
= chanserv_is_dnr(NULL
, hi
)))
1406 reply("NSMSG_HANDLEINFO_DNR", dnr
->setter
, dnr
->reason
);
1407 if (!oper_outranks(user
, hi
))
1409 } else if (hi
!= user
->handle_info
)
1412 if (nickserv_conf
.email_enabled
)
1413 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user
, hi
));
1417 switch (hi
->cookie
->type
) {
1418 case ACTIVATION
: type
= "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1419 case PASSWORD_CHANGE
: type
= "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1420 case EMAIL_CHANGE
: type
= "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1421 case ALLOWAUTH
: type
= "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1422 default: type
= "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1428 unsigned long flen
= 1;
1429 char flags
[34]; /* 32 bits possible plus '+' and '\0' */
1431 for (i
=0, flen
=1; handle_flags
[i
]; i
++)
1432 if (hi
->flags
& 1 << i
)
1433 flags
[flen
++] = handle_flags
[i
];
1435 reply("NSMSG_HANDLEINFO_FLAGS", flags
);
1437 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none
);
1440 if (HANDLE_FLAGGED(hi
, SUPPORT_HELPER
)
1441 || HANDLE_FLAGGED(hi
, NETWORK_HELPER
)
1442 || (hi
->opserv_level
> 0)) {
1443 reply("NSMSG_HANDLEINFO_EPITHET", (hi
->epithet
? hi
->epithet
: nsmsg_none
));
1447 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi
->fakehost
? hi
->fakehost
: handle_find_message(hi
, "MSG_NONE")));
1449 if (hi
->last_quit_host
[0])
1450 reply("NSMSG_HANDLEINFO_LAST_HOST", hi
->last_quit_host
);
1452 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1454 if (nickserv_conf
.disable_nicks
) {
1455 /* nicks disabled; don't show anything about registered nicks */
1456 } else if (hi
->nicks
) {
1457 struct nick_info
*ni
, *next_ni
;
1458 for (ni
= hi
->nicks
; ni
; ni
= next_ni
) {
1459 herelen
= strlen(ni
->nick
);
1460 if (pos
+ herelen
+ 1 > ArrayLength(buff
)) {
1462 goto print_nicks_buff
;
1466 memcpy(buff
+pos
, ni
->nick
, herelen
);
1467 pos
+= herelen
; buff
[pos
++] = ' ';
1471 reply("NSMSG_HANDLEINFO_NICKS", buff
);
1476 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none
);
1479 if (hi
->masks
->used
) {
1480 for (i
=0; i
< hi
->masks
->used
; i
++) {
1481 herelen
= strlen(hi
->masks
->list
[i
]);
1482 if (pos
+ herelen
+ 1 > ArrayLength(buff
)) {
1484 goto print_mask_buff
;
1486 memcpy(buff
+pos
, hi
->masks
->list
[i
], herelen
);
1487 pos
+= herelen
; buff
[pos
++] = ' ';
1488 if (i
+1 == hi
->masks
->used
) {
1491 reply("NSMSG_HANDLEINFO_MASKS", buff
);
1496 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none
);
1500 struct userData
*channel
, *next
;
1503 for (channel
= hi
->channels
; channel
; channel
= next
) {
1504 next
= channel
->u_next
;
1505 name
= channel
->channel
->channel
->name
;
1506 herelen
= strlen(name
);
1507 if (pos
+ herelen
+ 7 > ArrayLength(buff
)) {
1509 goto print_chans_buff
;
1511 if (IsUserSuspended(channel
))
1513 pos
+= sprintf(buff
+pos
, "%d:%s ", channel
->access
, name
);
1517 reply("NSMSG_HANDLEINFO_CHANNELS", buff
);
1522 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none
);
1525 for (target
= hi
->users
; target
; target
= next_un
) {
1526 herelen
= strlen(target
->nick
);
1527 if (pos
+ herelen
+ 1 > ArrayLength(buff
)) {
1529 goto print_cnick_buff
;
1531 next_un
= target
->next_authed
;
1533 memcpy(buff
+pos
, target
->nick
, herelen
);
1534 pos
+= herelen
; buff
[pos
++] = ' ';
1538 reply("NSMSG_HANDLEINFO_CURRENT", buff
);
1546 static NICKSERV_FUNC(cmd_userinfo
)
1548 struct userNode
*target
;
1550 NICKSERV_MIN_PARMS(2);
1551 if (!(target
= GetUserH(argv
[1]))) {
1552 reply("MSG_NICK_UNKNOWN", argv
[1]);
1555 if (target
->handle_info
)
1556 reply("NSMSG_USERINFO_AUTHED_AS", target
->nick
, target
->handle_info
->handle
);
1558 reply("NSMSG_USERINFO_NOT_AUTHED", target
->nick
);
1562 static NICKSERV_FUNC(cmd_nickinfo
)
1564 struct nick_info
*ni
;
1566 NICKSERV_MIN_PARMS(2);
1567 if (!(ni
= get_nick_info(argv
[1]))) {
1568 reply("MSG_NICK_UNKNOWN", argv
[1]);
1571 reply("NSMSG_NICKINFO_OWNER", ni
->nick
, ni
->owner
->handle
);
1575 static NICKSERV_FUNC(cmd_rename_handle
)
1577 struct handle_info
*hi
;
1578 char msgbuf
[MAXLEN
], *old_handle
;
1581 NICKSERV_MIN_PARMS(3);
1582 if (!(hi
= get_victim_oper(user
, argv
[1])))
1584 if (!is_valid_handle(argv
[2])) {
1585 reply("NSMSG_FAIL_RENAME", argv
[1], argv
[2]);
1588 if (get_handle_info(argv
[2])) {
1589 reply("NSMSG_HANDLE_EXISTS", argv
[2]);
1592 if(strlen(argv
[2]) > 15)
1594 reply("NMSG_HANDLE_TOLONG", argv
[2], 15);
1598 dict_remove2(nickserv_handle_dict
, old_handle
= hi
->handle
, 1);
1599 hi
->handle
= strdup(argv
[2]);
1600 dict_insert(nickserv_handle_dict
, hi
->handle
, hi
);
1601 for (nn
=0; nn
<rf_list_used
; nn
++)
1602 rf_list
[nn
](hi
, old_handle
);
1603 snprintf(msgbuf
, sizeof(msgbuf
), "%s renamed account %s to %s.", user
->handle_info
->handle
, old_handle
, hi
->handle
);
1604 reply("NSMSG_HANDLE_CHANGED", old_handle
, hi
->handle
);
1605 global_message(MESSAGE_RECIPIENT_STAFF
, msgbuf
);
1610 static failpw_func_t
*failpw_func_list
;
1611 static unsigned int failpw_func_size
= 0, failpw_func_used
= 0;
1614 reg_failpw_func(failpw_func_t func
)
1616 if (failpw_func_used
== failpw_func_size
) {
1617 if (failpw_func_size
) {
1618 failpw_func_size
<<= 1;
1619 failpw_func_list
= realloc(failpw_func_list
, failpw_func_size
*sizeof(failpw_func_t
));
1621 failpw_func_size
= 8;
1622 failpw_func_list
= malloc(failpw_func_size
*sizeof(failpw_func_t
));
1625 failpw_func_list
[failpw_func_used
++] = func
;
1629 * Return hi if the handle/pass pair matches, NULL if it doesnt.
1631 * called by nefariouses enhanced AC login-on-connect code
1634 struct handle_info
*loc_auth(char *handle
, char *password
)
1636 int pw_arg
, used
, maxlogins
;
1639 struct handle_info
*hi
;
1640 struct userNode
*other
;
1642 hi
= dict_find(nickserv_handle_dict
, handle
, NULL
);
1648 /* We don't know the users hostname, or anything because they
1649 * havn't registered yet. So we can only allow LOC if your
1650 * account has *@* as a hostmask.
1652 for (ii
=0; ii
<hi
->masks
->used
; ii
++)
1654 if (!strcmp(hi
->masks
->list
[ii
], "*@*"))
1663 /* Responses from here on look up the language used by the handle they asked about. */
1664 if (!checkpass(password
, hi
->passwd
)) {
1667 if (HANDLE_FLAGGED(hi
, SUSPENDED
)) {
1670 maxlogins
= hi
->maxlogins
? hi
->maxlogins
: nickserv_conf
.default_maxlogins
;
1671 for (used
= 0, other
= hi
->users
; other
; other
= other
->next_authed
) {
1672 if (++used
>= maxlogins
) {
1679 static NICKSERV_FUNC(cmd_auth
)
1681 int pw_arg
, used
, maxlogins
;
1682 struct handle_info
*hi
;
1684 struct userNode
*other
;
1686 if (user
->handle_info
) {
1687 reply("NSMSG_ALREADY_AUTHED", user
->handle_info
->handle
);
1690 if (IsStamped(user
)) {
1691 /* Unauthenticated users might still have been stamped
1692 previously and could therefore have a hidden host;
1693 do not allow them to authenticate. */
1694 reply("NSMSG_STAMPED_AUTH");
1698 hi
= dict_find(nickserv_handle_dict
, argv
[1], NULL
);
1700 } else if (argc
== 2) {
1701 if (nickserv_conf
.disable_nicks
) {
1702 if (!(hi
= get_handle_info(user
->nick
))) {
1703 reply("NSMSG_HANDLE_NOT_FOUND");
1707 /* try to look up their handle from their nick */
1708 struct nick_info
*ni
;
1709 ni
= get_nick_info(user
->nick
);
1711 reply("NSMSG_NICK_NOT_REGISTERED", user
->nick
);
1718 reply("MSG_MISSING_PARAMS", argv
[0]);
1719 svccmd_send_help(user
, nickserv
, cmd
);
1723 reply("NSMSG_HANDLE_NOT_FOUND");
1726 /* Responses from here on look up the language used by the handle they asked about. */
1727 passwd
= argv
[pw_arg
];
1728 if (!valid_user_for(user
, hi
)) {
1729 if (hi
->email_addr
&& nickserv_conf
.email_enabled
)
1730 send_message_type(4, user
, cmd
->parent
->bot
,
1731 handle_find_message(hi
, "NSMSG_USE_AUTHCOOKIE"),
1734 send_message_type(4, user
, cmd
->parent
->bot
,
1735 handle_find_message(hi
, "NSMSG_HOSTMASK_INVALID"),
1737 argv
[pw_arg
] = "BADMASK";
1740 if (!checkpass(passwd
, hi
->passwd
)) {
1742 send_message_type(4, user
, cmd
->parent
->bot
,
1743 handle_find_message(hi
, "NSMSG_PASSWORD_INVALID"));
1744 argv
[pw_arg
] = "BADPASS";
1745 for (n
=0; n
<failpw_func_used
; n
++) failpw_func_list
[n
](user
, hi
);
1746 if (nickserv_conf
.autogag_enabled
) {
1747 if (!user
->auth_policer
.params
) {
1748 user
->auth_policer
.last_req
= now
;
1749 user
->auth_policer
.params
= nickserv_conf
.auth_policer_params
;
1751 if (!policer_conforms(&user
->auth_policer
, now
, 1.0)) {
1753 hostmask
= generate_hostmask(user
, GENMASK_STRICT_HOST
|GENMASK_BYIP
|GENMASK_NO_HIDING
);
1754 log_module(NS_LOG
, LOG_INFO
, "%s auto-gagged for repeated password guessing.", hostmask
);
1755 gag_create(hostmask
, nickserv
->nick
, "Repeated password guessing.", now
+nickserv_conf
.autogag_duration
);
1757 argv
[pw_arg
] = "GAGGED";
1762 if (HANDLE_FLAGGED(hi
, SUSPENDED
)) {
1763 send_message_type(4, user
, cmd
->parent
->bot
,
1764 handle_find_message(hi
, "NSMSG_HANDLE_SUSPENDED"));
1765 argv
[pw_arg
] = "SUSPENDED";
1768 maxlogins
= hi
->maxlogins
? hi
->maxlogins
: nickserv_conf
.default_maxlogins
;
1769 for (used
= 0, other
= hi
->users
; other
; other
= other
->next_authed
) {
1770 if (++used
>= maxlogins
) {
1771 send_message_type(4, user
, cmd
->parent
->bot
,
1772 handle_find_message(hi
, "NSMSG_MAX_LOGINS"),
1774 argv
[pw_arg
] = "MAXLOGINS";
1779 set_user_handle_info(user
, hi
, 1);
1780 if (nickserv_conf
.email_required
&& !hi
->email_addr
)
1781 reply("NSMSG_PLEASE_SET_EMAIL");
1782 if (!is_secure_password(hi
->handle
, passwd
, NULL
))
1783 reply("NSMSG_WEAK_PASSWORD");
1784 if (hi
->passwd
[0] != '$')
1785 cryptpass(passwd
, hi
->passwd
);
1786 reply("NSMSG_AUTH_SUCCESS");
1787 argv
[pw_arg
] = "****";
1791 static allowauth_func_t
*allowauth_func_list
;
1792 static unsigned int allowauth_func_size
= 0, allowauth_func_used
= 0;
1795 reg_allowauth_func(allowauth_func_t func
)
1797 if (allowauth_func_used
== allowauth_func_size
) {
1798 if (allowauth_func_size
) {
1799 allowauth_func_size
<<= 1;
1800 allowauth_func_list
= realloc(allowauth_func_list
, allowauth_func_size
*sizeof(allowauth_func_t
));
1802 allowauth_func_size
= 8;
1803 allowauth_func_list
= malloc(allowauth_func_size
*sizeof(allowauth_func_t
));
1806 allowauth_func_list
[allowauth_func_used
++] = func
;
1809 static NICKSERV_FUNC(cmd_allowauth
)
1811 struct userNode
*target
;
1812 struct handle_info
*hi
;
1815 NICKSERV_MIN_PARMS(2);
1816 if (!(target
= GetUserH(argv
[1]))) {
1817 reply("MSG_NICK_UNKNOWN", argv
[1]);
1820 if (target
->handle_info
) {
1821 reply("NSMSG_USER_PREV_AUTH", target
->nick
);
1824 if (IsStamped(target
)) {
1825 /* Unauthenticated users might still have been stamped
1826 previously and could therefore have a hidden host;
1827 do not allow them to authenticate to an account. */
1828 reply("NSMSG_USER_PREV_STAMP", target
->nick
);
1833 else if (!(hi
= get_handle_info(argv
[2]))) {
1834 reply("MSG_HANDLE_UNKNOWN", argv
[2]);
1838 if (hi
->opserv_level
> user
->handle_info
->opserv_level
) {
1839 reply("MSG_USER_OUTRANKED", hi
->handle
);
1842 if (((hi
->flags
& (HI_FLAG_SUPPORT_HELPER
|HI_FLAG_NETWORK_HELPER
))
1843 || (hi
->opserv_level
> 0))
1844 && ((argc
< 4) || irccasecmp(argv
[3], "staff"))) {
1845 reply("NSMSG_ALLOWAUTH_STAFF", hi
->handle
);
1848 dict_insert(nickserv_allow_auth_dict
, target
->nick
, hi
);
1849 reply("NSMSG_AUTH_ALLOWED", target
->nick
, hi
->handle
);
1850 send_message(target
, nickserv
, "NSMSG_AUTH_ALLOWED_MSG", hi
->handle
, hi
->handle
);
1851 if (nickserv_conf
.email_enabled
)
1852 send_message(target
, nickserv
, "NSMSG_AUTH_ALLOWED_EMAIL");
1854 if (dict_remove(nickserv_allow_auth_dict
, target
->nick
))
1855 reply("NSMSG_AUTH_NORMAL_ONLY", target
->nick
);
1857 reply("NSMSG_AUTH_UNSPECIAL", target
->nick
);
1859 for (n
=0; n
<allowauth_func_used
; n
++)
1860 allowauth_func_list
[n
](user
, target
, hi
);
1864 static NICKSERV_FUNC(cmd_authcookie
)
1866 struct handle_info
*hi
;
1868 NICKSERV_MIN_PARMS(2);
1869 if (user
->handle_info
) {
1870 reply("NSMSG_ALREADY_AUTHED", user
->handle_info
->handle
);
1873 if (IsStamped(user
)) {
1874 /* Unauthenticated users might still have been stamped
1875 previously and could therefore have a hidden host;
1876 do not allow them to authenticate to an account. */
1877 reply("NSMSG_STAMPED_AUTHCOOKIE");
1880 if (!(hi
= get_handle_info(argv
[1]))) {
1881 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
1884 if (!hi
->email_addr
) {
1885 reply("MSG_SET_EMAIL_ADDR");
1888 nickserv_make_cookie(user
, hi
, ALLOWAUTH
, NULL
, 0);
1892 static NICKSERV_FUNC(cmd_delcookie
)
1894 struct handle_info
*hi
;
1896 hi
= user
->handle_info
;
1898 reply("NSMSG_NO_COOKIE");
1901 switch (hi
->cookie
->type
) {
1904 reply("NSMSG_MUST_TIME_OUT");
1907 nickserv_eat_cookie(hi
->cookie
);
1908 reply("NSMSG_ATE_COOKIE");
1914 static NICKSERV_FUNC(cmd_odelcookie
)
1916 struct handle_info
*hi
;
1918 NICKSERV_MIN_PARMS(2);
1920 if (!(hi
= get_victim_oper(user
, argv
[1])))
1924 reply("NSMSG_NO_COOKIE_FOREIGN", hi
->handle
);
1928 nickserv_eat_cookie(hi
->cookie
);
1929 reply("NSMSG_ATE_FOREIGN_COOKIE", hi
->handle
);
1934 static NICKSERV_FUNC(cmd_resetpass
)
1936 struct handle_info
*hi
;
1937 char crypted
[MD5_CRYPT_LENGTH
];
1940 NICKSERV_MIN_PARMS(3);
1941 if(argc
>= 4 && !strcmp(argv
[3], "WEBLINK"))
1945 if (user
->handle_info
) {
1946 reply("NSMSG_ALREADY_AUTHED", user
->handle_info
->handle
);
1949 if (IsStamped(user
)) {
1950 /* Unauthenticated users might still have been stamped
1951 previously and could therefore have a hidden host;
1952 do not allow them to activate an account. */
1953 reply("NSMSG_STAMPED_RESETPASS");
1956 if (!(hi
= get_handle_info(argv
[1]))) {
1957 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
1960 if (!hi
->email_addr
) {
1961 reply("MSG_SET_EMAIL_ADDR");
1964 cryptpass(argv
[2], crypted
);
1966 nickserv_make_cookie(user
, hi
, PASSWORD_CHANGE
, crypted
, weblink
);
1970 static NICKSERV_FUNC(cmd_cookie
)
1972 struct handle_info
*hi
;
1975 if ((argc
== 2) && (hi
= user
->handle_info
) && hi
->cookie
&& (hi
->cookie
->type
== EMAIL_CHANGE
)) {
1978 NICKSERV_MIN_PARMS(3);
1979 if (!(hi
= get_handle_info(argv
[1]))) {
1980 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
1986 if (HANDLE_FLAGGED(hi
, SUSPENDED
)) {
1987 reply("NSMSG_HANDLE_SUSPENDED");
1992 reply("NSMSG_NO_COOKIE");
1996 /* Check validity of operation before comparing cookie to
1997 * prohibit guessing by authed users. */
1998 if (user
->handle_info
1999 && (hi
->cookie
->type
!= EMAIL_CHANGE
)
2000 && (hi
->cookie
->type
!= PASSWORD_CHANGE
)) {
2001 reply("NSMSG_CANNOT_COOKIE");
2005 if (strcmp(cookie
, hi
->cookie
->cookie
)) {
2006 reply("NSMSG_BAD_COOKIE");
2010 switch (hi
->cookie
->type
) {
2012 safestrncpy(hi
->passwd
, hi
->cookie
->data
, sizeof(hi
->passwd
));
2013 set_user_handle_info(user
, hi
, 1);
2014 reply("NSMSG_HANDLE_ACTIVATED");
2015 if (nickserv_conf
.sync_log
)
2016 SyncLog("ACCOUNTACC %s", hi
->handle
);
2018 case PASSWORD_CHANGE
:
2019 set_user_handle_info(user
, hi
, 1);
2020 safestrncpy(hi
->passwd
, hi
->cookie
->data
, sizeof(hi
->passwd
));
2021 reply("NSMSG_PASSWORD_CHANGED");
2022 if (nickserv_conf
.sync_log
)
2023 SyncLog("PASSCHANGE %s %s", hi
->handle
, hi
->passwd
);
2026 if (!hi
->email_addr
&& nickserv_conf
.sync_log
) {
2028 * This should only happen if an OREGISTER was sent. Require
2029 * email must be enabled! - SiRVulcaN
2031 SyncLog("REGISTER %s %s %s %s", hi
->handle
, hi
->passwd
, hi
->cookie
->data
, user
->info
);
2033 nickserv_set_email_addr(hi
, hi
->cookie
->data
);
2034 reply("NSMSG_EMAIL_CHANGED");
2035 if (nickserv_conf
.sync_log
)
2036 SyncLog("EMAILCHANGE %s %s", hi
->handle
, hi
->cookie
->data
);
2039 set_user_handle_info(user
, hi
, 1);
2040 reply("NSMSG_AUTH_SUCCESS");
2043 reply("NSMSG_BAD_COOKIE_TYPE", hi
->cookie
->type
);
2044 log_module(NS_LOG
, LOG_ERROR
, "Bad cookie type %d for account %s.", hi
->cookie
->type
, hi
->handle
);
2048 nickserv_eat_cookie(hi
->cookie
);
2053 static NICKSERV_FUNC(cmd_oregnick
) {
2055 struct handle_info
*target
;
2056 struct nick_info
*ni
;
2058 NICKSERV_MIN_PARMS(3);
2059 if (!(target
= modcmd_get_handle_info(user
, argv
[1])))
2062 if (!is_registerable_nick(nick
)) {
2063 reply("NSMSG_BAD_NICK", nick
);
2066 ni
= dict_find(nickserv_nick_dict
, nick
, NULL
);
2068 reply("NSMSG_NICK_EXISTS", nick
);
2071 register_nick(nick
, target
);
2072 reply("NSMSG_OREGNICK_SUCCESS", nick
, target
->handle
);
2076 static NICKSERV_FUNC(cmd_regnick
) {
2078 struct nick_info
*ni
;
2080 if (!is_registerable_nick(user
->nick
)) {
2081 reply("NSMSG_BAD_NICK", user
->nick
);
2084 /* count their nicks, see if it's too many */
2085 for (n
=0,ni
=user
->handle_info
->nicks
; ni
; n
++,ni
=ni
->next
) ;
2086 if (n
>= nickserv_conf
.nicks_per_handle
) {
2087 reply("NSMSG_TOO_MANY_NICKS");
2090 ni
= dict_find(nickserv_nick_dict
, user
->nick
, NULL
);
2092 reply("NSMSG_NICK_EXISTS", user
->nick
);
2095 register_nick(user
->nick
, user
->handle_info
);
2096 reply("NSMSG_REGNICK_SUCCESS", user
->nick
);
2100 static NICKSERV_FUNC(cmd_pass
)
2102 struct handle_info
*hi
;
2103 const char *old_pass
, *new_pass
;
2105 NICKSERV_MIN_PARMS(3);
2106 hi
= user
->handle_info
;
2110 if (!is_secure_password(hi
->handle
, new_pass
, user
)) return 0;
2111 if (!checkpass(old_pass
, hi
->passwd
)) {
2112 argv
[1] = "BADPASS";
2113 reply("NSMSG_PASSWORD_INVALID");
2116 cryptpass(new_pass
, hi
->passwd
);
2117 if (nickserv_conf
.sync_log
)
2118 SyncLog("PASSCHANGE %s %s", hi
->handle
, hi
->passwd
);
2120 reply("NSMSG_PASS_SUCCESS");
2125 nickserv_addmask(struct userNode
*user
, struct handle_info
*hi
, const char *mask
)
2128 char *new_mask
= canonicalize_hostmask(strdup(mask
));
2129 for (i
=0; i
<hi
->masks
->used
; i
++) {
2130 if (!irccasecmp(new_mask
, hi
->masks
->list
[i
])) {
2131 send_message(user
, nickserv
, "NSMSG_ADDMASK_ALREADY", new_mask
);
2136 string_list_append(hi
->masks
, new_mask
);
2137 send_message(user
, nickserv
, "NSMSG_ADDMASK_SUCCESS", new_mask
);
2141 static NICKSERV_FUNC(cmd_addmask
)
2144 char *mask
= generate_hostmask(user
, GENMASK_OMITNICK
|GENMASK_NO_HIDING
|GENMASK_ANY_IDENT
);
2145 int res
= nickserv_addmask(user
, user
->handle_info
, mask
);
2149 if (!is_gline(argv
[1])) {
2150 reply("NSMSG_MASK_INVALID", argv
[1]);
2153 return nickserv_addmask(user
, user
->handle_info
, argv
[1]);
2157 static NICKSERV_FUNC(cmd_oaddmask
)
2159 struct handle_info
*hi
;
2161 NICKSERV_MIN_PARMS(3);
2162 if (!(hi
= get_victim_oper(user
, argv
[1])))
2164 return nickserv_addmask(user
, hi
, argv
[2]);
2168 nickserv_delmask(struct userNode
*user
, struct handle_info
*hi
, const char *del_mask
)
2171 for (i
=0; i
<hi
->masks
->used
; i
++) {
2172 if (!strcmp(del_mask
, hi
->masks
->list
[i
])) {
2173 char *old_mask
= hi
->masks
->list
[i
];
2174 if (hi
->masks
->used
== 1) {
2175 send_message(user
, nickserv
, "NSMSG_DELMASK_NOTLAST");
2178 hi
->masks
->list
[i
] = hi
->masks
->list
[--hi
->masks
->used
];
2179 send_message(user
, nickserv
, "NSMSG_DELMASK_SUCCESS", old_mask
);
2184 send_message(user
, nickserv
, "NSMSG_DELMASK_NOT_FOUND");
2188 static NICKSERV_FUNC(cmd_delmask
)
2190 NICKSERV_MIN_PARMS(2);
2191 return nickserv_delmask(user
, user
->handle_info
, argv
[1]);
2194 static NICKSERV_FUNC(cmd_odelmask
)
2196 struct handle_info
*hi
;
2197 NICKSERV_MIN_PARMS(3);
2198 if (!(hi
= get_victim_oper(user
, argv
[1])))
2200 return nickserv_delmask(user
, hi
, argv
[2]);
2204 nickserv_modify_handle_flags(struct userNode
*user
, struct userNode
*bot
, const char *str
, unsigned long *padded
, unsigned long *premoved
) {
2205 unsigned int nn
, add
= 1, pos
;
2206 unsigned long added
, removed
, flag
;
2208 for (added
=removed
=nn
=0; str
[nn
]; nn
++) {
2210 case '+': add
= 1; break;
2211 case '-': add
= 0; break;
2213 if (!(pos
= handle_inverse_flags
[(unsigned char)str
[nn
]])) {
2214 send_message(user
, bot
, "NSMSG_INVALID_FLAG", str
[nn
]);
2217 if (user
&& (user
->handle_info
->opserv_level
< flag_access_levels
[pos
-1])) {
2218 /* cheesy avoidance of looking up the flag name.. */
2219 send_message(user
, bot
, "NSMSG_FLAG_PRIVILEGED", str
[nn
]);
2222 flag
= 1 << (pos
- 1);
2224 added
|= flag
, removed
&= ~flag
;
2226 removed
|= flag
, added
&= ~flag
;
2231 *premoved
= removed
;
2236 nickserv_apply_flags(struct userNode
*user
, struct handle_info
*hi
, const char *flags
)
2238 unsigned long before
, after
, added
, removed
;
2239 struct userNode
*uNode
;
2241 before
= hi
->flags
& (HI_FLAG_SUPPORT_HELPER
|HI_FLAG_NETWORK_HELPER
);
2242 if (!nickserv_modify_handle_flags(user
, nickserv
, flags
, &added
, &removed
))
2244 hi
->flags
= (hi
->flags
| added
) & ~removed
;
2245 after
= hi
->flags
& (HI_FLAG_SUPPORT_HELPER
|HI_FLAG_NETWORK_HELPER
);
2247 /* Strip helping flag if they're only a support helper and not
2248 * currently in #support. */
2249 if (HANDLE_FLAGGED(hi
, HELPING
) && (after
== HI_FLAG_SUPPORT_HELPER
)) {
2250 struct channelList
*schannels
;
2252 schannels
= chanserv_support_channels();
2253 for (uNode
= hi
->users
; uNode
; uNode
= uNode
->next_authed
) {
2254 for (ii
= 0; ii
< schannels
->used
; ++ii
)
2255 if (GetUserMode(schannels
->list
[ii
], uNode
))
2257 if (ii
< schannels
->used
)
2261 HANDLE_CLEAR_FLAG(hi
, HELPING
);
2264 if (after
&& !before
) {
2265 /* Add user to current helper list. */
2266 for (uNode
= hi
->users
; uNode
; uNode
= uNode
->next_authed
)
2267 userList_append(&curr_helpers
, uNode
);
2268 } else if (!after
&& before
) {
2269 /* Remove user from current helper list. */
2270 for (uNode
= hi
->users
; uNode
; uNode
= uNode
->next_authed
)
2271 userList_remove(&curr_helpers
, uNode
);
2278 set_list(struct userNode
*user
, struct handle_info
*hi
, int override
)
2282 char *set_display
[] = {
2283 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2284 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2287 send_message(user
, nickserv
, "NSMSG_SETTING_LIST");
2289 /* Do this so options are presented in a consistent order. */
2290 for (i
= 0; i
< ArrayLength(set_display
); ++i
)
2291 if ((opt
= dict_find(nickserv_opt_dict
, set_display
[i
], NULL
)))
2292 opt(user
, hi
, override
, 0, NULL
);
2295 static NICKSERV_FUNC(cmd_set
)
2297 struct handle_info
*hi
;
2300 hi
= user
->handle_info
;
2302 set_list(user
, hi
, 0);
2305 if (!(opt
= dict_find(nickserv_opt_dict
, argv
[1], NULL
))) {
2306 reply("NSMSG_INVALID_OPTION", argv
[1]);
2309 return opt(user
, hi
, 0, argc
-1, argv
+1);
2312 static NICKSERV_FUNC(cmd_oset
)
2314 struct handle_info
*hi
;
2317 NICKSERV_MIN_PARMS(2);
2319 if (!(hi
= get_victim_oper(user
, argv
[1])))
2323 set_list(user
, hi
, 0);
2327 if (!(opt
= dict_find(nickserv_opt_dict
, argv
[2], NULL
))) {
2328 reply("NSMSG_INVALID_OPTION", argv
[2]);
2332 return opt(user
, hi
, 1, argc
-2, argv
+2);
2335 static OPTION_FUNC(opt_info
)
2339 if ((argv
[1][0] == '*') && (argv
[1][1] == 0)) {
2341 hi
->infoline
= NULL
;
2343 hi
->infoline
= strdup(unsplit_string(argv
+1, argc
-1, NULL
));
2347 info
= hi
->infoline
? hi
->infoline
: user_find_message(user
, "MSG_NONE");
2348 send_message(user
, nickserv
, "NSMSG_SET_INFO", info
);
2352 static OPTION_FUNC(opt_width
)
2355 hi
->screen_width
= strtoul(argv
[1], NULL
, 0);
2357 if ((hi
->screen_width
> 0) && (hi
->screen_width
< MIN_LINE_SIZE
))
2358 hi
->screen_width
= MIN_LINE_SIZE
;
2359 else if (hi
->screen_width
> MAX_LINE_SIZE
)
2360 hi
->screen_width
= MAX_LINE_SIZE
;
2362 send_message(user
, nickserv
, "NSMSG_SET_WIDTH", hi
->screen_width
);
2366 static OPTION_FUNC(opt_tablewidth
)
2369 hi
->table_width
= strtoul(argv
[1], NULL
, 0);
2371 if ((hi
->table_width
> 0) && (hi
->table_width
< MIN_LINE_SIZE
))
2372 hi
->table_width
= MIN_LINE_SIZE
;
2373 else if (hi
->screen_width
> MAX_LINE_SIZE
)
2374 hi
->table_width
= MAX_LINE_SIZE
;
2376 send_message(user
, nickserv
, "NSMSG_SET_TABLEWIDTH", hi
->table_width
);
2380 static OPTION_FUNC(opt_color
)
2383 if (enabled_string(argv
[1]))
2384 HANDLE_SET_FLAG(hi
, MIRC_COLOR
);
2385 else if (disabled_string(argv
[1]))
2386 HANDLE_CLEAR_FLAG(hi
, MIRC_COLOR
);
2388 send_message(user
, nickserv
, "MSG_INVALID_BINARY", argv
[1]);
2393 send_message(user
, nickserv
, "NSMSG_SET_COLOR", user_find_message(user
, HANDLE_FLAGGED(hi
, MIRC_COLOR
) ? "MSG_ON" : "MSG_OFF"));
2397 static OPTION_FUNC(opt_privmsg
)
2400 if (enabled_string(argv
[1]))
2401 HANDLE_SET_FLAG(hi
, USE_PRIVMSG
);
2402 else if (disabled_string(argv
[1]))
2403 HANDLE_CLEAR_FLAG(hi
, USE_PRIVMSG
);
2405 send_message(user
, nickserv
, "MSG_INVALID_BINARY", argv
[1]);
2410 send_message(user
, nickserv
, "NSMSG_SET_PRIVMSG", user_find_message(user
, HANDLE_FLAGGED(hi
, USE_PRIVMSG
) ? "MSG_ON" : "MSG_OFF"));
2414 static OPTION_FUNC(opt_style
)
2419 if (!irccasecmp(argv
[1], "Zoot"))
2420 hi
->userlist_style
= HI_STYLE_ZOOT
;
2421 else if (!irccasecmp(argv
[1], "def"))
2422 hi
->userlist_style
= HI_STYLE_DEF
;
2425 switch (hi
->userlist_style
) {
2434 send_message(user
, nickserv
, "NSMSG_SET_STYLE", style
);
2438 static OPTION_FUNC(opt_announcements
)
2443 if (enabled_string(argv
[1]))
2444 hi
->announcements
= 'y';
2445 else if (disabled_string(argv
[1]))
2446 hi
->announcements
= 'n';
2447 else if (!strcmp(argv
[1], "?") || !irccasecmp(argv
[1], "default"))
2448 hi
->announcements
= '?';
2450 send_message(user
, nickserv
, "NSMSG_INVALID_ANNOUNCE", argv
[1]);
2455 switch (hi
->announcements
) {
2456 case 'y': choice
= user_find_message(user
, "MSG_ON"); break;
2457 case 'n': choice
= user_find_message(user
, "MSG_OFF"); break;
2458 case '?': choice
= "default"; break;
2459 default: choice
= "unknown"; break;
2461 send_message(user
, nickserv
, "NSMSG_SET_ANNOUNCEMENTS", choice
);
2465 static OPTION_FUNC(opt_password
)
2468 send_message(user
, nickserv
, "NSMSG_USE_CMD_PASS");
2473 cryptpass(argv
[1], hi
->passwd
);
2475 send_message(user
, nickserv
, "NSMSG_SET_PASSWORD", "***");
2479 static OPTION_FUNC(opt_flags
)
2482 unsigned int ii
, flen
;
2485 send_message(user
, nickserv
, "MSG_SETTING_PRIVILEGED", argv
[0]);
2490 nickserv_apply_flags(user
, hi
, argv
[1]);
2492 for (ii
= flen
= 0; handle_flags
[ii
]; ii
++)
2493 if (hi
->flags
& (1 << ii
))
2494 flags
[flen
++] = handle_flags
[ii
];
2497 send_message(user
, nickserv
, "NSMSG_SET_FLAGS", flags
);
2499 send_message(user
, nickserv
, "NSMSG_SET_FLAGS", user_find_message(user
, "MSG_NONE"));
2503 static OPTION_FUNC(opt_email
)
2507 if (!is_valid_email_addr(argv
[1])) {
2508 send_message(user
, nickserv
, "NSMSG_BAD_EMAIL_ADDR");
2511 if ((str
= sendmail_prohibited_address(argv
[1]))) {
2512 send_message(user
, nickserv
, "NSMSG_EMAIL_PROHIBITED", argv
[1], str
);
2515 if (hi
->email_addr
&& !irccasecmp(hi
->email_addr
, argv
[1]))
2516 send_message(user
, nickserv
, "NSMSG_EMAIL_SAME");
2518 nickserv_make_cookie(user
, hi
, EMAIL_CHANGE
, argv
[1], 0);
2520 nickserv_set_email_addr(hi
, argv
[1]);
2522 nickserv_eat_cookie(hi
->cookie
);
2523 send_message(user
, nickserv
, "NSMSG_SET_EMAIL", visible_email_addr(user
, hi
));
2526 send_message(user
, nickserv
, "NSMSG_SET_EMAIL", visible_email_addr(user
, hi
));
2530 static OPTION_FUNC(opt_maxlogins
)
2532 unsigned char maxlogins
;
2534 maxlogins
= strtoul(argv
[1], NULL
, 0);
2535 if ((maxlogins
> nickserv_conf
.hard_maxlogins
) && !override
) {
2536 send_message(user
, nickserv
, "NSMSG_BAD_MAX_LOGINS", nickserv_conf
.hard_maxlogins
);
2539 hi
->maxlogins
= maxlogins
;
2541 maxlogins
= hi
->maxlogins
? hi
->maxlogins
: nickserv_conf
.default_maxlogins
;
2542 send_message(user
, nickserv
, "NSMSG_SET_MAXLOGINS", maxlogins
);
2546 static OPTION_FUNC(opt_language
)
2548 struct language
*lang
;
2550 lang
= language_find(argv
[1]);
2551 if (irccasecmp(lang
->name
, argv
[1]))
2552 send_message(user
, nickserv
, "NSMSG_LANGUAGE_NOT_FOUND", argv
[1], lang
->name
);
2553 hi
->language
= lang
;
2555 send_message(user
, nickserv
, "NSMSG_SET_LANGUAGE", hi
->language
->name
);
2560 oper_try_set_access(struct userNode
*user
, struct userNode
*bot
, struct handle_info
*target
, unsigned int new_level
) {
2561 if (!oper_has_access(user
, bot
, nickserv_conf
.modoper_level
, 0))
2563 if ((user
->handle_info
->opserv_level
< target
->opserv_level
)
2564 || ((user
->handle_info
->opserv_level
== target
->opserv_level
)
2565 && (user
->handle_info
->opserv_level
< 1000))) {
2566 send_message(user
, bot
, "MSG_USER_OUTRANKED", target
->handle
);
2569 if ((user
->handle_info
->opserv_level
< new_level
)
2570 || ((user
->handle_info
->opserv_level
== new_level
)
2571 && (user
->handle_info
->opserv_level
< 1000))) {
2572 send_message(user
, bot
, "NSMSG_OPSERV_LEVEL_BAD");
2575 if (user
->handle_info
== target
) {
2576 send_message(user
, bot
, "MSG_STUPID_ACCESS_CHANGE");
2579 if (target
->opserv_level
== new_level
)
2581 log_module(NS_LOG
, LOG_INFO
, "Account %s setting oper level for account %s to %d (from %d).",
2582 user
->handle_info
->handle
, target
->handle
, new_level
, target
->opserv_level
);
2583 target
->opserv_level
= new_level
;
2587 static OPTION_FUNC(opt_level
)
2592 send_message(user
, nickserv
, "MSG_SETTING_PRIVILEGED", argv
[0]);
2596 res
= (argc
> 1) ? oper_try_set_access(user
, nickserv
, hi
, strtoul(argv
[1], NULL
, 0)) : 0;
2597 send_message(user
, nickserv
, "NSMSG_SET_LEVEL", hi
->opserv_level
);
2601 static OPTION_FUNC(opt_epithet
)
2604 send_message(user
, nickserv
, "MSG_SETTING_PRIVILEGED", argv
[0]);
2608 if ((argc
> 1) && oper_has_access(user
, nickserv
, nickserv_conf
.set_epithet_level
, 0)) {
2609 char *epithet
= unsplit_string(argv
+1, argc
-1, NULL
);
2612 if ((epithet
[0] == '*') && !epithet
[1])
2615 hi
->epithet
= strdup(epithet
);
2619 send_message(user
, nickserv
, "NSMSG_SET_EPITHET", hi
->epithet
);
2621 send_message(user
, nickserv
, "NSMSG_SET_EPITHET", user_find_message(user
, "MSG_NONE"));
2625 static OPTION_FUNC(opt_title
)
2630 send_message(user
, nickserv
, "MSG_SETTING_PRIVILEGED", argv
[0]);
2634 if ((argc
> 1) && oper_has_access(user
, nickserv
, nickserv_conf
.set_title_level
, 0)) {
2636 if (strchr(title
, '.')) {
2637 send_message(user
, nickserv
, "NSMSG_TITLE_INVALID");
2640 if ((strlen(user
->handle_info
->handle
) + strlen(title
) +
2641 strlen(nickserv_conf
.titlehost_suffix
) + 2) > HOSTLEN
) {
2642 send_message(user
, nickserv
, "NSMSG_TITLE_TRUNCATED");
2647 if (!strcmp(title
, "*")) {
2648 hi
->fakehost
= NULL
;
2650 hi
->fakehost
= malloc(strlen(title
)+2);
2651 hi
->fakehost
[0] = '.';
2652 strcpy(hi
->fakehost
+1, title
);
2655 } else if (hi
->fakehost
&& (hi
->fakehost
[0] == '.'))
2656 title
= hi
->fakehost
+ 1;
2660 title
= user_find_message(user
, "MSG_NONE");
2661 send_message(user
, nickserv
, "NSMSG_SET_TITLE", title
);
2665 static OPTION_FUNC(opt_fakehost
)
2670 send_message(user
, nickserv
, "MSG_SETTING_PRIVILEGED", argv
[0]);
2674 if ((argc
> 1) && oper_has_access(user
, nickserv
, nickserv_conf
.set_fakehost_level
, 0)) {
2676 if ((strlen(fake
) > HOSTLEN
) || (fake
[0] == '.')) {
2677 send_message(user
, nickserv
, "NSMSG_FAKEHOST_INVALID", HOSTLEN
);
2681 if (!strcmp(fake
, "*"))
2682 hi
->fakehost
= NULL
;
2684 hi
->fakehost
= strdup(fake
);
2685 fake
= hi
->fakehost
;
2688 fake
= generate_fakehost(hi
);
2690 fake
= user_find_message(user
, "MSG_NONE");
2691 send_message(user
, nickserv
, "NSMSG_SET_FAKEHOST", fake
);
2695 static NICKSERV_FUNC(cmd_reclaim
)
2697 struct handle_info
*hi
;
2698 struct nick_info
*ni
;
2699 struct userNode
*victim
;
2701 NICKSERV_MIN_PARMS(2);
2702 hi
= user
->handle_info
;
2703 ni
= dict_find(nickserv_nick_dict
, argv
[1], 0);
2705 reply("NSMSG_UNKNOWN_NICK", argv
[1]);
2708 if (ni
->owner
!= user
->handle_info
) {
2709 reply("NSMSG_NOT_YOUR_NICK", ni
->nick
);
2712 victim
= GetUserH(ni
->nick
);
2714 reply("MSG_NICK_UNKNOWN", ni
->nick
);
2717 if (victim
== user
) {
2718 reply("NSMSG_NICK_USER_YOU");
2721 nickserv_reclaim(victim
, ni
, nickserv_conf
.reclaim_action
);
2722 switch (nickserv_conf
.reclaim_action
) {
2723 case RECLAIM_NONE
: reply("NSMSG_RECLAIMED_NONE"); break;
2724 case RECLAIM_WARN
: reply("NSMSG_RECLAIMED_WARN", victim
->nick
); break;
2725 case RECLAIM_SVSNICK
: reply("NSMSG_RECLAIMED_SVSNICK", victim
->nick
); break;
2726 case RECLAIM_KILL
: reply("NSMSG_RECLAIMED_KILL", victim
->nick
); break;
2731 static NICKSERV_FUNC(cmd_unregnick
)
2734 struct handle_info
*hi
;
2735 struct nick_info
*ni
;
2737 hi
= user
->handle_info
;
2738 nick
= (argc
< 2) ? user
->nick
: (const char*)argv
[1];
2739 ni
= dict_find(nickserv_nick_dict
, nick
, NULL
);
2741 reply("NSMSG_UNKNOWN_NICK", nick
);
2744 if (hi
!= ni
->owner
) {
2745 reply("NSMSG_NOT_YOUR_NICK", nick
);
2748 reply("NSMSG_UNREGNICK_SUCCESS", ni
->nick
);
2753 static NICKSERV_FUNC(cmd_ounregnick
)
2755 struct nick_info
*ni
;
2757 NICKSERV_MIN_PARMS(2);
2758 if (!(ni
= get_nick_info(argv
[1]))) {
2759 reply("NSMSG_NICK_NOT_REGISTERED", argv
[1]);
2762 if (ni
->owner
->opserv_level
>= user
->handle_info
->opserv_level
) {
2763 reply("MSG_USER_OUTRANKED", ni
->nick
);
2766 reply("NSMSG_UNREGNICK_SUCCESS", ni
->nick
);
2771 static NICKSERV_FUNC(cmd_unregister
)
2773 struct handle_info
*hi
;
2776 NICKSERV_MIN_PARMS(2);
2777 hi
= user
->handle_info
;
2780 if (checkpass(passwd
, hi
->passwd
)) {
2781 nickserv_unregister_handle(hi
, user
);
2784 log_module(NS_LOG
, LOG_INFO
, "Account '%s' tried to unregister with the wrong password.", hi
->handle
);
2785 reply("NSMSG_PASSWORD_INVALID");
2790 static NICKSERV_FUNC(cmd_ounregister
)
2792 struct handle_info
*hi
;
2794 NICKSERV_MIN_PARMS(2);
2795 if (!(hi
= get_victim_oper(user
, argv
[1])))
2797 nickserv_unregister_handle(hi
, user
);
2801 static NICKSERV_FUNC(cmd_status
)
2803 if (nickserv_conf
.disable_nicks
) {
2804 reply("NSMSG_GLOBAL_STATS_NONICK",
2805 dict_size(nickserv_handle_dict
));
2807 if (user
->handle_info
) {
2809 struct nick_info
*ni
;
2810 for (ni
=user
->handle_info
->nicks
; ni
; ni
=ni
->next
) cnt
++;
2811 reply("NSMSG_HANDLE_STATS", cnt
);
2813 reply("NSMSG_HANDLE_NONE");
2815 reply("NSMSG_GLOBAL_STATS",
2816 dict_size(nickserv_handle_dict
),
2817 dict_size(nickserv_nick_dict
));
2822 static NICKSERV_FUNC(cmd_ghost
)
2824 struct userNode
*target
;
2825 char reason
[MAXLEN
];
2827 NICKSERV_MIN_PARMS(2);
2828 if (!(target
= GetUserH(argv
[1]))) {
2829 reply("MSG_NICK_UNKNOWN", argv
[1]);
2832 if (target
== user
) {
2833 reply("NSMSG_CANNOT_GHOST_SELF");
2836 if (!target
->handle_info
|| (target
->handle_info
!= user
->handle_info
)) {
2837 reply("NSMSG_CANNOT_GHOST_USER", target
->nick
);
2840 snprintf(reason
, sizeof(reason
), "Ghost kill on account %s (requested by %s).", target
->handle_info
->handle
, user
->nick
);
2841 DelUser(target
, nickserv
, 1, reason
);
2842 reply("NSMSG_GHOST_KILLED", argv
[1]);
2846 static NICKSERV_FUNC(cmd_vacation
)
2848 HANDLE_SET_FLAG(user
->handle_info
, FROZEN
);
2849 reply("NSMSG_ON_VACATION");
2854 nickserv_saxdb_write(struct saxdb_context
*ctx
) {
2856 struct handle_info
*hi
;
2859 for (it
= dict_first(nickserv_handle_dict
); it
; it
= iter_next(it
)) {
2861 #ifdef WITH_PROTOCOL_BAHAMUT
2864 saxdb_start_record(ctx
, iter_key(it
), 0);
2865 if (hi
->announcements
!= '?') {
2866 flags
[0] = hi
->announcements
;
2868 saxdb_write_string(ctx
, KEY_ANNOUNCEMENTS
, flags
);
2871 struct handle_cookie
*cookie
= hi
->cookie
;
2874 switch (cookie
->type
) {
2875 case ACTIVATION
: type
= KEY_ACTIVATION
; break;
2876 case PASSWORD_CHANGE
: type
= KEY_PASSWORD_CHANGE
; break;
2877 case EMAIL_CHANGE
: type
= KEY_EMAIL_CHANGE
; break;
2878 case ALLOWAUTH
: type
= KEY_ALLOWAUTH
; break;
2879 default: type
= NULL
; break;
2882 saxdb_start_record(ctx
, KEY_COOKIE
, 0);
2883 saxdb_write_string(ctx
, KEY_COOKIE_TYPE
, type
);
2884 saxdb_write_int(ctx
, KEY_COOKIE_EXPIRES
, cookie
->expires
);
2886 saxdb_write_string(ctx
, KEY_COOKIE_DATA
, cookie
->data
);
2887 saxdb_write_string(ctx
, KEY_COOKIE
, cookie
->cookie
);
2888 saxdb_end_record(ctx
);
2892 saxdb_write_string(ctx
, KEY_EMAIL_ADDR
, hi
->email_addr
);
2894 saxdb_write_string(ctx
, KEY_EPITHET
, hi
->epithet
);
2896 saxdb_write_string(ctx
, KEY_FAKEHOST
, hi
->fakehost
);
2900 for (ii
=flen
=0; handle_flags
[ii
]; ++ii
)
2901 if (hi
->flags
& (1 << ii
))
2902 flags
[flen
++] = handle_flags
[ii
];
2904 saxdb_write_string(ctx
, KEY_FLAGS
, flags
);
2906 #ifdef WITH_PROTOCOL_BAHAMUT
2907 saxdb_write_int(ctx
, KEY_ID
, hi
->id
);
2910 saxdb_write_string(ctx
, KEY_INFO
, hi
->infoline
);
2911 if (hi
->last_quit_host
[0])
2912 saxdb_write_string(ctx
, KEY_LAST_QUIT_HOST
, hi
->last_quit_host
);
2913 saxdb_write_int(ctx
, KEY_LAST_SEEN
, hi
->lastseen
);
2914 if (hi
->masks
->used
)
2915 saxdb_write_string_list(ctx
, KEY_MASKS
, hi
->masks
);
2917 saxdb_write_int(ctx
, KEY_MAXLOGINS
, hi
->maxlogins
);
2919 struct string_list
*slist
;
2920 struct nick_info
*ni
;
2922 slist
= alloc_string_list(nickserv_conf
.nicks_per_handle
);
2923 for (ni
= hi
->nicks
; ni
; ni
= ni
->next
) string_list_append(slist
, ni
->nick
);
2924 saxdb_write_string_list(ctx
, KEY_NICKS
, slist
);
2928 if (hi
->opserv_level
)
2929 saxdb_write_int(ctx
, KEY_OPSERV_LEVEL
, hi
->opserv_level
);
2930 if (hi
->language
!= lang_C
)
2931 saxdb_write_string(ctx
, KEY_LANGUAGE
, hi
->language
->name
);
2932 saxdb_write_string(ctx
, KEY_PASSWD
, hi
->passwd
);
2933 saxdb_write_int(ctx
, KEY_REGISTER_ON
, hi
->registered
);
2934 if (hi
->screen_width
)
2935 saxdb_write_int(ctx
, KEY_SCREEN_WIDTH
, hi
->screen_width
);
2936 if (hi
->table_width
)
2937 saxdb_write_int(ctx
, KEY_TABLE_WIDTH
, hi
->table_width
);
2938 flags
[0] = hi
->userlist_style
;
2940 saxdb_write_string(ctx
, KEY_USERLIST_STYLE
, flags
);
2941 saxdb_end_record(ctx
);
2946 static handle_merge_func_t
*handle_merge_func_list
;
2947 static unsigned int handle_merge_func_size
= 0, handle_merge_func_used
= 0;
2950 reg_handle_merge_func(handle_merge_func_t func
)
2952 if (handle_merge_func_used
== handle_merge_func_size
) {
2953 if (handle_merge_func_size
) {
2954 handle_merge_func_size
<<= 1;
2955 handle_merge_func_list
= realloc(handle_merge_func_list
, handle_merge_func_size
*sizeof(handle_merge_func_t
));
2957 handle_merge_func_size
= 8;
2958 handle_merge_func_list
= malloc(handle_merge_func_size
*sizeof(handle_merge_func_t
));
2961 handle_merge_func_list
[handle_merge_func_used
++] = func
;
2964 static NICKSERV_FUNC(cmd_merge
)
2966 struct handle_info
*hi_from
, *hi_to
;
2967 struct userNode
*last_user
;
2968 struct userData
*cList
, *cListNext
;
2969 unsigned int ii
, jj
, n
;
2970 char buffer
[MAXLEN
];
2972 NICKSERV_MIN_PARMS(3);
2974 if (!(hi_from
= get_victim_oper(user
, argv
[1])))
2976 if (!(hi_to
= get_victim_oper(user
, argv
[2])))
2978 if (hi_to
== hi_from
) {
2979 reply("NSMSG_CANNOT_MERGE_SELF", hi_to
->handle
);
2983 for (n
=0; n
<handle_merge_func_used
; n
++)
2984 handle_merge_func_list
[n
](user
, hi_to
, hi_from
);
2986 /* Append "from" handle's nicks to "to" handle's nick list. */
2988 struct nick_info
*last_ni
;
2989 for (last_ni
=hi_to
->nicks
; last_ni
->next
; last_ni
=last_ni
->next
) ;
2990 last_ni
->next
= hi_from
->nicks
;
2992 while (hi_from
->nicks
) {
2993 hi_from
->nicks
->owner
= hi_to
;
2994 hi_from
->nicks
= hi_from
->nicks
->next
;
2997 /* Merge the hostmasks. */
2998 for (ii
=0; ii
<hi_from
->masks
->used
; ii
++) {
2999 char *mask
= hi_from
->masks
->list
[ii
];
3000 for (jj
=0; jj
<hi_to
->masks
->used
; jj
++)
3001 if (match_ircglobs(hi_to
->masks
->list
[jj
], mask
))
3003 if (jj
==hi_to
->masks
->used
) /* Nothing from the "to" handle covered this mask, so add it. */
3004 string_list_append(hi_to
->masks
, strdup(mask
));
3007 /* Merge the lists of authed users. */
3009 for (last_user
=hi_to
->users
; last_user
->next_authed
; last_user
=last_user
->next_authed
) ;
3010 last_user
->next_authed
= hi_from
->users
;
3012 hi_to
->users
= hi_from
->users
;
3014 /* Repoint the old "from" handle's users. */
3015 for (last_user
=hi_from
->users
; last_user
; last_user
=last_user
->next_authed
) {
3016 last_user
->handle_info
= hi_to
;
3018 hi_from
->users
= NULL
;
3020 /* Merge channel userlists. */
3021 for (cList
=hi_from
->channels
; cList
; cList
=cListNext
) {
3022 struct userData
*cList2
;
3023 cListNext
= cList
->u_next
;
3024 for (cList2
=hi_to
->channels
; cList2
; cList2
=cList2
->u_next
)
3025 if (cList
->channel
== cList2
->channel
)
3027 if (cList2
&& (cList2
->access
>= cList
->access
)) {
3028 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
);
3029 /* keep cList2 in hi_to; remove cList from hi_from */
3030 del_channel_user(cList
, 1);
3033 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
);
3034 /* remove the lower-ranking cList2 from hi_to */
3035 del_channel_user(cList2
, 1);
3037 log_module(NS_LOG
, LOG_INFO
, "Merge: %s had no access in %s", hi_to
->handle
, cList
->channel
->channel
->name
);
3039 /* cList needs to be moved from hi_from to hi_to */
3040 cList
->handle
= hi_to
;
3041 /* Remove from linked list for hi_from */
3042 assert(!cList
->u_prev
);
3043 hi_from
->channels
= cList
->u_next
;
3045 cList
->u_next
->u_prev
= cList
->u_prev
;
3046 /* Add to linked list for hi_to */
3047 cList
->u_prev
= NULL
;
3048 cList
->u_next
= hi_to
->channels
;
3049 if (hi_to
->channels
)
3050 hi_to
->channels
->u_prev
= cList
;
3051 hi_to
->channels
= cList
;
3055 /* Do they get an OpServ level promotion? */
3056 if (hi_from
->opserv_level
> hi_to
->opserv_level
)
3057 hi_to
->opserv_level
= hi_from
->opserv_level
;
3059 /* What about last seen time? */
3060 if (hi_from
->lastseen
> hi_to
->lastseen
)
3061 hi_to
->lastseen
= hi_from
->lastseen
;
3063 /* Does a fakehost carry over? (This intentionally doesn't set it
3064 * for users previously attached to hi_to. They'll just have to
3067 if (hi_from
->fakehost
&& !hi_to
->fakehost
)
3068 hi_to
->fakehost
= strdup(hi_from
->fakehost
);
3070 /* Notify of success. */
3071 sprintf(buffer
, "%s (%s) merged account %s into %s.", user
->nick
, user
->handle_info
->handle
, hi_from
->handle
, hi_to
->handle
);
3072 reply("NSMSG_HANDLES_MERGED", hi_from
->handle
, hi_to
->handle
);
3073 global_message(MESSAGE_RECIPIENT_STAFF
, buffer
);
3075 /* Unregister the "from" handle. */
3076 nickserv_unregister_handle(hi_from
, NULL
);
3081 struct nickserv_discrim
{
3082 unsigned int limit
, min_level
, max_level
;
3083 unsigned long flags_on
, flags_off
;
3084 time_t min_registered
, max_registered
;
3086 enum { SUBSET
, EXACT
, SUPERSET
, LASTQUIT
} hostmask_type
;
3087 const char *nickmask
;
3088 const char *hostmask
;
3089 const char *handlemask
;
3090 const char *emailmask
;
3093 typedef void (*discrim_search_func
)(struct userNode
*source
, struct handle_info
*hi
);
3095 struct discrim_apply_info
{
3096 struct nickserv_discrim
*discrim
;
3097 discrim_search_func func
;
3098 struct userNode
*source
;
3099 unsigned int matched
;
3102 static struct nickserv_discrim
*
3103 nickserv_discrim_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
3106 struct nickserv_discrim
*discrim
;
3108 discrim
= malloc(sizeof(*discrim
));
3109 memset(discrim
, 0, sizeof(*discrim
));
3110 discrim
->min_level
= 0;
3111 discrim
->max_level
= ~0;
3112 discrim
->limit
= 50;
3113 discrim
->min_registered
= 0;
3114 discrim
->max_registered
= INT_MAX
;
3115 discrim
->lastseen
= now
;
3117 for (i
=0; i
<argc
; i
++) {
3118 if (i
== argc
- 1) {
3119 send_message(user
, nickserv
, "MSG_MISSING_PARAMS", argv
[i
]);
3122 if (!irccasecmp(argv
[i
], "limit")) {
3123 discrim
->limit
= strtoul(argv
[++i
], NULL
, 0);
3124 } else if (!irccasecmp(argv
[i
], "flags")) {
3125 nickserv_modify_handle_flags(user
, nickserv
, argv
[++i
], &discrim
->flags_on
, &discrim
->flags_off
);
3126 } else if (!irccasecmp(argv
[i
], "registered")) {
3127 const char *cmp
= argv
[++i
];
3128 if (cmp
[0] == '<') {
3129 if (cmp
[1] == '=') {
3130 discrim
->min_registered
= now
- ParseInterval(cmp
+2);
3132 discrim
->min_registered
= now
- ParseInterval(cmp
+1) + 1;
3134 } else if (cmp
[0] == '=') {
3135 discrim
->min_registered
= discrim
->max_registered
= now
- ParseInterval(cmp
+1);
3136 } else if (cmp
[0] == '>') {
3137 if (cmp
[1] == '=') {
3138 discrim
->max_registered
= now
- ParseInterval(cmp
+2);
3140 discrim
->max_registered
= now
- ParseInterval(cmp
+1) - 1;
3143 send_message(user
, nickserv
, "MSG_INVALID_CRITERIA", cmp
);
3145 } else if (!irccasecmp(argv
[i
], "seen")) {
3146 discrim
->lastseen
= now
- ParseInterval(argv
[++i
]);
3147 } else if (!nickserv_conf
.disable_nicks
&& !irccasecmp(argv
[i
], "nickmask")) {
3148 discrim
->nickmask
= argv
[++i
];
3149 } else if (!irccasecmp(argv
[i
], "hostmask")) {
3151 if (!irccasecmp(argv
[i
], "exact")) {
3152 if (i
== argc
- 1) {
3153 send_message(user
, nickserv
, "MSG_MISSING_PARAMS", argv
[i
]);
3156 discrim
->hostmask_type
= EXACT
;
3157 } else if (!irccasecmp(argv
[i
], "subset")) {
3158 if (i
== argc
- 1) {
3159 send_message(user
, nickserv
, "MSG_MISSING_PARAMS", argv
[i
]);
3162 discrim
->hostmask_type
= SUBSET
;
3163 } else if (!irccasecmp(argv
[i
], "superset")) {
3164 if (i
== argc
- 1) {
3165 send_message(user
, nickserv
, "MSG_MISSING_PARAMS", argv
[i
]);
3168 discrim
->hostmask_type
= SUPERSET
;
3169 } else if (!irccasecmp(argv
[i
], "lastquit") || !irccasecmp(argv
[i
], "lastauth")) {
3170 if (i
== argc
- 1) {
3171 send_message(user
, nickserv
, "MSG_MISSING_PARAMS", argv
[i
]);
3174 discrim
->hostmask_type
= LASTQUIT
;
3177 discrim
->hostmask_type
= SUPERSET
;
3179 discrim
->hostmask
= argv
[++i
];
3180 } else if (!irccasecmp(argv
[i
], "handlemask") || !irccasecmp(argv
[i
], "accountmask")) {
3181 if (!irccasecmp(argv
[++i
], "*")) {
3182 discrim
->handlemask
= 0;
3184 discrim
->handlemask
= argv
[i
];
3186 } else if (!irccasecmp(argv
[i
], "email")) {
3187 if (user
->handle_info
->opserv_level
< nickserv_conf
.email_search_level
) {
3188 send_message(user
, nickserv
, "MSG_NO_SEARCH_ACCESS", "email");
3190 } else if (!irccasecmp(argv
[++i
], "*")) {
3191 discrim
->emailmask
= 0;
3193 discrim
->emailmask
= argv
[i
];
3195 } else if (!irccasecmp(argv
[i
], "access")) {
3196 const char *cmp
= argv
[++i
];
3197 if (cmp
[0] == '<') {
3198 if (discrim
->min_level
== 0) discrim
->min_level
= 1;
3199 if (cmp
[1] == '=') {
3200 discrim
->max_level
= strtoul(cmp
+2, NULL
, 0);
3202 discrim
->max_level
= strtoul(cmp
+1, NULL
, 0) - 1;
3204 } else if (cmp
[0] == '=') {
3205 discrim
->min_level
= discrim
->max_level
= strtoul(cmp
+1, NULL
, 0);
3206 } else if (cmp
[0] == '>') {
3207 if (cmp
[1] == '=') {
3208 discrim
->min_level
= strtoul(cmp
+2, NULL
, 0);
3210 discrim
->min_level
= strtoul(cmp
+1, NULL
, 0) + 1;
3213 send_message(user
, nickserv
, "MSG_INVALID_CRITERIA", cmp
);
3216 send_message(user
, nickserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
3227 nickserv_discrim_match(struct nickserv_discrim
*discrim
, struct handle_info
*hi
)
3229 if (((discrim
->flags_on
& hi
->flags
) != discrim
->flags_on
)
3230 || (discrim
->flags_off
& hi
->flags
)
3231 || (discrim
->min_registered
> hi
->registered
)
3232 || (discrim
->max_registered
< hi
->registered
)
3233 || (discrim
->lastseen
< (hi
->users
?now
:hi
->lastseen
))
3234 || (discrim
->handlemask
&& !match_ircglob(hi
->handle
, discrim
->handlemask
))
3235 || (discrim
->emailmask
&& (!hi
->email_addr
|| !match_ircglob(hi
->email_addr
, discrim
->emailmask
)))
3236 || (discrim
->min_level
> hi
->opserv_level
)
3237 || (discrim
->max_level
< hi
->opserv_level
)) {
3240 if (discrim
->hostmask
) {
3242 for (i
=0; i
<hi
->masks
->used
; i
++) {
3243 const char *mask
= hi
->masks
->list
[i
];
3244 if ((discrim
->hostmask_type
== SUBSET
)
3245 && (match_ircglobs(discrim
->hostmask
, mask
))) break;
3246 else if ((discrim
->hostmask_type
== EXACT
)
3247 && !irccasecmp(discrim
->hostmask
, mask
)) break;
3248 else if ((discrim
->hostmask_type
== SUPERSET
)
3249 && (match_ircglobs(mask
, discrim
->hostmask
))) break;
3250 else if ((discrim
->hostmask_type
== LASTQUIT
)
3251 && (match_ircglobs(discrim
->hostmask
, hi
->last_quit_host
))) break;
3253 if (i
==hi
->masks
->used
) return 0;
3255 if (discrim
->nickmask
) {
3256 struct nick_info
*nick
= hi
->nicks
;
3258 if (match_ircglob(nick
->nick
, discrim
->nickmask
)) break;
3261 if (!nick
) return 0;
3267 nickserv_discrim_search(struct nickserv_discrim
*discrim
, discrim_search_func dsf
, struct userNode
*source
)
3269 dict_iterator_t it
, next
;
3270 unsigned int matched
;
3272 for (it
= dict_first(nickserv_handle_dict
), matched
= 0;
3273 it
&& (matched
< discrim
->limit
);
3275 next
= iter_next(it
);
3276 if (nickserv_discrim_match(discrim
, iter_data(it
))) {
3277 dsf(source
, iter_data(it
));
3285 search_print_func(struct userNode
*source
, struct handle_info
*match
)
3287 send_message(source
, nickserv
, "NSMSG_SEARCH_MATCH", match
->handle
);
3291 search_count_func(UNUSED_ARG(struct userNode
*source
), UNUSED_ARG(struct handle_info
*match
))
3296 search_unregister_func (struct userNode
*source
, struct handle_info
*match
)
3298 if (oper_has_access(source
, nickserv
, match
->opserv_level
, 0))
3299 nickserv_unregister_handle(match
, source
);
3303 nickserv_sort_accounts_by_access(const void *a
, const void *b
)
3305 const struct handle_info
*hi_a
= *(const struct handle_info
**)a
;
3306 const struct handle_info
*hi_b
= *(const struct handle_info
**)b
;
3307 if (hi_a
->opserv_level
!= hi_b
->opserv_level
)
3308 return hi_b
->opserv_level
- hi_a
->opserv_level
;
3309 return irccasecmp(hi_a
->handle
, hi_b
->handle
);
3313 nickserv_show_oper_accounts(struct userNode
*user
, struct svccmd
*cmd
)
3315 struct handle_info_list hil
;
3316 struct helpfile_table tbl
;
3321 memset(&hil
, 0, sizeof(hil
));
3322 for (it
= dict_first(nickserv_handle_dict
); it
; it
= iter_next(it
)) {
3323 struct handle_info
*hi
= iter_data(it
);
3324 if (hi
->opserv_level
)
3325 handle_info_list_append(&hil
, hi
);
3327 qsort(hil
.list
, hil
.used
, sizeof(hil
.list
[0]), nickserv_sort_accounts_by_access
);
3328 tbl
.length
= hil
.used
+ 1;
3330 tbl
.flags
= TABLE_NO_FREE
;
3331 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
3332 tbl
.contents
[0] = ary
= malloc(tbl
.width
* sizeof(ary
[0]));
3335 for (ii
= 0; ii
< hil
.used
; ) {
3336 ary
= malloc(tbl
.width
* sizeof(ary
[0]));
3337 ary
[0] = hil
.list
[ii
]->handle
;
3338 ary
[1] = strtab(hil
.list
[ii
]->opserv_level
);
3339 tbl
.contents
[++ii
] = ary
;
3341 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3342 reply("MSG_MATCH_COUNT", hil
.used
);
3343 for (ii
= 0; ii
< hil
.used
; ii
++)
3344 free(tbl
.contents
[ii
]);
3349 static NICKSERV_FUNC(cmd_search
)
3351 struct nickserv_discrim
*discrim
;
3352 discrim_search_func action
;
3353 struct svccmd
*subcmd
;
3354 unsigned int matches
;
3357 NICKSERV_MIN_PARMS(3);
3358 sprintf(buf
, "search %s", argv
[1]);
3359 subcmd
= dict_find(nickserv_service
->commands
, buf
, NULL
);
3360 if (!irccasecmp(argv
[1], "print"))
3361 action
= search_print_func
;
3362 else if (!irccasecmp(argv
[1], "count"))
3363 action
= search_count_func
;
3364 else if (!irccasecmp(argv
[1], "unregister"))
3365 action
= search_unregister_func
;
3367 reply("NSMSG_INVALID_ACTION", argv
[1]);
3371 if (subcmd
&& !svccmd_can_invoke(user
, nickserv
, subcmd
, NULL
, SVCCMD_NOISY
))
3374 discrim
= nickserv_discrim_create(user
, argc
-2, argv
+2);
3378 if (action
== search_print_func
)
3379 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3380 else if (action
== search_count_func
)
3381 discrim
->limit
= INT_MAX
;
3383 matches
= nickserv_discrim_search(discrim
, action
, user
);
3386 reply("MSG_MATCH_COUNT", matches
);
3388 reply("MSG_NO_MATCHES");
3394 static MODCMD_FUNC(cmd_checkpass
)
3396 struct handle_info
*hi
;
3398 NICKSERV_MIN_PARMS(3);
3399 if (!(hi
= get_handle_info(argv
[1]))) {
3400 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
3403 if (checkpass(argv
[2], hi
->passwd
))
3404 reply("CHECKPASS_YES");
3406 reply("CHECKPASS_NO");
3412 nickserv_db_read_handle(const char *handle
, dict_t obj
)
3415 struct string_list
*masks
, *slist
;
3416 struct handle_info
*hi
;
3417 struct userNode
*authed_users
;
3418 unsigned long int id
;
3422 str
= database_get_data(obj
, KEY_ID
, RECDB_QSTRING
);
3423 id
= str
? strtoul(str
, NULL
, 0) : 0;
3424 str
= database_get_data(obj
, KEY_PASSWD
, RECDB_QSTRING
);
3426 log_module(NS_LOG
, LOG_WARNING
, "did not find a password for %s -- skipping user.", handle
);
3429 if ((hi
= get_handle_info(handle
))) {
3430 authed_users
= hi
->users
;
3432 dict_remove(nickserv_handle_dict
, hi
->handle
);
3434 authed_users
= NULL
;
3436 hi
= register_handle(handle
, str
, id
);
3438 hi
->users
= authed_users
;
3439 while (authed_users
) {
3440 authed_users
->handle_info
= hi
;
3441 authed_users
= authed_users
->next_authed
;
3444 masks
= database_get_data(obj
, KEY_MASKS
, RECDB_STRING_LIST
);
3445 hi
->masks
= masks
? string_list_copy(masks
) : alloc_string_list(1);
3446 str
= database_get_data(obj
, KEY_MAXLOGINS
, RECDB_QSTRING
);
3447 hi
->maxlogins
= str
? strtoul(str
, NULL
, 0) : 0;
3448 str
= database_get_data(obj
, KEY_LANGUAGE
, RECDB_QSTRING
);
3449 hi
->language
= language_find(str
? str
: "C");
3450 str
= database_get_data(obj
, KEY_OPSERV_LEVEL
, RECDB_QSTRING
);
3451 hi
->opserv_level
= str
? strtoul(str
, NULL
, 0) : 0;
3452 str
= database_get_data(obj
, KEY_INFO
, RECDB_QSTRING
);
3454 hi
->infoline
= strdup(str
);
3455 str
= database_get_data(obj
, KEY_REGISTER_ON
, RECDB_QSTRING
);
3456 hi
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
3457 str
= database_get_data(obj
, KEY_LAST_SEEN
, RECDB_QSTRING
);
3458 hi
->lastseen
= str
? (time_t)strtoul(str
, NULL
, 0) : hi
->registered
;
3459 /* We want to read the nicks even if disable_nicks is set. This is so
3460 * that we don't lose the nick data entirely. */
3461 slist
= database_get_data(obj
, KEY_NICKS
, RECDB_STRING_LIST
);
3463 for (ii
=0; ii
<slist
->used
; ii
++)
3464 register_nick(slist
->list
[ii
], hi
);
3466 str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
);
3468 for (ii
=0; str
[ii
]; ii
++)
3469 hi
->flags
|= 1 << (handle_inverse_flags
[(unsigned char)str
[ii
]] - 1);
3471 str
= database_get_data(obj
, KEY_USERLIST_STYLE
, RECDB_QSTRING
);
3472 hi
->userlist_style
= str
? str
[0] : HI_STYLE_ZOOT
;
3473 str
= database_get_data(obj
, KEY_ANNOUNCEMENTS
, RECDB_QSTRING
);
3474 hi
->announcements
= str
? str
[0] : '?';
3475 str
= database_get_data(obj
, KEY_SCREEN_WIDTH
, RECDB_QSTRING
);
3476 hi
->screen_width
= str
? strtoul(str
, NULL
, 0) : 0;
3477 str
= database_get_data(obj
, KEY_TABLE_WIDTH
, RECDB_QSTRING
);
3478 hi
->table_width
= str
? strtoul(str
, NULL
, 0) : 0;
3479 str
= database_get_data(obj
, KEY_LAST_QUIT_HOST
, RECDB_QSTRING
);
3481 str
= database_get_data(obj
, KEY_LAST_AUTHED_HOST
, RECDB_QSTRING
);
3483 safestrncpy(hi
->last_quit_host
, str
, sizeof(hi
->last_quit_host
));
3484 str
= database_get_data(obj
, KEY_EMAIL_ADDR
, RECDB_QSTRING
);
3486 nickserv_set_email_addr(hi
, str
);
3487 str
= database_get_data(obj
, KEY_EPITHET
, RECDB_QSTRING
);
3489 hi
->epithet
= strdup(str
);
3490 str
= database_get_data(obj
, KEY_FAKEHOST
, RECDB_QSTRING
);
3492 hi
->fakehost
= strdup(str
);
3493 subdb
= database_get_data(obj
, KEY_COOKIE
, RECDB_OBJECT
);
3495 const char *data
, *type
, *expires
, *cookie_str
;
3496 struct handle_cookie
*cookie
;
3498 cookie
= calloc(1, sizeof(*cookie
));
3499 type
= database_get_data(subdb
, KEY_COOKIE_TYPE
, RECDB_QSTRING
);
3500 data
= database_get_data(subdb
, KEY_COOKIE_DATA
, RECDB_QSTRING
);
3501 expires
= database_get_data(subdb
, KEY_COOKIE_EXPIRES
, RECDB_QSTRING
);
3502 cookie_str
= database_get_data(subdb
, KEY_COOKIE
, RECDB_QSTRING
);
3503 if (!type
|| !expires
|| !cookie_str
) {
3504 log_module(NS_LOG
, LOG_ERROR
, "Missing field(s) from cookie for account %s; dropping cookie.", hi
->handle
);
3507 if (!irccasecmp(type
, KEY_ACTIVATION
))
3508 cookie
->type
= ACTIVATION
;
3509 else if (!irccasecmp(type
, KEY_PASSWORD_CHANGE
))
3510 cookie
->type
= PASSWORD_CHANGE
;
3511 else if (!irccasecmp(type
, KEY_EMAIL_CHANGE
))
3512 cookie
->type
= EMAIL_CHANGE
;
3513 else if (!irccasecmp(type
, KEY_ALLOWAUTH
))
3514 cookie
->type
= ALLOWAUTH
;
3516 log_module(NS_LOG
, LOG_ERROR
, "Invalid cookie type %s for account %s; dropping cookie.", type
, handle
);
3519 cookie
->expires
= strtoul(expires
, NULL
, 0);
3520 if (cookie
->expires
< now
)
3523 cookie
->data
= strdup(data
);
3524 safestrncpy(cookie
->cookie
, cookie_str
, sizeof(cookie
->cookie
));
3528 nickserv_bake_cookie(cookie
);
3530 nickserv_free_cookie(cookie
);
3535 nickserv_saxdb_read(dict_t db
) {
3537 struct record_data
*rd
;
3539 for (it
=dict_first(db
); it
; it
=iter_next(it
)) {
3541 nickserv_db_read_handle(iter_key(it
), rd
->d
.object
);
3546 static NICKSERV_FUNC(cmd_mergedb
)
3548 struct timeval start
, stop
;
3551 NICKSERV_MIN_PARMS(2);
3552 gettimeofday(&start
, NULL
);
3553 if (!(db
= parse_database(argv
[1]))) {
3554 reply("NSMSG_DB_UNREADABLE", argv
[1]);
3557 nickserv_saxdb_read(db
);
3559 gettimeofday(&stop
, NULL
);
3560 stop
.tv_sec
-= start
.tv_sec
;
3561 stop
.tv_usec
-= start
.tv_usec
;
3562 if (stop
.tv_usec
< 0) {
3564 stop
.tv_usec
+= 1000000;
3566 reply("NSMSG_DB_MERGED", argv
[1], stop
.tv_sec
, stop
.tv_usec
/1000);
3571 expire_handles(UNUSED_ARG(void *data
))
3573 dict_iterator_t it
, next
;
3575 struct handle_info
*hi
;
3577 for (it
=dict_first(nickserv_handle_dict
); it
; it
=next
) {
3578 next
= iter_next(it
);
3580 if ((hi
->opserv_level
> 0)
3582 || HANDLE_FLAGGED(hi
, FROZEN
)
3583 || HANDLE_FLAGGED(hi
, NODELETE
)) {
3586 expiry
= hi
->channels
? nickserv_conf
.handle_expire_delay
: nickserv_conf
.nochan_handle_expire_delay
;
3587 if ((now
- hi
->lastseen
) > expiry
) {
3588 log_module(NS_LOG
, LOG_INFO
, "Expiring account %s for inactivity.", hi
->handle
);
3589 nickserv_unregister_handle(hi
, NULL
);
3593 if (nickserv_conf
.handle_expire_frequency
)
3594 timeq_add(now
+ nickserv_conf
.handle_expire_frequency
, expire_handles
, NULL
);
3598 nickserv_load_dict(const char *fname
)
3602 if (!(file
= fopen(fname
, "r"))) {
3603 log_module(NS_LOG
, LOG_ERROR
, "Unable to open dictionary file %s: %s", fname
, strerror(errno
));
3606 while (!feof(file
)) {
3607 fgets(line
, sizeof(line
), file
);
3610 if (line
[strlen(line
)-1] == '\n')
3611 line
[strlen(line
)-1] = 0;
3612 dict_insert(nickserv_conf
.weak_password_dict
, strdup(line
), NULL
);
3615 log_module(NS_LOG
, LOG_INFO
, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf
.weak_password_dict
));
3618 static enum reclaim_action
3619 reclaim_action_from_string(const char *str
) {
3621 return RECLAIM_NONE
;
3622 else if (!irccasecmp(str
, "warn"))
3623 return RECLAIM_WARN
;
3624 else if (!irccasecmp(str
, "svsnick"))
3625 return RECLAIM_SVSNICK
;
3626 else if (!irccasecmp(str
, "kill"))
3627 return RECLAIM_KILL
;
3629 return RECLAIM_NONE
;
3633 nickserv_conf_read(void)
3635 dict_t conf_node
, child
;
3639 if (!(conf_node
= conf_get_data(NICKSERV_CONF_NAME
, RECDB_OBJECT
))) {
3640 log_module(NS_LOG
, LOG_ERROR
, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME
);
3643 str
= database_get_data(conf_node
, KEY_VALID_HANDLE_REGEX
, RECDB_QSTRING
);
3645 str
= database_get_data(conf_node
, KEY_VALID_ACCOUNT_REGEX
, RECDB_QSTRING
);
3646 if (nickserv_conf
.valid_handle_regex_set
)
3647 regfree(&nickserv_conf
.valid_handle_regex
);
3649 int err
= regcomp(&nickserv_conf
.valid_handle_regex
, str
, REG_EXTENDED
|REG_ICASE
|REG_NOSUB
);
3650 nickserv_conf
.valid_handle_regex_set
= !err
;
3651 if (err
) log_module(NS_LOG
, LOG_ERROR
, "Bad valid_account_regex (error %d)", err
);
3653 nickserv_conf
.valid_handle_regex_set
= 0;
3655 str
= database_get_data(conf_node
, KEY_VALID_NICK_REGEX
, RECDB_QSTRING
);
3656 if (nickserv_conf
.valid_nick_regex_set
)
3657 regfree(&nickserv_conf
.valid_nick_regex
);
3659 int err
= regcomp(&nickserv_conf
.valid_nick_regex
, str
, REG_EXTENDED
|REG_ICASE
|REG_NOSUB
);
3660 nickserv_conf
.valid_nick_regex_set
= !err
;
3661 if (err
) log_module(NS_LOG
, LOG_ERROR
, "Bad valid_nick_regex (error %d)", err
);
3663 nickserv_conf
.valid_nick_regex_set
= 0;
3665 str
= database_get_data(conf_node
, KEY_NICKS_PER_HANDLE
, RECDB_QSTRING
);
3667 str
= database_get_data(conf_node
, KEY_NICKS_PER_ACCOUNT
, RECDB_QSTRING
);
3668 nickserv_conf
.nicks_per_handle
= str
? strtoul(str
, NULL
, 0) : 4;
3669 str
= database_get_data(conf_node
, KEY_DISABLE_NICKS
, RECDB_QSTRING
);
3670 nickserv_conf
.disable_nicks
= str
? strtoul(str
, NULL
, 0) : 0;
3671 str
= database_get_data(conf_node
, KEY_DEFAULT_HOSTMASK
, RECDB_QSTRING
);
3672 nickserv_conf
.default_hostmask
= str
? !disabled_string(str
) : 0;
3673 str
= database_get_data(conf_node
, KEY_PASSWORD_MIN_LENGTH
, RECDB_QSTRING
);
3674 nickserv_conf
.password_min_length
= str
? strtoul(str
, NULL
, 0) : 0;
3675 str
= database_get_data(conf_node
, KEY_PASSWORD_MIN_DIGITS
, RECDB_QSTRING
);
3676 nickserv_conf
.password_min_digits
= str
? strtoul(str
, NULL
, 0) : 0;
3677 str
= database_get_data(conf_node
, KEY_PASSWORD_MIN_UPPER
, RECDB_QSTRING
);
3678 nickserv_conf
.password_min_upper
= str
? strtoul(str
, NULL
, 0) : 0;
3679 str
= database_get_data(conf_node
, KEY_PASSWORD_MIN_LOWER
, RECDB_QSTRING
);
3680 nickserv_conf
.password_min_lower
= str
? strtoul(str
, NULL
, 0) : 0;
3681 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
3682 nickserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
3683 str
= database_get_data(conf_node
, KEY_MODOPER_LEVEL
, RECDB_QSTRING
);
3684 nickserv_conf
.modoper_level
= str
? strtoul(str
, NULL
, 0) : 900;
3685 str
= database_get_data(conf_node
, KEY_SET_EPITHET_LEVEL
, RECDB_QSTRING
);
3686 nickserv_conf
.set_epithet_level
= str
? strtoul(str
, NULL
, 0) : 1;
3687 str
= database_get_data(conf_node
, KEY_SET_TITLE_LEVEL
, RECDB_QSTRING
);
3688 nickserv_conf
.set_title_level
= str
? strtoul(str
, NULL
, 0) : 900;
3689 str
= database_get_data(conf_node
, KEY_SET_FAKEHOST_LEVEL
, RECDB_QSTRING
);
3690 nickserv_conf
.set_fakehost_level
= str
? strtoul(str
, NULL
, 0) : 1000;
3691 str
= database_get_data(conf_node
, KEY_HANDLE_EXPIRE_FREQ
, RECDB_QSTRING
);
3693 str
= database_get_data(conf_node
, KEY_ACCOUNT_EXPIRE_FREQ
, RECDB_QSTRING
);
3694 nickserv_conf
.handle_expire_frequency
= str
? ParseInterval(str
) : 86400;
3695 str
= database_get_data(conf_node
, KEY_HANDLE_EXPIRE_DELAY
, RECDB_QSTRING
);
3697 str
= database_get_data(conf_node
, KEY_ACCOUNT_EXPIRE_DELAY
, RECDB_QSTRING
);
3698 nickserv_conf
.handle_expire_delay
= str
? ParseInterval(str
) : 86400*30;
3699 str
= database_get_data(conf_node
, KEY_NOCHAN_HANDLE_EXPIRE_DELAY
, RECDB_QSTRING
);
3701 str
= database_get_data(conf_node
, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY
, RECDB_QSTRING
);
3702 nickserv_conf
.nochan_handle_expire_delay
= str
? ParseInterval(str
) : 86400*15;
3703 str
= database_get_data(conf_node
, "warn_clone_auth", RECDB_QSTRING
);
3704 nickserv_conf
.warn_clone_auth
= str
? !disabled_string(str
) : 1;
3705 str
= database_get_data(conf_node
, "default_maxlogins", RECDB_QSTRING
);
3706 nickserv_conf
.default_maxlogins
= str
? strtoul(str
, NULL
, 0) : 2;
3707 str
= database_get_data(conf_node
, "hard_maxlogins", RECDB_QSTRING
);
3708 nickserv_conf
.hard_maxlogins
= str
? strtoul(str
, NULL
, 0) : 10;
3709 if (!nickserv_conf
.disable_nicks
) {
3710 str
= database_get_data(conf_node
, "reclaim_action", RECDB_QSTRING
);
3711 nickserv_conf
.reclaim_action
= str
? reclaim_action_from_string(str
) : RECLAIM_NONE
;
3712 str
= database_get_data(conf_node
, "warn_nick_owned", RECDB_QSTRING
);
3713 nickserv_conf
.warn_nick_owned
= str
? enabled_string(str
) : 0;
3714 str
= database_get_data(conf_node
, "auto_reclaim_action", RECDB_QSTRING
);
3715 nickserv_conf
.auto_reclaim_action
= str
? reclaim_action_from_string(str
) : RECLAIM_NONE
;
3716 str
= database_get_data(conf_node
, "auto_reclaim_delay", RECDB_QSTRING
);
3717 nickserv_conf
.auto_reclaim_delay
= str
? ParseInterval(str
) : 0;
3719 child
= database_get_data(conf_node
, KEY_FLAG_LEVELS
, RECDB_OBJECT
);
3720 for (it
=dict_first(child
); it
; it
=iter_next(it
)) {
3721 const char *key
= iter_key(it
), *value
;
3725 if (!strncasecmp(key
, "uc_", 3))
3726 flag
= toupper(key
[3]);
3727 else if (!strncasecmp(key
, "lc_", 3))
3728 flag
= tolower(key
[3]);
3732 if ((pos
= handle_inverse_flags
[flag
])) {
3733 value
= GET_RECORD_QSTRING((struct record_data
*)iter_data(it
));
3734 flag_access_levels
[pos
- 1] = strtoul(value
, NULL
, 0);
3737 if (nickserv_conf
.weak_password_dict
)
3738 dict_delete(nickserv_conf
.weak_password_dict
);
3739 nickserv_conf
.weak_password_dict
= dict_new();
3740 dict_set_free_keys(nickserv_conf
.weak_password_dict
, free
);
3741 dict_insert(nickserv_conf
.weak_password_dict
, strdup("password"), NULL
);
3742 dict_insert(nickserv_conf
.weak_password_dict
, strdup("<password>"), NULL
);
3743 str
= database_get_data(conf_node
, KEY_DICT_FILE
, RECDB_QSTRING
);
3745 nickserv_load_dict(str
);
3746 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
3747 if (nickserv
&& str
)
3748 NickChange(nickserv
, str
, 0);
3749 str
= database_get_data(conf_node
, KEY_AUTOGAG_ENABLED
, RECDB_QSTRING
);
3750 nickserv_conf
.autogag_enabled
= str
? strtoul(str
, NULL
, 0) : 1;
3751 str
= database_get_data(conf_node
, KEY_AUTOGAG_DURATION
, RECDB_QSTRING
);
3752 nickserv_conf
.autogag_duration
= str
? ParseInterval(str
) : 1800;
3753 str
= database_get_data(conf_node
, KEY_EMAIL_VISIBLE_LEVEL
, RECDB_QSTRING
);
3754 nickserv_conf
.email_visible_level
= str
? strtoul(str
, NULL
, 0) : 800;
3755 str
= database_get_data(conf_node
, KEY_EMAIL_ENABLED
, RECDB_QSTRING
);
3756 nickserv_conf
.email_enabled
= str
? enabled_string(str
) : 0;
3757 str
= database_get_data(conf_node
, KEY_SYNC_LOG
, RECDB_QSTRING
);
3758 nickserv_conf
.sync_log
= str
? enabled_string(str
) : 0;
3759 str
= database_get_data(conf_node
, KEY_COOKIE_TIMEOUT
, RECDB_QSTRING
);
3760 nickserv_conf
.cookie_timeout
= str
? ParseInterval(str
) : 24*3600;
3761 str
= database_get_data(conf_node
, KEY_EMAIL_REQUIRED
, RECDB_QSTRING
);
3762 nickserv_conf
.email_required
= (nickserv_conf
.email_enabled
&& str
) ? enabled_string(str
) : 0;
3763 str
= database_get_data(conf_node
, KEY_ACCOUNTS_PER_EMAIL
, RECDB_QSTRING
);
3764 nickserv_conf
.handles_per_email
= str
? strtoul(str
, NULL
, 0) : 1;
3765 str
= database_get_data(conf_node
, KEY_EMAIL_SEARCH_LEVEL
, RECDB_QSTRING
);
3766 nickserv_conf
.email_search_level
= str
? strtoul(str
, NULL
, 0) : 600;
3767 str
= database_get_data(conf_node
, KEY_TITLEHOST_SUFFIX
, RECDB_QSTRING
);
3768 nickserv_conf
.titlehost_suffix
= str
? str
: "example.net";
3769 str
= conf_get_data("server/network", RECDB_QSTRING
);
3770 nickserv_conf
.network_name
= str
? str
: "some IRC network";
3771 if (!nickserv_conf
.auth_policer_params
) {
3772 nickserv_conf
.auth_policer_params
= policer_params_new();
3773 policer_params_set(nickserv_conf
.auth_policer_params
, "size", "5");
3774 policer_params_set(nickserv_conf
.auth_policer_params
, "drain-rate", "0.05");
3776 child
= database_get_data(conf_node
, KEY_AUTH_POLICER
, RECDB_OBJECT
);
3777 for (it
=dict_first(child
); it
; it
=iter_next(it
))
3778 set_policer_param(iter_key(it
), iter_data(it
), nickserv_conf
.auth_policer_params
);
3782 nickserv_reclaim(struct userNode
*user
, struct nick_info
*ni
, enum reclaim_action action
) {
3784 char newnick
[NICKLEN
+1];
3793 send_message(user
, nickserv
, "NSMSG_RECLAIM_WARN", ni
->nick
, ni
->owner
->handle
);
3795 case RECLAIM_SVSNICK
:
3797 snprintf(newnick
, sizeof(newnick
), "Guest%d", rand()%10000
);
3798 } while (GetUserH(newnick
));
3799 irc_svsnick(nickserv
, user
, newnick
);
3802 msg
= user_find_message(user
, "NSMSG_RECLAIM_KILL");
3803 irc_kill(nickserv
, user
, msg
);
3809 nickserv_reclaim_p(void *data
) {
3810 struct userNode
*user
= data
;
3811 struct nick_info
*ni
= get_nick_info(user
->nick
);
3813 nickserv_reclaim(user
, ni
, nickserv_conf
.auto_reclaim_action
);
3817 check_user_nick(struct userNode
*user
) {
3818 struct nick_info
*ni
;
3819 user
->modes
&= ~FLAGS_REGNICK
;
3820 if (!(ni
= get_nick_info(user
->nick
)))
3822 if (user
->handle_info
== ni
->owner
) {
3823 user
->modes
|= FLAGS_REGNICK
;
3827 if (nickserv_conf
.warn_nick_owned
)
3828 send_message(user
, nickserv
, "NSMSG_RECLAIM_WARN", ni
->nick
, ni
->owner
->handle
);
3829 if (nickserv_conf
.auto_reclaim_action
== RECLAIM_NONE
)
3831 if (nickserv_conf
.auto_reclaim_delay
)
3832 timeq_add(now
+ nickserv_conf
.auto_reclaim_delay
, nickserv_reclaim_p
, user
);
3834 nickserv_reclaim(user
, ni
, nickserv_conf
.auto_reclaim_action
);
3839 handle_new_user(struct userNode
*user
)
3841 return check_user_nick(user
);
3845 handle_account(struct userNode
*user
, const char *stamp
)
3847 struct handle_info
*hi
;
3850 #ifdef WITH_PROTOCOL_P10
3851 time_t timestamp
= 0;
3853 colon
= strchr(stamp
, ':');
3854 if(colon
&& colon
[1])
3857 timestamp
= atoi(colon
+1);
3859 hi
= dict_find(nickserv_handle_dict
, stamp
, NULL
);
3860 if(hi
&& timestamp
&& hi
->registered
!= timestamp
)
3862 log_module(MAIN_LOG
, LOG_WARNING
, "%s using account %s but timestamp does not match %lu is not %lu.", user
->nick
, stamp
, timestamp
, hi
->registered
);
3866 hi
= dict_find(nickserv_id_dict
, stamp
, NULL
);
3867 log_module(MAIN_LOG
, LOG_WARNING
, "Using non-P10 code in accounts, not tested at all!");
3871 if (HANDLE_FLAGGED(hi
, SUSPENDED
)) {
3874 set_user_handle_info(user
, hi
, 0);
3876 log_module(MAIN_LOG
, LOG_WARNING
, "%s had unknown account stamp %s.", user
->nick
, stamp
);
3881 handle_nick_change(struct userNode
*user
, const char *old_nick
)
3883 struct handle_info
*hi
;
3885 if ((hi
= dict_find(nickserv_allow_auth_dict
, old_nick
, 0))) {
3886 dict_remove(nickserv_allow_auth_dict
, old_nick
);
3887 dict_insert(nickserv_allow_auth_dict
, user
->nick
, hi
);
3889 timeq_del(0, nickserv_reclaim_p
, user
, TIMEQ_IGNORE_WHEN
);
3890 check_user_nick(user
);
3894 nickserv_remove_user(struct userNode
*user
, UNUSED_ARG(struct userNode
*killer
), UNUSED_ARG(const char *why
))
3896 dict_remove(nickserv_allow_auth_dict
, user
->nick
);
3897 timeq_del(0, nickserv_reclaim_p
, user
, TIMEQ_IGNORE_WHEN
);
3898 set_user_handle_info(user
, NULL
, 0);
3901 static struct modcmd
*
3902 nickserv_define_func(const char *name
, modcmd_func_t func
, int min_level
, int must_auth
, int must_be_qualified
)
3904 if (min_level
> 0) {
3906 sprintf(buf
, "%u", min_level
);
3907 if (must_be_qualified
) {
3908 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), "level", buf
, "flags", "+qualified,+loghostmask", NULL
);
3910 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), "level", buf
, NULL
);
3912 } else if (min_level
== 0) {
3913 if (must_be_qualified
) {
3914 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), "flags", "+helping", NULL
);
3916 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), "flags", "+helping", NULL
);
3919 if (must_be_qualified
) {
3920 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), "flags", "+qualified,+loghostmask", NULL
);
3922 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), NULL
);
3928 nickserv_db_cleanup(void)
3930 unreg_del_user_func(nickserv_remove_user
);
3931 userList_clean(&curr_helpers
);
3932 policer_params_delete(nickserv_conf
.auth_policer_params
);
3933 dict_delete(nickserv_handle_dict
);
3934 dict_delete(nickserv_nick_dict
);
3935 dict_delete(nickserv_opt_dict
);
3936 dict_delete(nickserv_allow_auth_dict
);
3937 dict_delete(nickserv_email_dict
);
3938 dict_delete(nickserv_id_dict
);
3939 dict_delete(nickserv_conf
.weak_password_dict
);
3940 free(auth_func_list
);
3941 free(unreg_func_list
);
3943 free(allowauth_func_list
);
3944 free(handle_merge_func_list
);
3945 free(failpw_func_list
);
3946 if (nickserv_conf
.valid_handle_regex_set
)
3947 regfree(&nickserv_conf
.valid_handle_regex
);
3948 if (nickserv_conf
.valid_nick_regex_set
)
3949 regfree(&nickserv_conf
.valid_nick_regex
);
3953 init_nickserv(const char *nick
)
3956 NS_LOG
= log_register_type("NickServ", "file:nickserv.log");
3957 reg_new_user_func(handle_new_user
);
3958 reg_nick_change_func(handle_nick_change
);
3959 reg_del_user_func(nickserv_remove_user
);
3960 reg_account_func(handle_account
);
3962 /* set up handle_inverse_flags */
3963 memset(handle_inverse_flags
, 0, sizeof(handle_inverse_flags
));
3964 for (i
=0; handle_flags
[i
]; i
++) {
3965 handle_inverse_flags
[(unsigned char)handle_flags
[i
]] = i
+ 1;
3966 flag_access_levels
[i
] = 0;
3969 conf_register_reload(nickserv_conf_read
);
3970 nickserv_opt_dict
= dict_new();
3971 nickserv_email_dict
= dict_new();
3972 dict_set_free_keys(nickserv_email_dict
, free
);
3973 dict_set_free_data(nickserv_email_dict
, nickserv_free_email_addr
);
3975 nickserv_module
= module_register("NickServ", NS_LOG
, "nickserv.help", NULL
);
3976 /* Removed qualified_host as default requirement for AUTH, REGISTER, PASS, etc. nets
3977 * can enable it per command using modcmd. (its a shitty default IMO, and now in 1.3
3978 * a big pain to disable since its nolonger in the config file. ) -Rubin
3980 modcmd_register(nickserv_module
, "AUTH", cmd_auth
, 2, MODCMD_KEEP_BOUND
, "flags", "+loghostmask", NULL
);
3981 nickserv_define_func("ALLOWAUTH", cmd_allowauth
, 0, 1, 0);
3982 nickserv_define_func("REGISTER", cmd_register
, -1, 0, 0);
3983 nickserv_define_func("OREGISTER", cmd_oregister
, 0, 1, 0);
3984 nickserv_define_func("UNREGISTER", cmd_unregister
, -1, 1, 0);
3985 nickserv_define_func("OUNREGISTER", cmd_ounregister
, 0, 1, 0);
3986 nickserv_define_func("ADDMASK", cmd_addmask
, -1, 1, 0);
3987 nickserv_define_func("OADDMASK", cmd_oaddmask
, 0, 1, 0);
3988 nickserv_define_func("DELMASK", cmd_delmask
, -1, 1, 0);
3989 nickserv_define_func("ODELMASK", cmd_odelmask
, 0, 1, 0);
3990 nickserv_define_func("PASS", cmd_pass
, -1, 1, 0);
3991 nickserv_define_func("SET", cmd_set
, -1, 1, 0);
3992 nickserv_define_func("OSET", cmd_oset
, 0, 1, 0);
3993 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo
, -1, 0, 0);
3994 nickserv_define_func("USERINFO", cmd_userinfo
, -1, 1, 0);
3995 nickserv_define_func("RENAME", cmd_rename_handle
, -1, 1, 0);
3996 nickserv_define_func("VACATION", cmd_vacation
, -1, 1, 0);
3997 nickserv_define_func("MERGE", cmd_merge
, 0, 1, 0);
3998 if (!nickserv_conf
.disable_nicks
) {
3999 /* nick management commands */
4000 nickserv_define_func("REGNICK", cmd_regnick
, -1, 1, 0);
4001 nickserv_define_func("OREGNICK", cmd_oregnick
, 0, 1, 0);
4002 nickserv_define_func("UNREGNICK", cmd_unregnick
, -1, 1, 0);
4003 nickserv_define_func("OUNREGNICK", cmd_ounregnick
, 0, 1, 0);
4004 nickserv_define_func("NICKINFO", cmd_nickinfo
, -1, 1, 0);
4005 nickserv_define_func("RECLAIM", cmd_reclaim
, -1, 1, 0);
4007 if (nickserv_conf
.email_enabled
) {
4008 nickserv_define_func("AUTHCOOKIE", cmd_authcookie
, -1, 0, 0);
4009 nickserv_define_func("RESETPASS", cmd_resetpass
, -1, 0, 0);
4010 nickserv_define_func("COOKIE", cmd_cookie
, -1, 0, 0);
4011 nickserv_define_func("DELCOOKIE", cmd_delcookie
, -1, 1, 0);
4012 nickserv_define_func("ODELCOOKIE", cmd_odelcookie
, 0, 1, 0);
4013 dict_insert(nickserv_opt_dict
, "EMAIL", opt_email
);
4015 nickserv_define_func("GHOST", cmd_ghost
, -1, 1, 0);
4016 /* miscellaneous commands */
4017 nickserv_define_func("STATUS", cmd_status
, -1, 0, 0);
4018 nickserv_define_func("SEARCH", cmd_search
, 100, 1, 0);
4019 nickserv_define_func("SEARCH UNREGISTER", NULL
, 800, 1, 0);
4020 nickserv_define_func("MERGEDB", cmd_mergedb
, 999, 1, 0);
4021 nickserv_define_func("CHECKPASS", cmd_checkpass
, 601, 1, 0);
4023 dict_insert(nickserv_opt_dict
, "INFO", opt_info
);
4024 dict_insert(nickserv_opt_dict
, "WIDTH", opt_width
);
4025 dict_insert(nickserv_opt_dict
, "TABLEWIDTH", opt_tablewidth
);
4026 dict_insert(nickserv_opt_dict
, "COLOR", opt_color
);
4027 dict_insert(nickserv_opt_dict
, "PRIVMSG", opt_privmsg
);
4028 dict_insert(nickserv_opt_dict
, "STYLE", opt_style
);
4029 dict_insert(nickserv_opt_dict
, "PASS", opt_password
);
4030 dict_insert(nickserv_opt_dict
, "PASSWORD", opt_password
);
4031 dict_insert(nickserv_opt_dict
, "FLAGS", opt_flags
);
4032 dict_insert(nickserv_opt_dict
, "ACCESS", opt_level
);
4033 dict_insert(nickserv_opt_dict
, "LEVEL", opt_level
);
4034 dict_insert(nickserv_opt_dict
, "EPITHET", opt_epithet
);
4035 if (nickserv_conf
.titlehost_suffix
) {
4036 dict_insert(nickserv_opt_dict
, "TITLE", opt_title
);
4037 dict_insert(nickserv_opt_dict
, "FAKEHOST", opt_fakehost
);
4039 dict_insert(nickserv_opt_dict
, "ANNOUNCEMENTS", opt_announcements
);
4040 dict_insert(nickserv_opt_dict
, "MAXLOGINS", opt_maxlogins
);
4041 dict_insert(nickserv_opt_dict
, "LANGUAGE", opt_language
);
4043 nickserv_handle_dict
= dict_new();
4044 dict_set_free_keys(nickserv_handle_dict
, free
);
4045 dict_set_free_data(nickserv_handle_dict
, free_handle_info
);
4047 nickserv_id_dict
= dict_new();
4048 dict_set_free_keys(nickserv_id_dict
, free
);
4050 nickserv_nick_dict
= dict_new();
4051 dict_set_free_data(nickserv_nick_dict
, free
);
4053 nickserv_allow_auth_dict
= dict_new();
4055 userList_init(&curr_helpers
);
4058 const char *modes
= conf_get_data("services/nickserv/modes", RECDB_QSTRING
);
4059 nickserv
= AddService(nick
, modes
? modes
: NULL
, "Nick Services", NULL
);
4060 nickserv_service
= service_register(nickserv
);
4062 saxdb_register("NickServ", nickserv_saxdb_read
, nickserv_saxdb_write
);
4063 reg_exit_func(nickserv_db_cleanup
);
4064 if(nickserv_conf
.handle_expire_frequency
)
4065 timeq_add(now
+ nickserv_conf
.handle_expire_frequency
, expire_handles
, NULL
);
4066 message_register_table(msgtab
);