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", "$bAccount Information for %s$b" },
189 { "NSMSG_HANDLEINFO_END", "----------End of Account Info-----------" },
190 { "NSMSG_HANDLEINFO_ID", "Account ID: %lu" },
191 { "NSMSG_HANDLEINFO_REGGED", "Registered on: %s" },
192 { "NSMSG_HANDLEINFO_LASTSEEN", "Last seen: %s" },
193 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", "Last seen: Right now!" },
194 { "NSMSG_HANDLEINFO_VACATION", "On vacation." },
195 { "NSMSG_HANDLEINFO_EMAIL_ADDR", "Email address: %s" },
196 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", "Cookie: There is currently an activation cookie issued for this account" },
197 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", "Cookie: There is currently a password change cookie issued for this account" },
198 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", "Cookie: There is currently an email change cookie issued for this account" },
199 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", "Cookie: There is currently an allowauth cookie issued for this account" },
200 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", "Cookie: There is currently an unknown cookie issued for this account" },
201 { "NSMSG_HANDLEINFO_INFOLINE", "Infoline: %s" },
202 { "NSMSG_HANDLEINFO_FLAGS", "Flags: %s" },
203 { "NSMSG_HANDLEINFO_EPITHET", "Epithet: %s" },
204 { "NSMSG_HANDLEINFO_FAKEHOST", "Fake host: %s" },
205 { "NSMSG_HANDLEINFO_LAST_HOST", "Last quit hostmask: %s" },
206 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", "Last quit hostmask: Unknown" },
207 { "NSMSG_HANDLEINFO_NICKS", "Nickname(s): %s" },
208 { "NSMSG_HANDLEINFO_MASKS", "Hostmask(s): %s" },
209 { "NSMSG_HANDLEINFO_CHANNELS", "Channel(s): %s" },
210 { "NSMSG_HANDLEINFO_CURRENT", "Current nickname(s): %s" },
211 { "NSMSG_HANDLEINFO_DNR", "Do-not-register (by %s): %s" },
212 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
213 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
214 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
215 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
216 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
217 { "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)." },
218 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
219 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
220 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
221 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
222 { "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." },
223 { "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." },
224 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
225 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
226 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
227 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
228 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
229 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
230 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
231 { "NSMSG_PASS_SUCCESS", "Password changed." },
232 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
233 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
234 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
235 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
236 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
237 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
238 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
239 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
240 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
241 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
242 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
243 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
244 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
245 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
246 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
247 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
248 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
249 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
250 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
251 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
252 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
253 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
254 { "NSMSG_NO_ACCESS", "Access denied." },
255 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
256 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
257 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
258 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
259 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T
".%03lu seconds)." },
260 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
261 { "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." },
262 { "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." },
263 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
264 { "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." },
265 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
266 { "NSMSG_SEARCH_MATCH", "Match: %s" },
267 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
268 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
269 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
270 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
271 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
272 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
273 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
274 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
275 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
276 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
277 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
278 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
279 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
280 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
281 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
282 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
283 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
284 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
285 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
286 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
287 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
288 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
289 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
290 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
291 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
292 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
293 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
294 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
295 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
296 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
297 { "NSEMAIL_ACTIVATION_BODY",
298 "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"
300 "To verify your email address and complete the account registration, log on to %1$s and type the following command:\n"
301 "/msg %3$s@%4$s COOKIE %5$s %2$s\n"
302 "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"
303 "/msg %3$s@%4$s AUTH %5$s your-password\n"
304 "(Please remember to fill in 'your-password' with the actual password you gave to us when you registered.)\n"
305 "OR configure Login-On-Connect (see http://www.afternet.org/login-on-connect for instructions) to connect pre-logged in every time.\n"
307 "If you did NOT request this account, you do not need to do anything.\n"
308 "Please contact the %1$s staff if you have questions, and be sure to check our website." },
309 { "NSEMAIL_ACTIVATION_BODY_WEB",
310 "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"
312 "To verify your email address and complete the account registration, visit the following URL:\n"
313 "http://www.afternet.org/index.php?option=com_registration&task=activate&username=%5$s&cookie=%2$s\n"
315 "If you did NOT request this account, you do not need to do anything.\n"
316 "Please contact the %1$s staff if you have questions, and be sure to check our website." },
317 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
318 { "NSEMAIL_PASSWORD_CHANGE_BODY",
319 "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"
320 "To complete the password change, log on to %1$s and type the following command:\n"
321 "/msg %3$s@%4$s COOKIE %5$s %2$s\n"
322 "If you did NOT request your password to be changed, you do not need to do anything.\n"
323 "Please contact the %1$s staff if you have questions." },
324 { "NSEMAIL_PASSWORD_CHANGE_BODY_WEB",
325 "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"
326 "To complete the password change, click the following URL:\n"
327 "http://www.afternet.org/index.php?option=com_registration&task=passcookie&username=%5$s&cookie=%2$s\n"
328 "If you did NOT request your password to be changed, you do not need to do anything.\n"
329 "Please contact the %1$s staff if you have questions." },
330 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
331 #ifdef stupid_verify_old_email
332 { "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." },
333 { "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." },
335 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
336 { "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." },
337 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
338 { "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." },
339 { "CHECKPASS_YES", "Yes." },
340 { "CHECKPASS_NO", "No." },
344 enum reclaim_action
{
350 static void nickserv_reclaim(struct userNode
*user
, struct nick_info
*ni
, enum reclaim_action action
);
351 static void nickserv_reclaim_p(void *data
);
354 unsigned int disable_nicks
: 1;
355 unsigned int valid_handle_regex_set
: 1;
356 unsigned int valid_nick_regex_set
: 1;
357 unsigned int autogag_enabled
: 1;
358 unsigned int email_enabled
: 1;
359 unsigned int email_required
: 1;
360 unsigned int default_hostmask
: 1;
361 unsigned int warn_nick_owned
: 1;
362 unsigned int warn_clone_auth
: 1;
363 unsigned int sync_log
: 1;
364 unsigned long nicks_per_handle
;
365 unsigned long password_min_length
;
366 unsigned long password_min_digits
;
367 unsigned long password_min_upper
;
368 unsigned long password_min_lower
;
369 unsigned long db_backup_frequency
;
370 unsigned long handle_expire_frequency
;
371 unsigned long autogag_duration
;
372 unsigned long email_visible_level
;
373 unsigned long cookie_timeout
;
374 unsigned long handle_expire_delay
;
375 unsigned long nochan_handle_expire_delay
;
376 unsigned long modoper_level
;
377 unsigned long set_epithet_level
;
378 unsigned long set_title_level
;
379 unsigned long set_fakehost_level
;
380 unsigned long handles_per_email
;
381 unsigned long email_search_level
;
382 const char *network_name
;
383 const char *titlehost_suffix
;
384 regex_t valid_handle_regex
;
385 regex_t valid_nick_regex
;
386 dict_t weak_password_dict
;
387 struct policer_params
*auth_policer_params
;
388 enum reclaim_action reclaim_action
;
389 enum reclaim_action auto_reclaim_action
;
390 unsigned long auto_reclaim_delay
;
391 unsigned char default_maxlogins
;
392 unsigned char hard_maxlogins
;
395 /* We have 2^32 unique account IDs to use. */
396 unsigned long int highest_id
= 0;
399 canonicalize_hostmask(char *mask
)
401 char *out
= mask
, *temp
;
402 if ((temp
= strchr(mask
, '!'))) {
404 while (*temp
) *out
++ = *temp
++;
410 static struct handle_info
*
411 register_handle(const char *handle
, const char *passwd
, UNUSED_ARG(unsigned long id
))
413 struct handle_info
*hi
;
415 #ifdef WITH_PROTOCOL_BAHAMUT
416 char id_base64
[IDLEN
+ 1];
419 /* Assign a unique account ID to the account; note that 0 is
420 an invalid account ID. 1 is therefore the first account ID. */
422 id
= 1 + highest_id
++;
424 /* Note: highest_id is and must always be the highest ID. */
425 if(id
> highest_id
) {
429 inttobase64(id_base64
, id
, IDLEN
);
431 /* Make sure an account with the same ID doesn't exist. If a
432 duplicate is found, log some details and assign a new one.
433 This should be impossible, but it never hurts to expect it. */
434 if ((hi
= dict_find(nickserv_id_dict
, id_base64
, NULL
))) {
435 log_module(NS_LOG
, LOG_WARNING
, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id
, id_base64
, hi
->handle
, handle
);
441 hi
= calloc(1, sizeof(*hi
));
442 hi
->userlist_style
= HI_DEFAULT_STYLE
;
443 hi
->announcements
= '?';
444 hi
->handle
= strdup(handle
);
445 safestrncpy(hi
->passwd
, passwd
, sizeof(hi
->passwd
));
447 dict_insert(nickserv_handle_dict
, hi
->handle
, hi
);
449 #ifdef WITH_PROTOCOL_BAHAMUT
451 dict_insert(nickserv_id_dict
, strdup(id_base64
), hi
);
458 register_nick(const char *nick
, struct handle_info
*owner
)
460 struct nick_info
*ni
;
461 ni
= malloc(sizeof(struct nick_info
));
462 safestrncpy(ni
->nick
, nick
, sizeof(ni
->nick
));
464 ni
->next
= owner
->nicks
;
466 dict_insert(nickserv_nick_dict
, ni
->nick
, ni
);
470 delete_nick(struct nick_info
*ni
)
472 struct nick_info
*last
, *next
;
473 struct userNode
*user
;
474 /* Check to see if we should mark a user as unregistered. */
475 if ((user
= GetUserH(ni
->nick
)) && IsReggedNick(user
)) {
476 user
->modes
&= ~FLAGS_REGNICK
;
479 /* Remove ni from the nick_info linked list. */
480 if (ni
== ni
->owner
->nicks
) {
481 ni
->owner
->nicks
= ni
->next
;
483 last
= ni
->owner
->nicks
;
489 last
->next
= next
->next
;
491 dict_remove(nickserv_nick_dict
, ni
->nick
);
494 static unreg_func_t
*unreg_func_list
;
495 static unsigned int unreg_func_size
= 0, unreg_func_used
= 0;
498 reg_unreg_func(unreg_func_t func
)
500 if (unreg_func_used
== unreg_func_size
) {
501 if (unreg_func_size
) {
502 unreg_func_size
<<= 1;
503 unreg_func_list
= realloc(unreg_func_list
, unreg_func_size
*sizeof(unreg_func_t
));
506 unreg_func_list
= malloc(unreg_func_size
*sizeof(unreg_func_t
));
509 unreg_func_list
[unreg_func_used
++] = func
;
513 nickserv_free_cookie(void *data
)
515 struct handle_cookie
*cookie
= data
;
516 if (cookie
->hi
) cookie
->hi
->cookie
= NULL
;
517 if (cookie
->data
) free(cookie
->data
);
522 free_handle_info(void *vhi
)
524 struct handle_info
*hi
= vhi
;
526 #ifdef WITH_PROTOCOL_BAHAMUT
529 inttobase64(id
, hi
->id
, IDLEN
);
530 dict_remove(nickserv_id_dict
, id
);
533 free_string_list(hi
->masks
);
537 delete_nick(hi
->nicks
);
542 timeq_del(hi
->cookie
->expires
, nickserv_free_cookie
, hi
->cookie
, 0);
543 nickserv_free_cookie(hi
->cookie
);
545 if (hi
->email_addr
) {
546 struct handle_info_list
*hil
= dict_find(nickserv_email_dict
, hi
->email_addr
, NULL
);
547 handle_info_list_remove(hil
, hi
);
549 dict_remove(nickserv_email_dict
, hi
->email_addr
);
554 static void set_user_handle_info(struct userNode
*user
, struct handle_info
*hi
, int stamp
);
557 nickserv_unregister_handle(struct handle_info
*hi
, struct userNode
*notify
)
561 for (n
=0; n
<unreg_func_used
; n
++)
562 unreg_func_list
[n
](notify
, hi
);
564 set_user_handle_info(hi
->users
, NULL
, 0);
566 if (nickserv_conf
.disable_nicks
)
567 send_message(notify
, nickserv
, "NSMSG_UNREGISTER_SUCCESS", hi
->handle
);
569 send_message(notify
, nickserv
, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi
->handle
);
572 if (nickserv_conf
.sync_log
)
573 SyncLog("UNREGISTER %s", hi
->handle
);
575 dict_remove(nickserv_handle_dict
, hi
->handle
);
579 get_handle_info(const char *handle
)
581 return dict_find(nickserv_handle_dict
, handle
, 0);
585 get_nick_info(const char *nick
)
587 return nickserv_conf
.disable_nicks
? 0 : dict_find(nickserv_nick_dict
, nick
, 0);
591 find_handle_in_channel(struct chanNode
*channel
, struct handle_info
*handle
, struct userNode
*except
)
596 for (nn
=0; nn
<channel
->members
.used
; ++nn
) {
597 mn
= channel
->members
.list
[nn
];
598 if ((mn
->user
!= except
) && (mn
->user
->handle_info
== handle
))
605 oper_has_access(struct userNode
*user
, struct userNode
*bot
, unsigned int min_level
, unsigned int quiet
) {
606 if (!user
->handle_info
) {
608 send_message(user
, bot
, "MSG_AUTHENTICATE");
612 if (!IsOper(user
) && (!IsHelping(user
) || min_level
)) {
614 send_message(user
, bot
, "NSMSG_NO_ACCESS");
618 if (HANDLE_FLAGGED(user
->handle_info
, OPER_SUSPENDED
)) {
620 send_message(user
, bot
, "MSG_OPER_SUSPENDED");
624 if (user
->handle_info
->opserv_level
< min_level
) {
626 send_message(user
, bot
, "NSMSG_NO_ACCESS");
634 is_valid_handle(const char *handle
)
636 struct userNode
*user
;
637 /* cant register a juped nick/service nick as handle, to prevent confusion */
638 user
= GetUserH(handle
);
639 if (user
&& IsLocal(user
))
641 /* check against maximum length */
642 if (strlen(handle
) > NICKSERV_HANDLE_LEN
)
644 /* for consistency, only allow account names that could be nicks */
645 if (!is_valid_nick(handle
))
647 /* disallow account names that look like bad words */
648 if (opserv_bad_channel(handle
))
650 /* test either regex or containing all valid chars */
651 if (nickserv_conf
.valid_handle_regex_set
) {
652 int err
= regexec(&nickserv_conf
.valid_handle_regex
, handle
, 0, 0, 0);
655 buff
[regerror(err
, &nickserv_conf
.valid_handle_regex
, buff
, sizeof(buff
))] = 0;
656 log_module(NS_LOG
, LOG_INFO
, "regexec error: %s (%d)", buff
, err
);
660 return !handle
[strspn(handle
, NICKSERV_VALID_CHARS
)];
665 is_registerable_nick(const char *nick
)
667 /* make sure it could be used as an account name */
668 if (!is_valid_handle(nick
))
671 if (strlen(nick
) > NICKLEN
)
673 /* test either regex or as valid handle */
674 if (nickserv_conf
.valid_nick_regex_set
) {
675 int err
= regexec(&nickserv_conf
.valid_nick_regex
, nick
, 0, 0, 0);
678 buff
[regerror(err
, &nickserv_conf
.valid_nick_regex
, buff
, sizeof(buff
))] = 0;
679 log_module(NS_LOG
, LOG_INFO
, "regexec error: %s (%d)", buff
, err
);
687 is_valid_email_addr(const char *email
)
689 return strchr(email
, '@') != NULL
;
693 visible_email_addr(struct userNode
*user
, struct handle_info
*hi
)
695 if (hi
->email_addr
) {
696 if (oper_has_access(user
, nickserv
, nickserv_conf
.email_visible_level
, 1)) {
697 return hi
->email_addr
;
707 smart_get_handle_info(struct userNode
*service
, struct userNode
*user
, const char *name
)
709 struct handle_info
*hi
;
710 struct userNode
*target
;
714 if (!(hi
= get_handle_info(++name
))) {
715 send_message(user
, service
, "MSG_HANDLE_UNKNOWN", name
);
720 if (!(target
= GetUserH(name
))) {
721 send_message(user
, service
, "MSG_NICK_UNKNOWN", name
);
724 if (IsLocal(target
)) {
725 if (IsService(target
))
726 send_message(user
, service
, "NSMSG_USER_IS_SERVICE", target
->nick
);
728 send_message(user
, service
, "MSG_USER_AUTHENTICATE", target
->nick
);
731 if (!(hi
= target
->handle_info
)) {
732 send_message(user
, service
, "MSG_USER_AUTHENTICATE", target
->nick
);
740 oper_outranks(struct userNode
*user
, struct handle_info
*hi
) {
741 if (user
->handle_info
->opserv_level
> hi
->opserv_level
)
743 if (user
->handle_info
->opserv_level
== hi
->opserv_level
) {
744 if ((user
->handle_info
->opserv_level
== 1000)
745 || (user
->handle_info
== hi
)
746 || ((user
->handle_info
->opserv_level
== 0)
747 && !(HANDLE_FLAGGED(hi
, SUPPORT_HELPER
) || HANDLE_FLAGGED(hi
, NETWORK_HELPER
))
748 && HANDLE_FLAGGED(user
->handle_info
, HELPING
))) {
752 send_message(user
, nickserv
, "MSG_USER_OUTRANKED", hi
->handle
);
756 static struct handle_info
*
757 get_victim_oper(struct userNode
*user
, const char *target
)
759 struct handle_info
*hi
;
760 if (!(hi
= smart_get_handle_info(nickserv
, user
, target
)))
762 if (HANDLE_FLAGGED(user
->handle_info
, OPER_SUSPENDED
)) {
763 send_message(user
, nickserv
, "MSG_OPER_SUSPENDED");
766 return oper_outranks(user
, hi
) ? hi
: NULL
;
770 valid_user_for(struct userNode
*user
, struct handle_info
*hi
)
774 /* If no hostmasks on the account, allow it. */
775 if (!hi
->masks
->used
)
777 /* If any hostmask matches, allow it. */
778 for (ii
=0; ii
<hi
->masks
->used
; ii
++)
779 if (user_matches_glob(user
, hi
->masks
->list
[ii
], 0))
781 /* If they are allowauthed to this account, allow it (removing the aa). */
782 if (dict_find(nickserv_allow_auth_dict
, user
->nick
, NULL
) == hi
) {
783 dict_remove(nickserv_allow_auth_dict
, user
->nick
);
786 /* The user is not allowed to use this account. */
791 is_secure_password(const char *handle
, const char *pass
, struct userNode
*user
)
794 unsigned int cnt_digits
= 0, cnt_upper
= 0, cnt_lower
= 0;
796 if (len
< nickserv_conf
.password_min_length
) {
798 send_message(user
, nickserv
, "NSMSG_PASSWORD_SHORT", nickserv_conf
.password_min_length
);
801 if (!irccasecmp(pass
, handle
)) {
803 send_message(user
, nickserv
, "NSMSG_PASSWORD_ACCOUNT");
806 dict_find(nickserv_conf
.weak_password_dict
, pass
, &i
);
809 send_message(user
, nickserv
, "NSMSG_PASSWORD_DICTIONARY");
812 for (i
=0; i
<len
; i
++) {
813 if (isdigit(pass
[i
]))
815 if (isupper(pass
[i
]))
817 if (islower(pass
[i
]))
820 if ((cnt_lower
< nickserv_conf
.password_min_lower
)
821 || (cnt_upper
< nickserv_conf
.password_min_upper
)
822 || (cnt_digits
< nickserv_conf
.password_min_digits
)) {
824 send_message(user
, nickserv
, "NSMSG_PASSWORD_READABLE", nickserv_conf
.password_min_digits
, nickserv_conf
.password_min_upper
, nickserv_conf
.password_min_lower
);
830 static auth_func_t
*auth_func_list
;
831 static unsigned int auth_func_size
= 0, auth_func_used
= 0;
834 reg_auth_func(auth_func_t func
)
836 if (auth_func_used
== auth_func_size
) {
837 if (auth_func_size
) {
838 auth_func_size
<<= 1;
839 auth_func_list
= realloc(auth_func_list
, auth_func_size
*sizeof(auth_func_t
));
842 auth_func_list
= malloc(auth_func_size
*sizeof(auth_func_t
));
845 auth_func_list
[auth_func_used
++] = func
;
848 static handle_rename_func_t
*rf_list
;
849 static unsigned int rf_list_size
, rf_list_used
;
852 reg_handle_rename_func(handle_rename_func_t func
)
854 if (rf_list_used
== rf_list_size
) {
857 rf_list
= realloc(rf_list
, rf_list_size
*sizeof(rf_list
[0]));
860 rf_list
= malloc(rf_list_size
*sizeof(rf_list
[0]));
863 rf_list
[rf_list_used
++] = func
;
867 generate_fakehost(struct handle_info
*handle
)
869 extern const char *hidden_host_suffix
;
870 static char buffer
[HOSTLEN
+1];
872 if (!handle
->fakehost
) {
873 snprintf(buffer
, sizeof(buffer
), "%s.%s", handle
->handle
, hidden_host_suffix
);
875 } else if (handle
->fakehost
[0] == '.') {
876 /* A leading dot indicates the stored value is actually a title. */
877 snprintf(buffer
, sizeof(buffer
), "%s.%s.%s", handle
->handle
, handle
->fakehost
+1, nickserv_conf
.titlehost_suffix
);
880 return handle
->fakehost
;
884 apply_fakehost(struct handle_info
*handle
)
886 struct userNode
*target
;
891 fake
= generate_fakehost(handle
);
892 for (target
= handle
->users
; target
; target
= target
->next_authed
)
893 assign_fakehost(target
, fake
, 1);
897 set_user_handle_info(struct userNode
*user
, struct handle_info
*hi
, int stamp
)
900 struct handle_info
*old_info
;
902 /* This can happen if somebody uses COOKIE while authed, or if
903 * they re-auth to their current handle (which is silly, but users
905 if (user
->handle_info
== hi
)
908 if (user
->handle_info
) {
909 struct userNode
*other
;
912 userList_remove(&curr_helpers
, user
);
914 /* remove from next_authed linked list */
915 if (user
->handle_info
->users
== user
) {
916 user
->handle_info
->users
= user
->next_authed
;
918 for (other
= user
->handle_info
->users
;
919 other
->next_authed
!= user
;
920 other
= other
->next_authed
) ;
921 other
->next_authed
= user
->next_authed
;
923 /* if nobody left on old handle, and they're not an oper, remove !god */
924 if (!user
->handle_info
->users
&& !user
->handle_info
->opserv_level
)
925 HANDLE_CLEAR_FLAG(user
->handle_info
, HELPING
);
926 /* record them as being last seen at this time */
927 user
->handle_info
->lastseen
= now
;
928 /* and record their hostmask */
929 snprintf(user
->handle_info
->last_quit_host
, sizeof(user
->handle_info
->last_quit_host
), "%s@%s", user
->ident
, user
->hostname
);
931 old_info
= user
->handle_info
;
932 user
->handle_info
= hi
;
933 if (hi
&& !hi
->users
&& !hi
->opserv_level
)
934 HANDLE_CLEAR_FLAG(hi
, HELPING
);
935 for (n
=0; n
<auth_func_used
; n
++)
936 auth_func_list
[n
](user
, old_info
);
938 struct nick_info
*ni
;
940 HANDLE_CLEAR_FLAG(hi
, FROZEN
);
941 if (nickserv_conf
.warn_clone_auth
) {
942 struct userNode
*other
;
943 for (other
= hi
->users
; other
; other
= other
->next_authed
)
944 send_message(other
, nickserv
, "NSMSG_CLONE_AUTH", user
->nick
, user
->ident
, user
->hostname
);
946 user
->next_authed
= hi
->users
;
950 userList_append(&curr_helpers
, user
);
952 if (hi
->fakehost
|| old_info
)
956 #ifdef WITH_PROTOCOL_BAHAMUT
957 /* Stamp users with their account ID. */
959 inttobase64(id
, hi
->id
, IDLEN
);
960 #elif WITH_PROTOCOL_P10
961 /* Stamp users with their account name. */
962 char *id
= hi
->handle
;
964 const char *id
= "???";
966 if (!nickserv_conf
.disable_nicks
) {
967 struct nick_info
*ni
;
968 for (ni
= hi
->nicks
; ni
; ni
= ni
->next
) {
969 if (!irccasecmp(user
->nick
, ni
->nick
)) {
970 user
->modes
|= FLAGS_REGNICK
;
975 StampUser(user
, id
, hi
->registered
);
978 if ((ni
= get_nick_info(user
->nick
)) && (ni
->owner
== hi
))
979 timeq_del(0, nickserv_reclaim_p
, user
, TIMEQ_IGNORE_WHEN
);
981 /* We cannot clear the user's account ID, unfortunately. */
982 user
->next_authed
= NULL
;
986 static struct handle_info
*
987 nickserv_register(struct userNode
*user
, struct userNode
*settee
, const char *handle
, const char *passwd
, int no_auth
)
989 struct handle_info
*hi
;
990 struct nick_info
*ni
;
991 char crypted
[MD5_CRYPT_LENGTH
];
993 if ((hi
= dict_find(nickserv_handle_dict
, handle
, NULL
))) {
994 send_message(user
, nickserv
, "NSMSG_HANDLE_EXISTS", handle
);
998 if(strlen(handle
) > 15)
1000 send_message(user
, nickserv
, "NSMSG_HANDLE_TOLONG", handle
, 15);
1004 if (!is_secure_password(handle
, passwd
, user
))
1007 cryptpass(passwd
, crypted
);
1008 hi
= register_handle(handle
, crypted
, 0);
1009 hi
->masks
= alloc_string_list(1);
1011 hi
->language
= lang_C
;
1012 hi
->registered
= now
;
1014 hi
->flags
= HI_DEFAULT_FLAGS
;
1015 if (settee
&& !no_auth
)
1016 set_user_handle_info(settee
, hi
, 1);
1019 send_message(user
, nickserv
, "NSMSG_OREGISTER_H_SUCCESS");
1020 else if (nickserv_conf
.disable_nicks
)
1021 send_message(user
, nickserv
, "NSMSG_REGISTER_H_SUCCESS");
1022 else if ((ni
= dict_find(nickserv_nick_dict
, user
->nick
, NULL
)))
1023 send_message(user
, nickserv
, "NSMSG_PARTIAL_REGISTER");
1025 register_nick(user
->nick
, hi
);
1026 send_message(user
, nickserv
, "NSMSG_REGISTER_HN_SUCCESS");
1028 if (settee
&& (user
!= settee
))
1029 send_message(settee
, nickserv
, "NSMSG_OREGISTER_VICTIM", user
->nick
, hi
->handle
);
1034 nickserv_bake_cookie(struct handle_cookie
*cookie
)
1036 cookie
->hi
->cookie
= cookie
;
1037 timeq_add(cookie
->expires
, nickserv_free_cookie
, cookie
);
1040 /* Contributed by the great sneep of afternet ;) */
1041 /* Since this gets used in a URL, we want to avoid stuff that confuses
1042 * email clients such as ] and ?. a-z, 0-9 only.
1044 void genpass(char *str
, int len
)
1049 for(i
= 0; i
< len
; i
++)
1053 c
= (char)((float)rand() / (float)RAND_MAX
* (float)256);
1054 } while(!((c
>= 'a' && c
<= 'z') || (c
>= 'A' && c
<= 'Z') || (c
>= '0' && c
<= '9')));
1062 nickserv_make_cookie(struct userNode
*user
, struct handle_info
*hi
, enum cookie_type type
, const char *cookie_data
, int weblink
)
1064 struct handle_cookie
*cookie
;
1065 char subject
[128], body
[4096], *misc
;
1066 const char *netname
, *fmt
;
1070 send_message(user
, nickserv
, "NSMSG_COOKIE_LIVE", hi
->handle
);
1074 cookie
= calloc(1, sizeof(*cookie
));
1076 cookie
->type
= type
;
1077 cookie
->data
= cookie_data
? strdup(cookie_data
) : NULL
;
1079 cookie
->expires
= now
+ nickserv_conf
.cookie_timeout
;
1080 /* Adding dedicated password gen function for more control -Rubin */
1081 genpass(cookie
->cookie
, 10);
1083 *inttobase64(cookie->cookie, rand(), 5);
1084 *inttobase64(cookie->cookie+5, rand(), 5);
1087 netname
= nickserv_conf
.network_name
;
1090 switch (cookie
->type
) {
1092 hi
->passwd
[0] = 0; /* invalidate password */
1093 send_message(user
, nickserv
, "NSMSG_USE_COOKIE_REGISTER");
1094 fmt
= handle_find_message(hi
, "NSEMAIL_ACTIVATION_SUBJECT");
1095 snprintf(subject
, sizeof(subject
), fmt
, netname
);
1098 fmt
= handle_find_message(hi
, "NSEMAIL_ACTIVATION_BODY_WEB");
1100 fmt
= handle_find_message(hi
, "NSEMAIL_ACTIVATION_BODY");
1102 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
, nickserv
->nick
, self
->name
, hi
->handle
);
1105 case PASSWORD_CHANGE
:
1106 send_message(user
, nickserv
, "NSMSG_USE_COOKIE_RESETPASS");
1107 fmt
= handle_find_message(hi
, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1108 snprintf(subject
, sizeof(subject
), fmt
, netname
);
1110 fmt
= handle_find_message(hi
, "NSEMAIL_PASSWORD_CHANGE_BODY_WEB");
1112 fmt
= handle_find_message(hi
, "NSEMAIL_PASSWORD_CHANGE_BODY");
1113 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
, nickserv
->nick
, self
->name
, hi
->handle
);
1116 misc
= hi
->email_addr
;
1117 hi
->email_addr
= cookie
->data
;
1118 #ifdef stupid_verify_old_email
1120 send_message(user
, nickserv
, "NSMSG_USE_COOKIE_EMAIL_2");
1121 fmt
= handle_find_message(hi
, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1122 snprintf(subject
, sizeof(subject
), fmt
, netname
);
1123 fmt
= handle_find_message(hi
, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1124 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
+COOKIELEN
/2, nickserv
->nick
, self
->name
, hi
->handle
, COOKIELEN
/2);
1125 sendmail(nickserv
, hi
, subject
, body
, 1);
1126 fmt
= handle_find_message(hi
, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1127 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
, nickserv
->nick
, self
->name
, hi
->handle
, COOKIELEN
/2, hi
->email_addr
);
1130 send_message(user
, nickserv
, "NSMSG_USE_COOKIE_EMAIL_1");
1131 fmt
= handle_find_message(hi
, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1132 snprintf(subject
, sizeof(subject
), fmt
, netname
);
1133 fmt
= handle_find_message(hi
, "NSEMAIL_EMAIL_VERIFY_BODY");
1134 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
, nickserv
->nick
, self
->name
, hi
->handle
);
1135 sendmail(nickserv
, hi
, subject
, body
, 1);
1137 #ifdef stupid_verify_old_email
1140 hi
->email_addr
= misc
;
1143 fmt
= handle_find_message(hi
, "NSEMAIL_ALLOWAUTH_SUBJECT");
1144 snprintf(subject
, sizeof(subject
), fmt
, netname
);
1145 fmt
= handle_find_message(hi
, "NSEMAIL_ALLOWAUTH_BODY");
1146 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
, nickserv
->nick
, self
->name
, hi
->handle
);
1147 send_message(user
, nickserv
, "NSMSG_USE_COOKIE_AUTH");
1150 log_module(NS_LOG
, LOG_ERROR
, "Bad cookie type %d in nickserv_make_cookie.", cookie
->type
);
1154 sendmail(nickserv
, hi
, subject
, body
, first_time
);
1155 nickserv_bake_cookie(cookie
);
1159 nickserv_eat_cookie(struct handle_cookie
*cookie
)
1161 cookie
->hi
->cookie
= NULL
;
1162 timeq_del(cookie
->expires
, nickserv_free_cookie
, cookie
, 0);
1163 nickserv_free_cookie(cookie
);
1167 nickserv_free_email_addr(void *data
)
1169 handle_info_list_clean(data
);
1174 nickserv_set_email_addr(struct handle_info
*hi
, const char *new_email_addr
)
1176 struct handle_info_list
*hil
;
1177 /* Remove from old handle_info_list ... */
1178 if (hi
->email_addr
&& (hil
= dict_find(nickserv_email_dict
, hi
->email_addr
, 0))) {
1179 handle_info_list_remove(hil
, hi
);
1180 if (!hil
->used
) dict_remove(nickserv_email_dict
, hil
->tag
);
1181 hi
->email_addr
= NULL
;
1183 /* Add to the new list.. */
1184 if (new_email_addr
) {
1185 if (!(hil
= dict_find(nickserv_email_dict
, new_email_addr
, 0))) {
1186 hil
= calloc(1, sizeof(*hil
));
1187 hil
->tag
= strdup(new_email_addr
);
1188 handle_info_list_init(hil
);
1189 dict_insert(nickserv_email_dict
, hil
->tag
, hil
);
1191 handle_info_list_append(hil
, hi
);
1192 hi
->email_addr
= hil
->tag
;
1196 static NICKSERV_FUNC(cmd_register
)
1198 struct handle_info
*hi
;
1199 const char *email_addr
, *password
;
1200 char syncpass
[MD5_CRYPT_LENGTH
];
1201 int no_auth
, weblink
;
1203 if (!IsOper(user
) && !dict_size(nickserv_handle_dict
)) {
1204 /* Require the first handle registered to belong to someone +o. */
1205 reply("NSMSG_REQUIRE_OPER");
1209 if (user
->handle_info
) {
1210 reply("NSMSG_USE_RENAME", user
->handle_info
->handle
);
1214 if (IsRegistering(user
)) {
1215 reply("NSMSG_ALREADY_REGISTERING");
1219 if (IsStamped(user
)) {
1220 /* Unauthenticated users might still have been stamped
1221 previously and could therefore have a hidden host;
1222 do not allow them to register a new account. */
1223 reply("NSMSG_STAMPED_REGISTER");
1227 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf
.email_required
);
1229 if (!is_valid_handle(argv
[1])) {
1230 reply("NSMSG_BAD_HANDLE", argv
[1]);
1235 if ((argc
>= 4) && nickserv_conf
.email_enabled
) {
1236 struct handle_info_list
*hil
;
1239 /* Remember email address. */
1240 email_addr
= argv
[3];
1242 /* Check that the email address looks valid.. */
1243 if (!is_valid_email_addr(email_addr
)) {
1244 reply("NSMSG_BAD_EMAIL_ADDR");
1248 /* .. and that we are allowed to send to it. */
1249 if ((str
= sendmail_prohibited_address(email_addr
))) {
1250 reply("NSMSG_EMAIL_PROHIBITED", email_addr
, str
);
1254 /* If we do email verify, make sure we don't spam the address. */
1255 if ((hil
= dict_find(nickserv_email_dict
, email_addr
, NULL
))) {
1257 for (nn
=0; nn
<hil
->used
; nn
++) {
1258 if (hil
->list
[nn
]->cookie
) {
1259 reply("NSMSG_EMAIL_UNACTIVATED");
1263 if (hil
->used
>= nickserv_conf
.handles_per_email
) {
1264 reply("NSMSG_EMAIL_OVERUSED");
1277 /* Webregister hack - send URL instead of IRC cookie
1280 if((argc
>= 5) && !strcmp(argv
[4],"WEBLINK"))
1284 if (!(hi
= nickserv_register(user
, user
, argv
[1], password
, no_auth
)))
1286 /* Add any masks they should get. */
1287 if (nickserv_conf
.default_hostmask
) {
1288 string_list_append(hi
->masks
, strdup("*@*"));
1290 string_list_append(hi
->masks
, generate_hostmask(user
, GENMASK_OMITNICK
|GENMASK_NO_HIDING
|GENMASK_ANY_IDENT
));
1291 if (user
->ip
.s_addr
&& user
->hostname
[strspn(user
->hostname
, "0123456789.")])
1292 string_list_append(hi
->masks
, generate_hostmask(user
, GENMASK_OMITNICK
|GENMASK_BYIP
|GENMASK_NO_HIDING
|GENMASK_ANY_IDENT
));
1295 /* If they're the first to register, give them level 1000. */
1296 if (dict_size(nickserv_handle_dict
) == 1) {
1297 hi
->opserv_level
= 1000;
1298 reply("NSMSG_ROOT_HANDLE", argv
[1]);
1301 /* Set their email address. */
1303 nickserv_set_email_addr(hi
, email_addr
);
1305 /* If they need to do email verification, tell them. */
1307 nickserv_make_cookie(user
, hi
, ACTIVATION
, hi
->passwd
, weblink
);
1309 /* Set registering flag.. */
1310 user
->modes
|= FLAGS_REGISTERING
;
1312 if (nickserv_conf
.sync_log
) {
1313 cryptpass(password
, syncpass
);
1315 * An 0 is only sent if theres no email address. Thios should only happen if email functions are
1316 * disabled which they wont be for us. Email Required MUST be set on if you are using this.
1319 SyncLog("REGISTER %s %s %s %s", hi
->handle
, syncpass
, email_addr
? email_addr
: "0", user
->info
);
1325 static NICKSERV_FUNC(cmd_oregister
)
1328 struct userNode
*settee
;
1329 struct handle_info
*hi
;
1331 NICKSERV_MIN_PARMS(4);
1333 if (!is_valid_handle(argv
[1])) {
1334 reply("NSMSG_BAD_HANDLE", argv
[1]);
1338 if (strchr(argv
[3], '@')) {
1339 mask
= canonicalize_hostmask(strdup(argv
[3]));
1341 settee
= GetUserH(argv
[4]);
1343 reply("MSG_NICK_UNKNOWN", argv
[4]);
1350 } else if ((settee
= GetUserH(argv
[3]))) {
1351 mask
= generate_hostmask(settee
, GENMASK_OMITNICK
|GENMASK_NO_HIDING
|GENMASK_ANY_IDENT
);
1353 reply("NSMSG_REGISTER_BAD_NICKMASK", argv
[3]);
1356 if (settee
&& settee
->handle_info
) {
1357 reply("NSMSG_USER_PREV_AUTH", settee
->nick
);
1361 if (!(hi
= nickserv_register(user
, settee
, argv
[1], argv
[2], 0))) {
1365 string_list_append(hi
->masks
, mask
);
1369 static NICKSERV_FUNC(cmd_handleinfo
)
1372 unsigned int i
, pos
=0, herelen
;
1373 struct userNode
*target
, *next_un
;
1374 struct handle_info
*hi
;
1375 const char *nsmsg_none
;
1378 if (!(hi
= user
->handle_info
)) {
1379 reply("NSMSG_MUST_AUTH");
1382 } else if (!(hi
= modcmd_get_handle_info(user
, argv
[1]))) {
1386 nsmsg_none
= handle_find_message(hi
, "MSG_NONE");
1387 reply("NSMSG_HANDLEINFO_ON", hi
->handle
);
1389 #ifdef WITH_PROTOCOL_BAHAMUT
1390 reply("NSMSG_HANDLEINFO_ID", hi
->id
);
1392 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi
->registered
));
1395 intervalString(buff
, now
- hi
->lastseen
, user
->handle_info
);
1396 reply("NSMSG_HANDLEINFO_LASTSEEN", buff
);
1398 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1401 reply("NSMSG_HANDLEINFO_INFOLINE", (hi
->infoline
? hi
->infoline
: nsmsg_none
));
1402 if (HANDLE_FLAGGED(hi
, FROZEN
))
1403 reply("NSMSG_HANDLEINFO_VACATION");
1405 if (oper_has_access(user
, cmd
->parent
->bot
, 0, 1)) {
1406 struct do_not_register
*dnr
;
1407 if ((dnr
= chanserv_is_dnr(NULL
, hi
)))
1408 reply("NSMSG_HANDLEINFO_DNR", dnr
->setter
, dnr
->reason
);
1409 if (!oper_outranks(user
, hi
))
1411 } else if (hi
!= user
->handle_info
)
1414 if (nickserv_conf
.email_enabled
)
1415 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user
, hi
));
1419 switch (hi
->cookie
->type
) {
1420 case ACTIVATION
: type
= "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1421 case PASSWORD_CHANGE
: type
= "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1422 case EMAIL_CHANGE
: type
= "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1423 case ALLOWAUTH
: type
= "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1424 default: type
= "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1430 unsigned long flen
= 1;
1431 char flags
[34]; /* 32 bits possible plus '+' and '\0' */
1433 for (i
=0, flen
=1; handle_flags
[i
]; i
++)
1434 if (hi
->flags
& 1 << i
)
1435 flags
[flen
++] = handle_flags
[i
];
1437 reply("NSMSG_HANDLEINFO_FLAGS", flags
);
1439 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none
);
1442 if (HANDLE_FLAGGED(hi
, SUPPORT_HELPER
)
1443 || HANDLE_FLAGGED(hi
, NETWORK_HELPER
)
1444 || (hi
->opserv_level
> 0)) {
1445 reply("NSMSG_HANDLEINFO_EPITHET", (hi
->epithet
? hi
->epithet
: nsmsg_none
));
1449 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi
->fakehost
? hi
->fakehost
: handle_find_message(hi
, "MSG_NONE")));
1451 if (hi
->last_quit_host
[0])
1452 reply("NSMSG_HANDLEINFO_LAST_HOST", hi
->last_quit_host
);
1454 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1456 if (nickserv_conf
.disable_nicks
) {
1457 /* nicks disabled; don't show anything about registered nicks */
1458 } else if (hi
->nicks
) {
1459 struct nick_info
*ni
, *next_ni
;
1460 for (ni
= hi
->nicks
; ni
; ni
= next_ni
) {
1461 herelen
= strlen(ni
->nick
);
1462 if (pos
+ herelen
+ 1 > ArrayLength(buff
)) {
1464 goto print_nicks_buff
;
1468 memcpy(buff
+pos
, ni
->nick
, herelen
);
1469 pos
+= herelen
; buff
[pos
++] = ' ';
1473 reply("NSMSG_HANDLEINFO_NICKS", buff
);
1478 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none
);
1481 if (hi
->masks
->used
) {
1482 for (i
=0; i
< hi
->masks
->used
; i
++) {
1483 herelen
= strlen(hi
->masks
->list
[i
]);
1484 if (pos
+ herelen
+ 1 > ArrayLength(buff
)) {
1486 goto print_mask_buff
;
1488 memcpy(buff
+pos
, hi
->masks
->list
[i
], herelen
);
1489 pos
+= herelen
; buff
[pos
++] = ' ';
1490 if (i
+1 == hi
->masks
->used
) {
1493 reply("NSMSG_HANDLEINFO_MASKS", buff
);
1498 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none
);
1502 struct userData
*channel
, *next
;
1505 for (channel
= hi
->channels
; channel
; channel
= next
) {
1506 next
= channel
->u_next
;
1507 name
= channel
->channel
->channel
->name
;
1508 herelen
= strlen(name
);
1509 if (pos
+ herelen
+ 7 > ArrayLength(buff
)) {
1511 goto print_chans_buff
;
1513 if (IsUserSuspended(channel
))
1515 pos
+= sprintf(buff
+pos
, "%s:%s ", user_level_name_from_level(channel
->access
), name
);
1519 reply("NSMSG_HANDLEINFO_CHANNELS", buff
);
1524 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none
);
1527 for (target
= hi
->users
; target
; target
= next_un
) {
1528 herelen
= strlen(target
->nick
);
1529 if (pos
+ herelen
+ 1 > ArrayLength(buff
)) {
1531 goto print_cnick_buff
;
1533 next_un
= target
->next_authed
;
1535 memcpy(buff
+pos
, target
->nick
, herelen
);
1536 pos
+= herelen
; buff
[pos
++] = ' ';
1540 reply("NSMSG_HANDLEINFO_CURRENT", buff
);
1545 reply("NSMSG_HANDLEINFO_END");
1549 static NICKSERV_FUNC(cmd_userinfo
)
1551 struct userNode
*target
;
1553 NICKSERV_MIN_PARMS(2);
1554 if (!(target
= GetUserH(argv
[1]))) {
1555 reply("MSG_NICK_UNKNOWN", argv
[1]);
1558 if (target
->handle_info
)
1559 reply("NSMSG_USERINFO_AUTHED_AS", target
->nick
, target
->handle_info
->handle
);
1561 reply("NSMSG_USERINFO_NOT_AUTHED", target
->nick
);
1565 static NICKSERV_FUNC(cmd_nickinfo
)
1567 struct nick_info
*ni
;
1569 NICKSERV_MIN_PARMS(2);
1570 if (!(ni
= get_nick_info(argv
[1]))) {
1571 reply("MSG_NICK_UNKNOWN", argv
[1]);
1574 reply("NSMSG_NICKINFO_OWNER", ni
->nick
, ni
->owner
->handle
);
1578 static NICKSERV_FUNC(cmd_rename_handle
)
1580 struct handle_info
*hi
;
1581 char msgbuf
[MAXLEN
], *old_handle
;
1584 NICKSERV_MIN_PARMS(3);
1585 if (!(hi
= get_victim_oper(user
, argv
[1])))
1587 if (!is_valid_handle(argv
[2])) {
1588 reply("NSMSG_FAIL_RENAME", argv
[1], argv
[2]);
1591 if (get_handle_info(argv
[2])) {
1592 reply("NSMSG_HANDLE_EXISTS", argv
[2]);
1595 if(strlen(argv
[2]) > 15)
1597 reply("NMSG_HANDLE_TOLONG", argv
[2], 15);
1601 dict_remove2(nickserv_handle_dict
, old_handle
= hi
->handle
, 1);
1602 hi
->handle
= strdup(argv
[2]);
1603 dict_insert(nickserv_handle_dict
, hi
->handle
, hi
);
1604 for (nn
=0; nn
<rf_list_used
; nn
++)
1605 rf_list
[nn
](hi
, old_handle
);
1606 snprintf(msgbuf
, sizeof(msgbuf
), "%s renamed account %s to %s.", user
->handle_info
->handle
, old_handle
, hi
->handle
);
1607 reply("NSMSG_HANDLE_CHANGED", old_handle
, hi
->handle
);
1608 global_message(MESSAGE_RECIPIENT_STAFF
, msgbuf
);
1613 static failpw_func_t
*failpw_func_list
;
1614 static unsigned int failpw_func_size
= 0, failpw_func_used
= 0;
1617 reg_failpw_func(failpw_func_t func
)
1619 if (failpw_func_used
== failpw_func_size
) {
1620 if (failpw_func_size
) {
1621 failpw_func_size
<<= 1;
1622 failpw_func_list
= realloc(failpw_func_list
, failpw_func_size
*sizeof(failpw_func_t
));
1624 failpw_func_size
= 8;
1625 failpw_func_list
= malloc(failpw_func_size
*sizeof(failpw_func_t
));
1628 failpw_func_list
[failpw_func_used
++] = func
;
1632 * Return hi if the handle/pass pair matches, NULL if it doesnt.
1634 * called by nefariouses enhanced AC login-on-connect code
1637 struct handle_info
*loc_auth(char *handle
, char *password
)
1639 int pw_arg
, used
, maxlogins
;
1642 struct handle_info
*hi
;
1643 struct userNode
*other
;
1645 hi
= dict_find(nickserv_handle_dict
, handle
, NULL
);
1651 /* We don't know the users hostname, or anything because they
1652 * havn't registered yet. So we can only allow LOC if your
1653 * account has *@* as a hostmask.
1655 for (ii
=0; ii
<hi
->masks
->used
; ii
++)
1657 if (!strcmp(hi
->masks
->list
[ii
], "*@*"))
1666 /* Responses from here on look up the language used by the handle they asked about. */
1667 if (!checkpass(password
, hi
->passwd
)) {
1670 if (HANDLE_FLAGGED(hi
, SUSPENDED
)) {
1673 maxlogins
= hi
->maxlogins
? hi
->maxlogins
: nickserv_conf
.default_maxlogins
;
1674 for (used
= 0, other
= hi
->users
; other
; other
= other
->next_authed
) {
1675 if (++used
>= maxlogins
) {
1682 static NICKSERV_FUNC(cmd_auth
)
1684 int pw_arg
, used
, maxlogins
;
1685 struct handle_info
*hi
;
1687 struct userNode
*other
;
1689 if (user
->handle_info
) {
1690 reply("NSMSG_ALREADY_AUTHED", user
->handle_info
->handle
);
1693 if (IsStamped(user
)) {
1694 /* Unauthenticated users might still have been stamped
1695 previously and could therefore have a hidden host;
1696 do not allow them to authenticate. */
1697 reply("NSMSG_STAMPED_AUTH");
1701 hi
= dict_find(nickserv_handle_dict
, argv
[1], NULL
);
1703 } else if (argc
== 2) {
1704 if (nickserv_conf
.disable_nicks
) {
1705 if (!(hi
= get_handle_info(user
->nick
))) {
1706 reply("NSMSG_HANDLE_NOT_FOUND");
1710 /* try to look up their handle from their nick */
1711 struct nick_info
*ni
;
1712 ni
= get_nick_info(user
->nick
);
1714 reply("NSMSG_NICK_NOT_REGISTERED", user
->nick
);
1721 reply("MSG_MISSING_PARAMS", argv
[0]);
1722 svccmd_send_help(user
, nickserv
, cmd
);
1726 reply("NSMSG_HANDLE_NOT_FOUND");
1729 /* Responses from here on look up the language used by the handle they asked about. */
1730 passwd
= argv
[pw_arg
];
1731 if (!valid_user_for(user
, hi
)) {
1732 if (hi
->email_addr
&& nickserv_conf
.email_enabled
)
1733 send_message_type(4, user
, cmd
->parent
->bot
,
1734 handle_find_message(hi
, "NSMSG_USE_AUTHCOOKIE"),
1737 send_message_type(4, user
, cmd
->parent
->bot
,
1738 handle_find_message(hi
, "NSMSG_HOSTMASK_INVALID"),
1740 argv
[pw_arg
] = "BADMASK";
1743 if (!checkpass(passwd
, hi
->passwd
)) {
1745 send_message_type(4, user
, cmd
->parent
->bot
,
1746 handle_find_message(hi
, "NSMSG_PASSWORD_INVALID"));
1747 argv
[pw_arg
] = "BADPASS";
1748 for (n
=0; n
<failpw_func_used
; n
++) failpw_func_list
[n
](user
, hi
);
1749 if (nickserv_conf
.autogag_enabled
) {
1750 if (!user
->auth_policer
.params
) {
1751 user
->auth_policer
.last_req
= now
;
1752 user
->auth_policer
.params
= nickserv_conf
.auth_policer_params
;
1754 if (!policer_conforms(&user
->auth_policer
, now
, 1.0)) {
1756 hostmask
= generate_hostmask(user
, GENMASK_STRICT_HOST
|GENMASK_BYIP
|GENMASK_NO_HIDING
);
1757 log_module(NS_LOG
, LOG_INFO
, "%s auto-gagged for repeated password guessing.", hostmask
);
1758 gag_create(hostmask
, nickserv
->nick
, "Repeated password guessing.", now
+nickserv_conf
.autogag_duration
);
1760 argv
[pw_arg
] = "GAGGED";
1765 if (HANDLE_FLAGGED(hi
, SUSPENDED
)) {
1766 send_message_type(4, user
, cmd
->parent
->bot
,
1767 handle_find_message(hi
, "NSMSG_HANDLE_SUSPENDED"));
1768 argv
[pw_arg
] = "SUSPENDED";
1771 maxlogins
= hi
->maxlogins
? hi
->maxlogins
: nickserv_conf
.default_maxlogins
;
1772 for (used
= 0, other
= hi
->users
; other
; other
= other
->next_authed
) {
1773 if (++used
>= maxlogins
) {
1774 send_message_type(4, user
, cmd
->parent
->bot
,
1775 handle_find_message(hi
, "NSMSG_MAX_LOGINS"),
1777 argv
[pw_arg
] = "MAXLOGINS";
1782 set_user_handle_info(user
, hi
, 1);
1783 if (nickserv_conf
.email_required
&& !hi
->email_addr
)
1784 reply("NSMSG_PLEASE_SET_EMAIL");
1785 if (!is_secure_password(hi
->handle
, passwd
, NULL
))
1786 reply("NSMSG_WEAK_PASSWORD");
1787 if (hi
->passwd
[0] != '$')
1788 cryptpass(passwd
, hi
->passwd
);
1789 reply("NSMSG_AUTH_SUCCESS");
1790 argv
[pw_arg
] = "****";
1794 static allowauth_func_t
*allowauth_func_list
;
1795 static unsigned int allowauth_func_size
= 0, allowauth_func_used
= 0;
1798 reg_allowauth_func(allowauth_func_t func
)
1800 if (allowauth_func_used
== allowauth_func_size
) {
1801 if (allowauth_func_size
) {
1802 allowauth_func_size
<<= 1;
1803 allowauth_func_list
= realloc(allowauth_func_list
, allowauth_func_size
*sizeof(allowauth_func_t
));
1805 allowauth_func_size
= 8;
1806 allowauth_func_list
= malloc(allowauth_func_size
*sizeof(allowauth_func_t
));
1809 allowauth_func_list
[allowauth_func_used
++] = func
;
1812 static NICKSERV_FUNC(cmd_allowauth
)
1814 struct userNode
*target
;
1815 struct handle_info
*hi
;
1818 NICKSERV_MIN_PARMS(2);
1819 if (!(target
= GetUserH(argv
[1]))) {
1820 reply("MSG_NICK_UNKNOWN", argv
[1]);
1823 if (target
->handle_info
) {
1824 reply("NSMSG_USER_PREV_AUTH", target
->nick
);
1827 if (IsStamped(target
)) {
1828 /* Unauthenticated users might still have been stamped
1829 previously and could therefore have a hidden host;
1830 do not allow them to authenticate to an account. */
1831 reply("NSMSG_USER_PREV_STAMP", target
->nick
);
1836 else if (!(hi
= get_handle_info(argv
[2]))) {
1837 reply("MSG_HANDLE_UNKNOWN", argv
[2]);
1841 if (hi
->opserv_level
> user
->handle_info
->opserv_level
) {
1842 reply("MSG_USER_OUTRANKED", hi
->handle
);
1845 if (((hi
->flags
& (HI_FLAG_SUPPORT_HELPER
|HI_FLAG_NETWORK_HELPER
))
1846 || (hi
->opserv_level
> 0))
1847 && ((argc
< 4) || irccasecmp(argv
[3], "staff"))) {
1848 reply("NSMSG_ALLOWAUTH_STAFF", hi
->handle
);
1851 dict_insert(nickserv_allow_auth_dict
, target
->nick
, hi
);
1852 reply("NSMSG_AUTH_ALLOWED", target
->nick
, hi
->handle
);
1853 send_message(target
, nickserv
, "NSMSG_AUTH_ALLOWED_MSG", hi
->handle
, hi
->handle
);
1854 if (nickserv_conf
.email_enabled
)
1855 send_message(target
, nickserv
, "NSMSG_AUTH_ALLOWED_EMAIL");
1857 if (dict_remove(nickserv_allow_auth_dict
, target
->nick
))
1858 reply("NSMSG_AUTH_NORMAL_ONLY", target
->nick
);
1860 reply("NSMSG_AUTH_UNSPECIAL", target
->nick
);
1862 for (n
=0; n
<allowauth_func_used
; n
++)
1863 allowauth_func_list
[n
](user
, target
, hi
);
1867 static NICKSERV_FUNC(cmd_authcookie
)
1869 struct handle_info
*hi
;
1871 NICKSERV_MIN_PARMS(2);
1872 if (user
->handle_info
) {
1873 reply("NSMSG_ALREADY_AUTHED", user
->handle_info
->handle
);
1876 if (IsStamped(user
)) {
1877 /* Unauthenticated users might still have been stamped
1878 previously and could therefore have a hidden host;
1879 do not allow them to authenticate to an account. */
1880 reply("NSMSG_STAMPED_AUTHCOOKIE");
1883 if (!(hi
= get_handle_info(argv
[1]))) {
1884 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
1887 if (!hi
->email_addr
) {
1888 reply("MSG_SET_EMAIL_ADDR");
1891 nickserv_make_cookie(user
, hi
, ALLOWAUTH
, NULL
, 0);
1895 static NICKSERV_FUNC(cmd_delcookie
)
1897 struct handle_info
*hi
;
1899 hi
= user
->handle_info
;
1901 reply("NSMSG_NO_COOKIE");
1904 switch (hi
->cookie
->type
) {
1907 reply("NSMSG_MUST_TIME_OUT");
1910 nickserv_eat_cookie(hi
->cookie
);
1911 reply("NSMSG_ATE_COOKIE");
1917 static NICKSERV_FUNC(cmd_odelcookie
)
1919 struct handle_info
*hi
;
1921 NICKSERV_MIN_PARMS(2);
1923 if (!(hi
= get_victim_oper(user
, argv
[1])))
1927 reply("NSMSG_NO_COOKIE_FOREIGN", hi
->handle
);
1931 nickserv_eat_cookie(hi
->cookie
);
1932 reply("NSMSG_ATE_FOREIGN_COOKIE", hi
->handle
);
1937 static NICKSERV_FUNC(cmd_resetpass
)
1939 struct handle_info
*hi
;
1940 char crypted
[MD5_CRYPT_LENGTH
];
1943 NICKSERV_MIN_PARMS(3);
1944 if(argc
>= 4 && !strcmp(argv
[3], "WEBLINK"))
1948 if (user
->handle_info
) {
1949 reply("NSMSG_ALREADY_AUTHED", user
->handle_info
->handle
);
1952 if (IsStamped(user
)) {
1953 /* Unauthenticated users might still have been stamped
1954 previously and could therefore have a hidden host;
1955 do not allow them to activate an account. */
1956 reply("NSMSG_STAMPED_RESETPASS");
1959 if (!(hi
= get_handle_info(argv
[1]))) {
1960 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
1963 if (!hi
->email_addr
) {
1964 reply("MSG_SET_EMAIL_ADDR");
1967 cryptpass(argv
[2], crypted
);
1969 nickserv_make_cookie(user
, hi
, PASSWORD_CHANGE
, crypted
, weblink
);
1973 static NICKSERV_FUNC(cmd_cookie
)
1975 struct handle_info
*hi
;
1978 if ((argc
== 2) && (hi
= user
->handle_info
) && hi
->cookie
&& (hi
->cookie
->type
== EMAIL_CHANGE
)) {
1981 NICKSERV_MIN_PARMS(3);
1982 if (!(hi
= get_handle_info(argv
[1]))) {
1983 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
1989 if (HANDLE_FLAGGED(hi
, SUSPENDED
)) {
1990 reply("NSMSG_HANDLE_SUSPENDED");
1995 reply("NSMSG_NO_COOKIE");
1999 /* Check validity of operation before comparing cookie to
2000 * prohibit guessing by authed users. */
2001 if (user
->handle_info
2002 && (hi
->cookie
->type
!= EMAIL_CHANGE
)
2003 && (hi
->cookie
->type
!= PASSWORD_CHANGE
)) {
2004 reply("NSMSG_CANNOT_COOKIE");
2008 if (strcmp(cookie
, hi
->cookie
->cookie
)) {
2009 reply("NSMSG_BAD_COOKIE");
2013 switch (hi
->cookie
->type
) {
2015 safestrncpy(hi
->passwd
, hi
->cookie
->data
, sizeof(hi
->passwd
));
2016 set_user_handle_info(user
, hi
, 1);
2017 reply("NSMSG_HANDLE_ACTIVATED");
2018 if (nickserv_conf
.sync_log
)
2019 SyncLog("ACCOUNTACC %s", hi
->handle
);
2021 case PASSWORD_CHANGE
:
2022 set_user_handle_info(user
, hi
, 1);
2023 safestrncpy(hi
->passwd
, hi
->cookie
->data
, sizeof(hi
->passwd
));
2024 reply("NSMSG_PASSWORD_CHANGED");
2025 if (nickserv_conf
.sync_log
)
2026 SyncLog("PASSCHANGE %s %s", hi
->handle
, hi
->passwd
);
2029 if (!hi
->email_addr
&& nickserv_conf
.sync_log
) {
2031 * This should only happen if an OREGISTER was sent. Require
2032 * email must be enabled! - SiRVulcaN
2034 SyncLog("REGISTER %s %s %s %s", hi
->handle
, hi
->passwd
, hi
->cookie
->data
, user
->info
);
2036 nickserv_set_email_addr(hi
, hi
->cookie
->data
);
2037 reply("NSMSG_EMAIL_CHANGED");
2038 if (nickserv_conf
.sync_log
)
2039 SyncLog("EMAILCHANGE %s %s", hi
->handle
, hi
->cookie
->data
);
2042 set_user_handle_info(user
, hi
, 1);
2043 reply("NSMSG_AUTH_SUCCESS");
2046 reply("NSMSG_BAD_COOKIE_TYPE", hi
->cookie
->type
);
2047 log_module(NS_LOG
, LOG_ERROR
, "Bad cookie type %d for account %s.", hi
->cookie
->type
, hi
->handle
);
2051 nickserv_eat_cookie(hi
->cookie
);
2056 static NICKSERV_FUNC(cmd_oregnick
) {
2058 struct handle_info
*target
;
2059 struct nick_info
*ni
;
2061 NICKSERV_MIN_PARMS(3);
2062 if (!(target
= modcmd_get_handle_info(user
, argv
[1])))
2065 if (!is_registerable_nick(nick
)) {
2066 reply("NSMSG_BAD_NICK", nick
);
2069 ni
= dict_find(nickserv_nick_dict
, nick
, NULL
);
2071 reply("NSMSG_NICK_EXISTS", nick
);
2074 register_nick(nick
, target
);
2075 reply("NSMSG_OREGNICK_SUCCESS", nick
, target
->handle
);
2079 static NICKSERV_FUNC(cmd_regnick
) {
2081 struct nick_info
*ni
;
2083 if (!is_registerable_nick(user
->nick
)) {
2084 reply("NSMSG_BAD_NICK", user
->nick
);
2087 /* count their nicks, see if it's too many */
2088 for (n
=0,ni
=user
->handle_info
->nicks
; ni
; n
++,ni
=ni
->next
) ;
2089 if (n
>= nickserv_conf
.nicks_per_handle
) {
2090 reply("NSMSG_TOO_MANY_NICKS");
2093 ni
= dict_find(nickserv_nick_dict
, user
->nick
, NULL
);
2095 reply("NSMSG_NICK_EXISTS", user
->nick
);
2098 register_nick(user
->nick
, user
->handle_info
);
2099 reply("NSMSG_REGNICK_SUCCESS", user
->nick
);
2103 static NICKSERV_FUNC(cmd_pass
)
2105 struct handle_info
*hi
;
2106 const char *old_pass
, *new_pass
;
2108 NICKSERV_MIN_PARMS(3);
2109 hi
= user
->handle_info
;
2113 if (!is_secure_password(hi
->handle
, new_pass
, user
)) return 0;
2114 if (!checkpass(old_pass
, hi
->passwd
)) {
2115 argv
[1] = "BADPASS";
2116 reply("NSMSG_PASSWORD_INVALID");
2119 cryptpass(new_pass
, hi
->passwd
);
2120 if (nickserv_conf
.sync_log
)
2121 SyncLog("PASSCHANGE %s %s", hi
->handle
, hi
->passwd
);
2123 reply("NSMSG_PASS_SUCCESS");
2128 nickserv_addmask(struct userNode
*user
, struct handle_info
*hi
, const char *mask
)
2131 char *new_mask
= canonicalize_hostmask(strdup(mask
));
2132 for (i
=0; i
<hi
->masks
->used
; i
++) {
2133 if (!irccasecmp(new_mask
, hi
->masks
->list
[i
])) {
2134 send_message(user
, nickserv
, "NSMSG_ADDMASK_ALREADY", new_mask
);
2139 string_list_append(hi
->masks
, new_mask
);
2140 send_message(user
, nickserv
, "NSMSG_ADDMASK_SUCCESS", new_mask
);
2144 static NICKSERV_FUNC(cmd_addmask
)
2147 char *mask
= generate_hostmask(user
, GENMASK_OMITNICK
|GENMASK_NO_HIDING
|GENMASK_ANY_IDENT
);
2148 int res
= nickserv_addmask(user
, user
->handle_info
, mask
);
2152 if (!is_gline(argv
[1])) {
2153 reply("NSMSG_MASK_INVALID", argv
[1]);
2156 return nickserv_addmask(user
, user
->handle_info
, argv
[1]);
2160 static NICKSERV_FUNC(cmd_oaddmask
)
2162 struct handle_info
*hi
;
2164 NICKSERV_MIN_PARMS(3);
2165 if (!(hi
= get_victim_oper(user
, argv
[1])))
2167 return nickserv_addmask(user
, hi
, argv
[2]);
2171 nickserv_delmask(struct userNode
*user
, struct handle_info
*hi
, const char *del_mask
)
2174 for (i
=0; i
<hi
->masks
->used
; i
++) {
2175 if (!strcmp(del_mask
, hi
->masks
->list
[i
])) {
2176 char *old_mask
= hi
->masks
->list
[i
];
2177 if (hi
->masks
->used
== 1) {
2178 send_message(user
, nickserv
, "NSMSG_DELMASK_NOTLAST");
2181 hi
->masks
->list
[i
] = hi
->masks
->list
[--hi
->masks
->used
];
2182 send_message(user
, nickserv
, "NSMSG_DELMASK_SUCCESS", old_mask
);
2187 send_message(user
, nickserv
, "NSMSG_DELMASK_NOT_FOUND");
2191 static NICKSERV_FUNC(cmd_delmask
)
2193 NICKSERV_MIN_PARMS(2);
2194 return nickserv_delmask(user
, user
->handle_info
, argv
[1]);
2197 static NICKSERV_FUNC(cmd_odelmask
)
2199 struct handle_info
*hi
;
2200 NICKSERV_MIN_PARMS(3);
2201 if (!(hi
= get_victim_oper(user
, argv
[1])))
2203 return nickserv_delmask(user
, hi
, argv
[2]);
2207 nickserv_modify_handle_flags(struct userNode
*user
, struct userNode
*bot
, const char *str
, unsigned long *padded
, unsigned long *premoved
) {
2208 unsigned int nn
, add
= 1, pos
;
2209 unsigned long added
, removed
, flag
;
2211 for (added
=removed
=nn
=0; str
[nn
]; nn
++) {
2213 case '+': add
= 1; break;
2214 case '-': add
= 0; break;
2216 if (!(pos
= handle_inverse_flags
[(unsigned char)str
[nn
]])) {
2217 send_message(user
, bot
, "NSMSG_INVALID_FLAG", str
[nn
]);
2220 if (user
&& (user
->handle_info
->opserv_level
< flag_access_levels
[pos
-1])) {
2221 /* cheesy avoidance of looking up the flag name.. */
2222 send_message(user
, bot
, "NSMSG_FLAG_PRIVILEGED", str
[nn
]);
2225 flag
= 1 << (pos
- 1);
2227 added
|= flag
, removed
&= ~flag
;
2229 removed
|= flag
, added
&= ~flag
;
2234 *premoved
= removed
;
2239 nickserv_apply_flags(struct userNode
*user
, struct handle_info
*hi
, const char *flags
)
2241 unsigned long before
, after
, added
, removed
;
2242 struct userNode
*uNode
;
2244 before
= hi
->flags
& (HI_FLAG_SUPPORT_HELPER
|HI_FLAG_NETWORK_HELPER
);
2245 if (!nickserv_modify_handle_flags(user
, nickserv
, flags
, &added
, &removed
))
2247 hi
->flags
= (hi
->flags
| added
) & ~removed
;
2248 after
= hi
->flags
& (HI_FLAG_SUPPORT_HELPER
|HI_FLAG_NETWORK_HELPER
);
2250 /* Strip helping flag if they're only a support helper and not
2251 * currently in #support. */
2252 if (HANDLE_FLAGGED(hi
, HELPING
) && (after
== HI_FLAG_SUPPORT_HELPER
)) {
2253 struct channelList
*schannels
;
2255 schannels
= chanserv_support_channels();
2256 for (uNode
= hi
->users
; uNode
; uNode
= uNode
->next_authed
) {
2257 for (ii
= 0; ii
< schannels
->used
; ++ii
)
2258 if (GetUserMode(schannels
->list
[ii
], uNode
))
2260 if (ii
< schannels
->used
)
2264 HANDLE_CLEAR_FLAG(hi
, HELPING
);
2267 if (after
&& !before
) {
2268 /* Add user to current helper list. */
2269 for (uNode
= hi
->users
; uNode
; uNode
= uNode
->next_authed
)
2270 userList_append(&curr_helpers
, uNode
);
2271 } else if (!after
&& before
) {
2272 /* Remove user from current helper list. */
2273 for (uNode
= hi
->users
; uNode
; uNode
= uNode
->next_authed
)
2274 userList_remove(&curr_helpers
, uNode
);
2281 set_list(struct userNode
*user
, struct handle_info
*hi
, int override
)
2285 char *set_display
[] = {
2286 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2287 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2290 send_message(user
, nickserv
, "NSMSG_SETTING_LIST");
2292 /* Do this so options are presented in a consistent order. */
2293 for (i
= 0; i
< ArrayLength(set_display
); ++i
)
2294 if ((opt
= dict_find(nickserv_opt_dict
, set_display
[i
], NULL
)))
2295 opt(user
, hi
, override
, 0, NULL
);
2298 static NICKSERV_FUNC(cmd_set
)
2300 struct handle_info
*hi
;
2303 hi
= user
->handle_info
;
2305 set_list(user
, hi
, 0);
2308 if (!(opt
= dict_find(nickserv_opt_dict
, argv
[1], NULL
))) {
2309 reply("NSMSG_INVALID_OPTION", argv
[1]);
2312 return opt(user
, hi
, 0, argc
-1, argv
+1);
2315 static NICKSERV_FUNC(cmd_oset
)
2317 struct handle_info
*hi
;
2320 NICKSERV_MIN_PARMS(2);
2322 if (!(hi
= get_victim_oper(user
, argv
[1])))
2326 set_list(user
, hi
, 0);
2330 if (!(opt
= dict_find(nickserv_opt_dict
, argv
[2], NULL
))) {
2331 reply("NSMSG_INVALID_OPTION", argv
[2]);
2335 return opt(user
, hi
, 1, argc
-2, argv
+2);
2338 static OPTION_FUNC(opt_info
)
2342 if ((argv
[1][0] == '*') && (argv
[1][1] == 0)) {
2344 hi
->infoline
= NULL
;
2346 hi
->infoline
= strdup(unsplit_string(argv
+1, argc
-1, NULL
));
2350 info
= hi
->infoline
? hi
->infoline
: user_find_message(user
, "MSG_NONE");
2351 send_message(user
, nickserv
, "NSMSG_SET_INFO", info
);
2355 static OPTION_FUNC(opt_width
)
2358 hi
->screen_width
= strtoul(argv
[1], NULL
, 0);
2360 if ((hi
->screen_width
> 0) && (hi
->screen_width
< MIN_LINE_SIZE
))
2361 hi
->screen_width
= MIN_LINE_SIZE
;
2362 else if (hi
->screen_width
> MAX_LINE_SIZE
)
2363 hi
->screen_width
= MAX_LINE_SIZE
;
2365 send_message(user
, nickserv
, "NSMSG_SET_WIDTH", hi
->screen_width
);
2369 static OPTION_FUNC(opt_tablewidth
)
2372 hi
->table_width
= strtoul(argv
[1], NULL
, 0);
2374 if ((hi
->table_width
> 0) && (hi
->table_width
< MIN_LINE_SIZE
))
2375 hi
->table_width
= MIN_LINE_SIZE
;
2376 else if (hi
->screen_width
> MAX_LINE_SIZE
)
2377 hi
->table_width
= MAX_LINE_SIZE
;
2379 send_message(user
, nickserv
, "NSMSG_SET_TABLEWIDTH", hi
->table_width
);
2383 static OPTION_FUNC(opt_color
)
2386 if (enabled_string(argv
[1]))
2387 HANDLE_SET_FLAG(hi
, MIRC_COLOR
);
2388 else if (disabled_string(argv
[1]))
2389 HANDLE_CLEAR_FLAG(hi
, MIRC_COLOR
);
2391 send_message(user
, nickserv
, "MSG_INVALID_BINARY", argv
[1]);
2396 send_message(user
, nickserv
, "NSMSG_SET_COLOR", user_find_message(user
, HANDLE_FLAGGED(hi
, MIRC_COLOR
) ? "MSG_ON" : "MSG_OFF"));
2400 static OPTION_FUNC(opt_privmsg
)
2403 if (enabled_string(argv
[1]))
2404 HANDLE_SET_FLAG(hi
, USE_PRIVMSG
);
2405 else if (disabled_string(argv
[1]))
2406 HANDLE_CLEAR_FLAG(hi
, USE_PRIVMSG
);
2408 send_message(user
, nickserv
, "MSG_INVALID_BINARY", argv
[1]);
2413 send_message(user
, nickserv
, "NSMSG_SET_PRIVMSG", user_find_message(user
, HANDLE_FLAGGED(hi
, USE_PRIVMSG
) ? "MSG_ON" : "MSG_OFF"));
2417 static OPTION_FUNC(opt_style
)
2422 if (!irccasecmp(argv
[1], "Zoot"))
2423 hi
->userlist_style
= HI_STYLE_ZOOT
;
2424 else if (!irccasecmp(argv
[1], "def"))
2425 hi
->userlist_style
= HI_STYLE_DEF
;
2428 switch (hi
->userlist_style
) {
2437 send_message(user
, nickserv
, "NSMSG_SET_STYLE", style
);
2441 static OPTION_FUNC(opt_announcements
)
2446 if (enabled_string(argv
[1]))
2447 hi
->announcements
= 'y';
2448 else if (disabled_string(argv
[1]))
2449 hi
->announcements
= 'n';
2450 else if (!strcmp(argv
[1], "?") || !irccasecmp(argv
[1], "default"))
2451 hi
->announcements
= '?';
2453 send_message(user
, nickserv
, "NSMSG_INVALID_ANNOUNCE", argv
[1]);
2458 switch (hi
->announcements
) {
2459 case 'y': choice
= user_find_message(user
, "MSG_ON"); break;
2460 case 'n': choice
= user_find_message(user
, "MSG_OFF"); break;
2461 case '?': choice
= "default"; break;
2462 default: choice
= "unknown"; break;
2464 send_message(user
, nickserv
, "NSMSG_SET_ANNOUNCEMENTS", choice
);
2468 static OPTION_FUNC(opt_password
)
2471 send_message(user
, nickserv
, "NSMSG_USE_CMD_PASS");
2476 cryptpass(argv
[1], hi
->passwd
);
2478 send_message(user
, nickserv
, "NSMSG_SET_PASSWORD", "***");
2482 static OPTION_FUNC(opt_flags
)
2485 unsigned int ii
, flen
;
2488 send_message(user
, nickserv
, "MSG_SETTING_PRIVILEGED", argv
[0]);
2493 nickserv_apply_flags(user
, hi
, argv
[1]);
2495 for (ii
= flen
= 0; handle_flags
[ii
]; ii
++)
2496 if (hi
->flags
& (1 << ii
))
2497 flags
[flen
++] = handle_flags
[ii
];
2500 send_message(user
, nickserv
, "NSMSG_SET_FLAGS", flags
);
2502 send_message(user
, nickserv
, "NSMSG_SET_FLAGS", user_find_message(user
, "MSG_NONE"));
2506 static OPTION_FUNC(opt_email
)
2510 if (!is_valid_email_addr(argv
[1])) {
2511 send_message(user
, nickserv
, "NSMSG_BAD_EMAIL_ADDR");
2514 if ((str
= sendmail_prohibited_address(argv
[1]))) {
2515 send_message(user
, nickserv
, "NSMSG_EMAIL_PROHIBITED", argv
[1], str
);
2518 if (hi
->email_addr
&& !irccasecmp(hi
->email_addr
, argv
[1]))
2519 send_message(user
, nickserv
, "NSMSG_EMAIL_SAME");
2521 nickserv_make_cookie(user
, hi
, EMAIL_CHANGE
, argv
[1], 0);
2523 nickserv_set_email_addr(hi
, argv
[1]);
2525 nickserv_eat_cookie(hi
->cookie
);
2526 send_message(user
, nickserv
, "NSMSG_SET_EMAIL", visible_email_addr(user
, hi
));
2529 send_message(user
, nickserv
, "NSMSG_SET_EMAIL", visible_email_addr(user
, hi
));
2533 static OPTION_FUNC(opt_maxlogins
)
2535 unsigned char maxlogins
;
2537 maxlogins
= strtoul(argv
[1], NULL
, 0);
2538 if ((maxlogins
> nickserv_conf
.hard_maxlogins
) && !override
) {
2539 send_message(user
, nickserv
, "NSMSG_BAD_MAX_LOGINS", nickserv_conf
.hard_maxlogins
);
2542 hi
->maxlogins
= maxlogins
;
2544 maxlogins
= hi
->maxlogins
? hi
->maxlogins
: nickserv_conf
.default_maxlogins
;
2545 send_message(user
, nickserv
, "NSMSG_SET_MAXLOGINS", maxlogins
);
2549 static OPTION_FUNC(opt_language
)
2551 struct language
*lang
;
2553 lang
= language_find(argv
[1]);
2554 if (irccasecmp(lang
->name
, argv
[1]))
2555 send_message(user
, nickserv
, "NSMSG_LANGUAGE_NOT_FOUND", argv
[1], lang
->name
);
2556 hi
->language
= lang
;
2558 send_message(user
, nickserv
, "NSMSG_SET_LANGUAGE", hi
->language
->name
);
2563 oper_try_set_access(struct userNode
*user
, struct userNode
*bot
, struct handle_info
*target
, unsigned int new_level
) {
2564 if (!oper_has_access(user
, bot
, nickserv_conf
.modoper_level
, 0))
2566 if ((user
->handle_info
->opserv_level
< target
->opserv_level
)
2567 || ((user
->handle_info
->opserv_level
== target
->opserv_level
)
2568 && (user
->handle_info
->opserv_level
< 1000))) {
2569 send_message(user
, bot
, "MSG_USER_OUTRANKED", target
->handle
);
2572 if ((user
->handle_info
->opserv_level
< new_level
)
2573 || ((user
->handle_info
->opserv_level
== new_level
)
2574 && (user
->handle_info
->opserv_level
< 1000))) {
2575 send_message(user
, bot
, "NSMSG_OPSERV_LEVEL_BAD");
2578 if (user
->handle_info
== target
) {
2579 send_message(user
, bot
, "MSG_STUPID_ACCESS_CHANGE");
2582 if (target
->opserv_level
== new_level
)
2584 log_module(NS_LOG
, LOG_INFO
, "Account %s setting oper level for account %s to %d (from %d).",
2585 user
->handle_info
->handle
, target
->handle
, new_level
, target
->opserv_level
);
2586 target
->opserv_level
= new_level
;
2590 static OPTION_FUNC(opt_level
)
2595 send_message(user
, nickserv
, "MSG_SETTING_PRIVILEGED", argv
[0]);
2599 res
= (argc
> 1) ? oper_try_set_access(user
, nickserv
, hi
, strtoul(argv
[1], NULL
, 0)) : 0;
2600 send_message(user
, nickserv
, "NSMSG_SET_LEVEL", hi
->opserv_level
);
2604 static OPTION_FUNC(opt_epithet
)
2607 send_message(user
, nickserv
, "MSG_SETTING_PRIVILEGED", argv
[0]);
2611 if ((argc
> 1) && oper_has_access(user
, nickserv
, nickserv_conf
.set_epithet_level
, 0)) {
2612 char *epithet
= unsplit_string(argv
+1, argc
-1, NULL
);
2615 if ((epithet
[0] == '*') && !epithet
[1])
2618 hi
->epithet
= strdup(epithet
);
2622 send_message(user
, nickserv
, "NSMSG_SET_EPITHET", hi
->epithet
);
2624 send_message(user
, nickserv
, "NSMSG_SET_EPITHET", user_find_message(user
, "MSG_NONE"));
2628 static OPTION_FUNC(opt_title
)
2633 send_message(user
, nickserv
, "MSG_SETTING_PRIVILEGED", argv
[0]);
2637 if ((argc
> 1) && oper_has_access(user
, nickserv
, nickserv_conf
.set_title_level
, 0)) {
2639 if (strchr(title
, '.')) {
2640 send_message(user
, nickserv
, "NSMSG_TITLE_INVALID");
2643 if ((strlen(user
->handle_info
->handle
) + strlen(title
) +
2644 strlen(nickserv_conf
.titlehost_suffix
) + 2) > HOSTLEN
) {
2645 send_message(user
, nickserv
, "NSMSG_TITLE_TRUNCATED");
2650 if (!strcmp(title
, "*")) {
2651 hi
->fakehost
= NULL
;
2653 hi
->fakehost
= malloc(strlen(title
)+2);
2654 hi
->fakehost
[0] = '.';
2655 strcpy(hi
->fakehost
+1, title
);
2658 } else if (hi
->fakehost
&& (hi
->fakehost
[0] == '.'))
2659 title
= hi
->fakehost
+ 1;
2663 title
= user_find_message(user
, "MSG_NONE");
2664 send_message(user
, nickserv
, "NSMSG_SET_TITLE", title
);
2668 static OPTION_FUNC(opt_fakehost
)
2673 send_message(user
, nickserv
, "MSG_SETTING_PRIVILEGED", argv
[0]);
2677 if ((argc
> 1) && oper_has_access(user
, nickserv
, nickserv_conf
.set_fakehost_level
, 0)) {
2679 if ((strlen(fake
) > HOSTLEN
) || (fake
[0] == '.')) {
2680 send_message(user
, nickserv
, "NSMSG_FAKEHOST_INVALID", HOSTLEN
);
2684 if (!strcmp(fake
, "*"))
2685 hi
->fakehost
= NULL
;
2687 hi
->fakehost
= strdup(fake
);
2688 fake
= hi
->fakehost
;
2691 fake
= generate_fakehost(hi
);
2693 fake
= user_find_message(user
, "MSG_NONE");
2694 send_message(user
, nickserv
, "NSMSG_SET_FAKEHOST", fake
);
2698 static NICKSERV_FUNC(cmd_reclaim
)
2700 struct handle_info
*hi
;
2701 struct nick_info
*ni
;
2702 struct userNode
*victim
;
2704 NICKSERV_MIN_PARMS(2);
2705 hi
= user
->handle_info
;
2706 ni
= dict_find(nickserv_nick_dict
, argv
[1], 0);
2708 reply("NSMSG_UNKNOWN_NICK", argv
[1]);
2711 if (ni
->owner
!= user
->handle_info
) {
2712 reply("NSMSG_NOT_YOUR_NICK", ni
->nick
);
2715 victim
= GetUserH(ni
->nick
);
2717 reply("MSG_NICK_UNKNOWN", ni
->nick
);
2720 if (victim
== user
) {
2721 reply("NSMSG_NICK_USER_YOU");
2724 nickserv_reclaim(victim
, ni
, nickserv_conf
.reclaim_action
);
2725 switch (nickserv_conf
.reclaim_action
) {
2726 case RECLAIM_NONE
: reply("NSMSG_RECLAIMED_NONE"); break;
2727 case RECLAIM_WARN
: reply("NSMSG_RECLAIMED_WARN", victim
->nick
); break;
2728 case RECLAIM_SVSNICK
: reply("NSMSG_RECLAIMED_SVSNICK", victim
->nick
); break;
2729 case RECLAIM_KILL
: reply("NSMSG_RECLAIMED_KILL", victim
->nick
); break;
2734 static NICKSERV_FUNC(cmd_unregnick
)
2737 struct handle_info
*hi
;
2738 struct nick_info
*ni
;
2740 hi
= user
->handle_info
;
2741 nick
= (argc
< 2) ? user
->nick
: (const char*)argv
[1];
2742 ni
= dict_find(nickserv_nick_dict
, nick
, NULL
);
2744 reply("NSMSG_UNKNOWN_NICK", nick
);
2747 if (hi
!= ni
->owner
) {
2748 reply("NSMSG_NOT_YOUR_NICK", nick
);
2751 reply("NSMSG_UNREGNICK_SUCCESS", ni
->nick
);
2756 static NICKSERV_FUNC(cmd_ounregnick
)
2758 struct nick_info
*ni
;
2760 NICKSERV_MIN_PARMS(2);
2761 if (!(ni
= get_nick_info(argv
[1]))) {
2762 reply("NSMSG_NICK_NOT_REGISTERED", argv
[1]);
2765 if (ni
->owner
->opserv_level
>= user
->handle_info
->opserv_level
) {
2766 reply("MSG_USER_OUTRANKED", ni
->nick
);
2769 reply("NSMSG_UNREGNICK_SUCCESS", ni
->nick
);
2774 static NICKSERV_FUNC(cmd_unregister
)
2776 struct handle_info
*hi
;
2779 NICKSERV_MIN_PARMS(2);
2780 hi
= user
->handle_info
;
2783 if (checkpass(passwd
, hi
->passwd
)) {
2784 nickserv_unregister_handle(hi
, user
);
2787 log_module(NS_LOG
, LOG_INFO
, "Account '%s' tried to unregister with the wrong password.", hi
->handle
);
2788 reply("NSMSG_PASSWORD_INVALID");
2793 static NICKSERV_FUNC(cmd_ounregister
)
2795 struct handle_info
*hi
;
2797 NICKSERV_MIN_PARMS(2);
2798 if (!(hi
= get_victim_oper(user
, argv
[1])))
2800 nickserv_unregister_handle(hi
, user
);
2804 static NICKSERV_FUNC(cmd_status
)
2806 if (nickserv_conf
.disable_nicks
) {
2807 reply("NSMSG_GLOBAL_STATS_NONICK",
2808 dict_size(nickserv_handle_dict
));
2810 if (user
->handle_info
) {
2812 struct nick_info
*ni
;
2813 for (ni
=user
->handle_info
->nicks
; ni
; ni
=ni
->next
) cnt
++;
2814 reply("NSMSG_HANDLE_STATS", cnt
);
2816 reply("NSMSG_HANDLE_NONE");
2818 reply("NSMSG_GLOBAL_STATS",
2819 dict_size(nickserv_handle_dict
),
2820 dict_size(nickserv_nick_dict
));
2825 static NICKSERV_FUNC(cmd_ghost
)
2827 struct userNode
*target
;
2828 char reason
[MAXLEN
];
2830 NICKSERV_MIN_PARMS(2);
2831 if (!(target
= GetUserH(argv
[1]))) {
2832 reply("MSG_NICK_UNKNOWN", argv
[1]);
2835 if (target
== user
) {
2836 reply("NSMSG_CANNOT_GHOST_SELF");
2839 if (!target
->handle_info
|| (target
->handle_info
!= user
->handle_info
)) {
2840 reply("NSMSG_CANNOT_GHOST_USER", target
->nick
);
2843 snprintf(reason
, sizeof(reason
), "Ghost kill on account %s (requested by %s).", target
->handle_info
->handle
, user
->nick
);
2844 DelUser(target
, nickserv
, 1, reason
);
2845 reply("NSMSG_GHOST_KILLED", argv
[1]);
2849 static NICKSERV_FUNC(cmd_vacation
)
2851 HANDLE_SET_FLAG(user
->handle_info
, FROZEN
);
2852 reply("NSMSG_ON_VACATION");
2857 nickserv_saxdb_write(struct saxdb_context
*ctx
) {
2859 struct handle_info
*hi
;
2862 for (it
= dict_first(nickserv_handle_dict
); it
; it
= iter_next(it
)) {
2864 #ifdef WITH_PROTOCOL_BAHAMUT
2867 saxdb_start_record(ctx
, iter_key(it
), 0);
2868 if (hi
->announcements
!= '?') {
2869 flags
[0] = hi
->announcements
;
2871 saxdb_write_string(ctx
, KEY_ANNOUNCEMENTS
, flags
);
2874 struct handle_cookie
*cookie
= hi
->cookie
;
2877 switch (cookie
->type
) {
2878 case ACTIVATION
: type
= KEY_ACTIVATION
; break;
2879 case PASSWORD_CHANGE
: type
= KEY_PASSWORD_CHANGE
; break;
2880 case EMAIL_CHANGE
: type
= KEY_EMAIL_CHANGE
; break;
2881 case ALLOWAUTH
: type
= KEY_ALLOWAUTH
; break;
2882 default: type
= NULL
; break;
2885 saxdb_start_record(ctx
, KEY_COOKIE
, 0);
2886 saxdb_write_string(ctx
, KEY_COOKIE_TYPE
, type
);
2887 saxdb_write_int(ctx
, KEY_COOKIE_EXPIRES
, cookie
->expires
);
2889 saxdb_write_string(ctx
, KEY_COOKIE_DATA
, cookie
->data
);
2890 saxdb_write_string(ctx
, KEY_COOKIE
, cookie
->cookie
);
2891 saxdb_end_record(ctx
);
2895 saxdb_write_string(ctx
, KEY_EMAIL_ADDR
, hi
->email_addr
);
2897 saxdb_write_string(ctx
, KEY_EPITHET
, hi
->epithet
);
2899 saxdb_write_string(ctx
, KEY_FAKEHOST
, hi
->fakehost
);
2903 for (ii
=flen
=0; handle_flags
[ii
]; ++ii
)
2904 if (hi
->flags
& (1 << ii
))
2905 flags
[flen
++] = handle_flags
[ii
];
2907 saxdb_write_string(ctx
, KEY_FLAGS
, flags
);
2909 #ifdef WITH_PROTOCOL_BAHAMUT
2910 saxdb_write_int(ctx
, KEY_ID
, hi
->id
);
2913 saxdb_write_string(ctx
, KEY_INFO
, hi
->infoline
);
2914 if (hi
->last_quit_host
[0])
2915 saxdb_write_string(ctx
, KEY_LAST_QUIT_HOST
, hi
->last_quit_host
);
2916 saxdb_write_int(ctx
, KEY_LAST_SEEN
, hi
->lastseen
);
2917 if (hi
->masks
->used
)
2918 saxdb_write_string_list(ctx
, KEY_MASKS
, hi
->masks
);
2920 saxdb_write_int(ctx
, KEY_MAXLOGINS
, hi
->maxlogins
);
2922 struct string_list
*slist
;
2923 struct nick_info
*ni
;
2925 slist
= alloc_string_list(nickserv_conf
.nicks_per_handle
);
2926 for (ni
= hi
->nicks
; ni
; ni
= ni
->next
) string_list_append(slist
, ni
->nick
);
2927 saxdb_write_string_list(ctx
, KEY_NICKS
, slist
);
2931 if (hi
->opserv_level
)
2932 saxdb_write_int(ctx
, KEY_OPSERV_LEVEL
, hi
->opserv_level
);
2933 if (hi
->language
!= lang_C
)
2934 saxdb_write_string(ctx
, KEY_LANGUAGE
, hi
->language
->name
);
2935 saxdb_write_string(ctx
, KEY_PASSWD
, hi
->passwd
);
2936 saxdb_write_int(ctx
, KEY_REGISTER_ON
, hi
->registered
);
2937 if (hi
->screen_width
)
2938 saxdb_write_int(ctx
, KEY_SCREEN_WIDTH
, hi
->screen_width
);
2939 if (hi
->table_width
)
2940 saxdb_write_int(ctx
, KEY_TABLE_WIDTH
, hi
->table_width
);
2941 flags
[0] = hi
->userlist_style
;
2943 saxdb_write_string(ctx
, KEY_USERLIST_STYLE
, flags
);
2944 saxdb_end_record(ctx
);
2949 static handle_merge_func_t
*handle_merge_func_list
;
2950 static unsigned int handle_merge_func_size
= 0, handle_merge_func_used
= 0;
2953 reg_handle_merge_func(handle_merge_func_t func
)
2955 if (handle_merge_func_used
== handle_merge_func_size
) {
2956 if (handle_merge_func_size
) {
2957 handle_merge_func_size
<<= 1;
2958 handle_merge_func_list
= realloc(handle_merge_func_list
, handle_merge_func_size
*sizeof(handle_merge_func_t
));
2960 handle_merge_func_size
= 8;
2961 handle_merge_func_list
= malloc(handle_merge_func_size
*sizeof(handle_merge_func_t
));
2964 handle_merge_func_list
[handle_merge_func_used
++] = func
;
2967 static NICKSERV_FUNC(cmd_merge
)
2969 struct handle_info
*hi_from
, *hi_to
;
2970 struct userNode
*last_user
;
2971 struct userData
*cList
, *cListNext
;
2972 unsigned int ii
, jj
, n
;
2973 char buffer
[MAXLEN
];
2975 NICKSERV_MIN_PARMS(3);
2977 if (!(hi_from
= get_victim_oper(user
, argv
[1])))
2979 if (!(hi_to
= get_victim_oper(user
, argv
[2])))
2981 if (hi_to
== hi_from
) {
2982 reply("NSMSG_CANNOT_MERGE_SELF", hi_to
->handle
);
2986 for (n
=0; n
<handle_merge_func_used
; n
++)
2987 handle_merge_func_list
[n
](user
, hi_to
, hi_from
);
2989 /* Append "from" handle's nicks to "to" handle's nick list. */
2991 struct nick_info
*last_ni
;
2992 for (last_ni
=hi_to
->nicks
; last_ni
->next
; last_ni
=last_ni
->next
) ;
2993 last_ni
->next
= hi_from
->nicks
;
2995 while (hi_from
->nicks
) {
2996 hi_from
->nicks
->owner
= hi_to
;
2997 hi_from
->nicks
= hi_from
->nicks
->next
;
3000 /* Merge the hostmasks. */
3001 for (ii
=0; ii
<hi_from
->masks
->used
; ii
++) {
3002 char *mask
= hi_from
->masks
->list
[ii
];
3003 for (jj
=0; jj
<hi_to
->masks
->used
; jj
++)
3004 if (match_ircglobs(hi_to
->masks
->list
[jj
], mask
))
3006 if (jj
==hi_to
->masks
->used
) /* Nothing from the "to" handle covered this mask, so add it. */
3007 string_list_append(hi_to
->masks
, strdup(mask
));
3010 /* Merge the lists of authed users. */
3012 for (last_user
=hi_to
->users
; last_user
->next_authed
; last_user
=last_user
->next_authed
) ;
3013 last_user
->next_authed
= hi_from
->users
;
3015 hi_to
->users
= hi_from
->users
;
3017 /* Repoint the old "from" handle's users. */
3018 for (last_user
=hi_from
->users
; last_user
; last_user
=last_user
->next_authed
) {
3019 last_user
->handle_info
= hi_to
;
3021 hi_from
->users
= NULL
;
3023 /* Merge channel userlists. */
3024 for (cList
=hi_from
->channels
; cList
; cList
=cListNext
) {
3025 struct userData
*cList2
;
3026 cListNext
= cList
->u_next
;
3027 for (cList2
=hi_to
->channels
; cList2
; cList2
=cList2
->u_next
)
3028 if (cList
->channel
== cList2
->channel
)
3030 if (cList2
&& (cList2
->access
>= cList
->access
)) {
3031 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
);
3032 /* keep cList2 in hi_to; remove cList from hi_from */
3033 del_channel_user(cList
, 1);
3036 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
);
3037 /* remove the lower-ranking cList2 from hi_to */
3038 del_channel_user(cList2
, 1);
3040 log_module(NS_LOG
, LOG_INFO
, "Merge: %s had no access in %s", hi_to
->handle
, cList
->channel
->channel
->name
);
3042 /* cList needs to be moved from hi_from to hi_to */
3043 cList
->handle
= hi_to
;
3044 /* Remove from linked list for hi_from */
3045 assert(!cList
->u_prev
);
3046 hi_from
->channels
= cList
->u_next
;
3048 cList
->u_next
->u_prev
= cList
->u_prev
;
3049 /* Add to linked list for hi_to */
3050 cList
->u_prev
= NULL
;
3051 cList
->u_next
= hi_to
->channels
;
3052 if (hi_to
->channels
)
3053 hi_to
->channels
->u_prev
= cList
;
3054 hi_to
->channels
= cList
;
3058 /* Do they get an OpServ level promotion? */
3059 if (hi_from
->opserv_level
> hi_to
->opserv_level
)
3060 hi_to
->opserv_level
= hi_from
->opserv_level
;
3062 /* What about last seen time? */
3063 if (hi_from
->lastseen
> hi_to
->lastseen
)
3064 hi_to
->lastseen
= hi_from
->lastseen
;
3066 /* Does a fakehost carry over? (This intentionally doesn't set it
3067 * for users previously attached to hi_to. They'll just have to
3070 if (hi_from
->fakehost
&& !hi_to
->fakehost
)
3071 hi_to
->fakehost
= strdup(hi_from
->fakehost
);
3073 /* Notify of success. */
3074 sprintf(buffer
, "%s (%s) merged account %s into %s.", user
->nick
, user
->handle_info
->handle
, hi_from
->handle
, hi_to
->handle
);
3075 reply("NSMSG_HANDLES_MERGED", hi_from
->handle
, hi_to
->handle
);
3076 global_message(MESSAGE_RECIPIENT_STAFF
, buffer
);
3078 /* Unregister the "from" handle. */
3079 nickserv_unregister_handle(hi_from
, NULL
);
3084 struct nickserv_discrim
{
3085 unsigned int limit
, min_level
, max_level
;
3086 unsigned long flags_on
, flags_off
;
3087 time_t min_registered
, max_registered
;
3089 enum { SUBSET
, EXACT
, SUPERSET
, LASTQUIT
} hostmask_type
;
3090 const char *nickmask
;
3091 const char *hostmask
;
3092 const char *handlemask
;
3093 const char *emailmask
;
3096 typedef void (*discrim_search_func
)(struct userNode
*source
, struct handle_info
*hi
);
3098 struct discrim_apply_info
{
3099 struct nickserv_discrim
*discrim
;
3100 discrim_search_func func
;
3101 struct userNode
*source
;
3102 unsigned int matched
;
3105 static struct nickserv_discrim
*
3106 nickserv_discrim_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
3109 struct nickserv_discrim
*discrim
;
3111 discrim
= malloc(sizeof(*discrim
));
3112 memset(discrim
, 0, sizeof(*discrim
));
3113 discrim
->min_level
= 0;
3114 discrim
->max_level
= ~0;
3115 discrim
->limit
= 50;
3116 discrim
->min_registered
= 0;
3117 discrim
->max_registered
= INT_MAX
;
3118 discrim
->lastseen
= now
;
3120 for (i
=0; i
<argc
; i
++) {
3121 if (i
== argc
- 1) {
3122 send_message(user
, nickserv
, "MSG_MISSING_PARAMS", argv
[i
]);
3125 if (!irccasecmp(argv
[i
], "limit")) {
3126 discrim
->limit
= strtoul(argv
[++i
], NULL
, 0);
3127 } else if (!irccasecmp(argv
[i
], "flags")) {
3128 nickserv_modify_handle_flags(user
, nickserv
, argv
[++i
], &discrim
->flags_on
, &discrim
->flags_off
);
3129 } else if (!irccasecmp(argv
[i
], "registered")) {
3130 const char *cmp
= argv
[++i
];
3131 if (cmp
[0] == '<') {
3132 if (cmp
[1] == '=') {
3133 discrim
->min_registered
= now
- ParseInterval(cmp
+2);
3135 discrim
->min_registered
= now
- ParseInterval(cmp
+1) + 1;
3137 } else if (cmp
[0] == '=') {
3138 discrim
->min_registered
= discrim
->max_registered
= now
- ParseInterval(cmp
+1);
3139 } else if (cmp
[0] == '>') {
3140 if (cmp
[1] == '=') {
3141 discrim
->max_registered
= now
- ParseInterval(cmp
+2);
3143 discrim
->max_registered
= now
- ParseInterval(cmp
+1) - 1;
3146 send_message(user
, nickserv
, "MSG_INVALID_CRITERIA", cmp
);
3148 } else if (!irccasecmp(argv
[i
], "seen")) {
3149 discrim
->lastseen
= now
- ParseInterval(argv
[++i
]);
3150 } else if (!nickserv_conf
.disable_nicks
&& !irccasecmp(argv
[i
], "nickmask")) {
3151 discrim
->nickmask
= argv
[++i
];
3152 } else if (!irccasecmp(argv
[i
], "hostmask")) {
3154 if (!irccasecmp(argv
[i
], "exact")) {
3155 if (i
== argc
- 1) {
3156 send_message(user
, nickserv
, "MSG_MISSING_PARAMS", argv
[i
]);
3159 discrim
->hostmask_type
= EXACT
;
3160 } else if (!irccasecmp(argv
[i
], "subset")) {
3161 if (i
== argc
- 1) {
3162 send_message(user
, nickserv
, "MSG_MISSING_PARAMS", argv
[i
]);
3165 discrim
->hostmask_type
= SUBSET
;
3166 } else if (!irccasecmp(argv
[i
], "superset")) {
3167 if (i
== argc
- 1) {
3168 send_message(user
, nickserv
, "MSG_MISSING_PARAMS", argv
[i
]);
3171 discrim
->hostmask_type
= SUPERSET
;
3172 } else if (!irccasecmp(argv
[i
], "lastquit") || !irccasecmp(argv
[i
], "lastauth")) {
3173 if (i
== argc
- 1) {
3174 send_message(user
, nickserv
, "MSG_MISSING_PARAMS", argv
[i
]);
3177 discrim
->hostmask_type
= LASTQUIT
;
3180 discrim
->hostmask_type
= SUPERSET
;
3182 discrim
->hostmask
= argv
[++i
];
3183 } else if (!irccasecmp(argv
[i
], "handlemask") || !irccasecmp(argv
[i
], "accountmask")) {
3184 if (!irccasecmp(argv
[++i
], "*")) {
3185 discrim
->handlemask
= 0;
3187 discrim
->handlemask
= argv
[i
];
3189 } else if (!irccasecmp(argv
[i
], "email")) {
3190 if (user
->handle_info
->opserv_level
< nickserv_conf
.email_search_level
) {
3191 send_message(user
, nickserv
, "MSG_NO_SEARCH_ACCESS", "email");
3193 } else if (!irccasecmp(argv
[++i
], "*")) {
3194 discrim
->emailmask
= 0;
3196 discrim
->emailmask
= argv
[i
];
3198 } else if (!irccasecmp(argv
[i
], "access")) {
3199 const char *cmp
= argv
[++i
];
3200 if (cmp
[0] == '<') {
3201 if (discrim
->min_level
== 0) discrim
->min_level
= 1;
3202 if (cmp
[1] == '=') {
3203 discrim
->max_level
= strtoul(cmp
+2, NULL
, 0);
3205 discrim
->max_level
= strtoul(cmp
+1, NULL
, 0) - 1;
3207 } else if (cmp
[0] == '=') {
3208 discrim
->min_level
= discrim
->max_level
= strtoul(cmp
+1, NULL
, 0);
3209 } else if (cmp
[0] == '>') {
3210 if (cmp
[1] == '=') {
3211 discrim
->min_level
= strtoul(cmp
+2, NULL
, 0);
3213 discrim
->min_level
= strtoul(cmp
+1, NULL
, 0) + 1;
3216 send_message(user
, nickserv
, "MSG_INVALID_CRITERIA", cmp
);
3219 send_message(user
, nickserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
3230 nickserv_discrim_match(struct nickserv_discrim
*discrim
, struct handle_info
*hi
)
3232 if (((discrim
->flags_on
& hi
->flags
) != discrim
->flags_on
)
3233 || (discrim
->flags_off
& hi
->flags
)
3234 || (discrim
->min_registered
> hi
->registered
)
3235 || (discrim
->max_registered
< hi
->registered
)
3236 || (discrim
->lastseen
< (hi
->users
?now
:hi
->lastseen
))
3237 || (discrim
->handlemask
&& !match_ircglob(hi
->handle
, discrim
->handlemask
))
3238 || (discrim
->emailmask
&& (!hi
->email_addr
|| !match_ircglob(hi
->email_addr
, discrim
->emailmask
)))
3239 || (discrim
->min_level
> hi
->opserv_level
)
3240 || (discrim
->max_level
< hi
->opserv_level
)) {
3243 if (discrim
->hostmask
) {
3245 for (i
=0; i
<hi
->masks
->used
; i
++) {
3246 const char *mask
= hi
->masks
->list
[i
];
3247 if ((discrim
->hostmask_type
== SUBSET
)
3248 && (match_ircglobs(discrim
->hostmask
, mask
))) break;
3249 else if ((discrim
->hostmask_type
== EXACT
)
3250 && !irccasecmp(discrim
->hostmask
, mask
)) break;
3251 else if ((discrim
->hostmask_type
== SUPERSET
)
3252 && (match_ircglobs(mask
, discrim
->hostmask
))) break;
3253 else if ((discrim
->hostmask_type
== LASTQUIT
)
3254 && (match_ircglobs(discrim
->hostmask
, hi
->last_quit_host
))) break;
3256 if (i
==hi
->masks
->used
) return 0;
3258 if (discrim
->nickmask
) {
3259 struct nick_info
*nick
= hi
->nicks
;
3261 if (match_ircglob(nick
->nick
, discrim
->nickmask
)) break;
3264 if (!nick
) return 0;
3270 nickserv_discrim_search(struct nickserv_discrim
*discrim
, discrim_search_func dsf
, struct userNode
*source
)
3272 dict_iterator_t it
, next
;
3273 unsigned int matched
;
3275 for (it
= dict_first(nickserv_handle_dict
), matched
= 0;
3276 it
&& (matched
< discrim
->limit
);
3278 next
= iter_next(it
);
3279 if (nickserv_discrim_match(discrim
, iter_data(it
))) {
3280 dsf(source
, iter_data(it
));
3288 search_print_func(struct userNode
*source
, struct handle_info
*match
)
3290 send_message(source
, nickserv
, "NSMSG_SEARCH_MATCH", match
->handle
);
3294 search_count_func(UNUSED_ARG(struct userNode
*source
), UNUSED_ARG(struct handle_info
*match
))
3299 search_unregister_func (struct userNode
*source
, struct handle_info
*match
)
3301 if (oper_has_access(source
, nickserv
, match
->opserv_level
, 0))
3302 nickserv_unregister_handle(match
, source
);
3306 nickserv_sort_accounts_by_access(const void *a
, const void *b
)
3308 const struct handle_info
*hi_a
= *(const struct handle_info
**)a
;
3309 const struct handle_info
*hi_b
= *(const struct handle_info
**)b
;
3310 if (hi_a
->opserv_level
!= hi_b
->opserv_level
)
3311 return hi_b
->opserv_level
- hi_a
->opserv_level
;
3312 return irccasecmp(hi_a
->handle
, hi_b
->handle
);
3316 nickserv_show_oper_accounts(struct userNode
*user
, struct svccmd
*cmd
)
3318 struct handle_info_list hil
;
3319 struct helpfile_table tbl
;
3324 memset(&hil
, 0, sizeof(hil
));
3325 for (it
= dict_first(nickserv_handle_dict
); it
; it
= iter_next(it
)) {
3326 struct handle_info
*hi
= iter_data(it
);
3327 if (hi
->opserv_level
)
3328 handle_info_list_append(&hil
, hi
);
3330 qsort(hil
.list
, hil
.used
, sizeof(hil
.list
[0]), nickserv_sort_accounts_by_access
);
3331 tbl
.length
= hil
.used
+ 1;
3333 tbl
.flags
= TABLE_NO_FREE
;
3334 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
3335 tbl
.contents
[0] = ary
= malloc(tbl
.width
* sizeof(ary
[0]));
3338 for (ii
= 0; ii
< hil
.used
; ) {
3339 ary
= malloc(tbl
.width
* sizeof(ary
[0]));
3340 ary
[0] = hil
.list
[ii
]->handle
;
3341 ary
[1] = strtab(hil
.list
[ii
]->opserv_level
);
3342 tbl
.contents
[++ii
] = ary
;
3344 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3345 reply("MSG_MATCH_COUNT", hil
.used
);
3346 for (ii
= 0; ii
< hil
.used
; ii
++)
3347 free(tbl
.contents
[ii
]);
3352 static NICKSERV_FUNC(cmd_search
)
3354 struct nickserv_discrim
*discrim
;
3355 discrim_search_func action
;
3356 struct svccmd
*subcmd
;
3357 unsigned int matches
;
3360 NICKSERV_MIN_PARMS(3);
3361 sprintf(buf
, "search %s", argv
[1]);
3362 subcmd
= dict_find(nickserv_service
->commands
, buf
, NULL
);
3363 if (!irccasecmp(argv
[1], "print"))
3364 action
= search_print_func
;
3365 else if (!irccasecmp(argv
[1], "count"))
3366 action
= search_count_func
;
3367 else if (!irccasecmp(argv
[1], "unregister"))
3368 action
= search_unregister_func
;
3370 reply("NSMSG_INVALID_ACTION", argv
[1]);
3374 if (subcmd
&& !svccmd_can_invoke(user
, nickserv
, subcmd
, NULL
, SVCCMD_NOISY
))
3377 discrim
= nickserv_discrim_create(user
, argc
-2, argv
+2);
3381 if (action
== search_print_func
)
3382 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3383 else if (action
== search_count_func
)
3384 discrim
->limit
= INT_MAX
;
3386 matches
= nickserv_discrim_search(discrim
, action
, user
);
3389 reply("MSG_MATCH_COUNT", matches
);
3391 reply("MSG_NO_MATCHES");
3397 static MODCMD_FUNC(cmd_checkpass
)
3399 struct handle_info
*hi
;
3401 NICKSERV_MIN_PARMS(3);
3402 if (!(hi
= get_handle_info(argv
[1]))) {
3403 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
3406 if (checkpass(argv
[2], hi
->passwd
))
3407 reply("CHECKPASS_YES");
3409 reply("CHECKPASS_NO");
3415 nickserv_db_read_handle(const char *handle
, dict_t obj
)
3418 struct string_list
*masks
, *slist
;
3419 struct handle_info
*hi
;
3420 struct userNode
*authed_users
;
3421 unsigned long int id
;
3425 str
= database_get_data(obj
, KEY_ID
, RECDB_QSTRING
);
3426 id
= str
? strtoul(str
, NULL
, 0) : 0;
3427 str
= database_get_data(obj
, KEY_PASSWD
, RECDB_QSTRING
);
3429 log_module(NS_LOG
, LOG_WARNING
, "did not find a password for %s -- skipping user.", handle
);
3432 if ((hi
= get_handle_info(handle
))) {
3433 authed_users
= hi
->users
;
3435 dict_remove(nickserv_handle_dict
, hi
->handle
);
3437 authed_users
= NULL
;
3439 hi
= register_handle(handle
, str
, id
);
3441 hi
->users
= authed_users
;
3442 while (authed_users
) {
3443 authed_users
->handle_info
= hi
;
3444 authed_users
= authed_users
->next_authed
;
3447 masks
= database_get_data(obj
, KEY_MASKS
, RECDB_STRING_LIST
);
3448 hi
->masks
= masks
? string_list_copy(masks
) : alloc_string_list(1);
3449 str
= database_get_data(obj
, KEY_MAXLOGINS
, RECDB_QSTRING
);
3450 hi
->maxlogins
= str
? strtoul(str
, NULL
, 0) : 0;
3451 str
= database_get_data(obj
, KEY_LANGUAGE
, RECDB_QSTRING
);
3452 hi
->language
= language_find(str
? str
: "C");
3453 str
= database_get_data(obj
, KEY_OPSERV_LEVEL
, RECDB_QSTRING
);
3454 hi
->opserv_level
= str
? strtoul(str
, NULL
, 0) : 0;
3455 str
= database_get_data(obj
, KEY_INFO
, RECDB_QSTRING
);
3457 hi
->infoline
= strdup(str
);
3458 str
= database_get_data(obj
, KEY_REGISTER_ON
, RECDB_QSTRING
);
3459 hi
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
3460 str
= database_get_data(obj
, KEY_LAST_SEEN
, RECDB_QSTRING
);
3461 hi
->lastseen
= str
? (time_t)strtoul(str
, NULL
, 0) : hi
->registered
;
3462 /* We want to read the nicks even if disable_nicks is set. This is so
3463 * that we don't lose the nick data entirely. */
3464 slist
= database_get_data(obj
, KEY_NICKS
, RECDB_STRING_LIST
);
3466 for (ii
=0; ii
<slist
->used
; ii
++)
3467 register_nick(slist
->list
[ii
], hi
);
3469 str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
);
3471 for (ii
=0; str
[ii
]; ii
++)
3472 hi
->flags
|= 1 << (handle_inverse_flags
[(unsigned char)str
[ii
]] - 1);
3474 str
= database_get_data(obj
, KEY_USERLIST_STYLE
, RECDB_QSTRING
);
3475 hi
->userlist_style
= str
? str
[0] : HI_STYLE_ZOOT
;
3476 str
= database_get_data(obj
, KEY_ANNOUNCEMENTS
, RECDB_QSTRING
);
3477 hi
->announcements
= str
? str
[0] : '?';
3478 str
= database_get_data(obj
, KEY_SCREEN_WIDTH
, RECDB_QSTRING
);
3479 hi
->screen_width
= str
? strtoul(str
, NULL
, 0) : 0;
3480 str
= database_get_data(obj
, KEY_TABLE_WIDTH
, RECDB_QSTRING
);
3481 hi
->table_width
= str
? strtoul(str
, NULL
, 0) : 0;
3482 str
= database_get_data(obj
, KEY_LAST_QUIT_HOST
, RECDB_QSTRING
);
3484 str
= database_get_data(obj
, KEY_LAST_AUTHED_HOST
, RECDB_QSTRING
);
3486 safestrncpy(hi
->last_quit_host
, str
, sizeof(hi
->last_quit_host
));
3487 str
= database_get_data(obj
, KEY_EMAIL_ADDR
, RECDB_QSTRING
);
3489 nickserv_set_email_addr(hi
, str
);
3490 str
= database_get_data(obj
, KEY_EPITHET
, RECDB_QSTRING
);
3492 hi
->epithet
= strdup(str
);
3493 str
= database_get_data(obj
, KEY_FAKEHOST
, RECDB_QSTRING
);
3495 hi
->fakehost
= strdup(str
);
3496 subdb
= database_get_data(obj
, KEY_COOKIE
, RECDB_OBJECT
);
3498 const char *data
, *type
, *expires
, *cookie_str
;
3499 struct handle_cookie
*cookie
;
3501 cookie
= calloc(1, sizeof(*cookie
));
3502 type
= database_get_data(subdb
, KEY_COOKIE_TYPE
, RECDB_QSTRING
);
3503 data
= database_get_data(subdb
, KEY_COOKIE_DATA
, RECDB_QSTRING
);
3504 expires
= database_get_data(subdb
, KEY_COOKIE_EXPIRES
, RECDB_QSTRING
);
3505 cookie_str
= database_get_data(subdb
, KEY_COOKIE
, RECDB_QSTRING
);
3506 if (!type
|| !expires
|| !cookie_str
) {
3507 log_module(NS_LOG
, LOG_ERROR
, "Missing field(s) from cookie for account %s; dropping cookie.", hi
->handle
);
3510 if (!irccasecmp(type
, KEY_ACTIVATION
))
3511 cookie
->type
= ACTIVATION
;
3512 else if (!irccasecmp(type
, KEY_PASSWORD_CHANGE
))
3513 cookie
->type
= PASSWORD_CHANGE
;
3514 else if (!irccasecmp(type
, KEY_EMAIL_CHANGE
))
3515 cookie
->type
= EMAIL_CHANGE
;
3516 else if (!irccasecmp(type
, KEY_ALLOWAUTH
))
3517 cookie
->type
= ALLOWAUTH
;
3519 log_module(NS_LOG
, LOG_ERROR
, "Invalid cookie type %s for account %s; dropping cookie.", type
, handle
);
3522 cookie
->expires
= strtoul(expires
, NULL
, 0);
3523 if (cookie
->expires
< now
)
3526 cookie
->data
= strdup(data
);
3527 safestrncpy(cookie
->cookie
, cookie_str
, sizeof(cookie
->cookie
));
3531 nickserv_bake_cookie(cookie
);
3533 nickserv_free_cookie(cookie
);
3538 nickserv_saxdb_read(dict_t db
) {
3540 struct record_data
*rd
;
3542 for (it
=dict_first(db
); it
; it
=iter_next(it
)) {
3544 nickserv_db_read_handle(iter_key(it
), rd
->d
.object
);
3549 static NICKSERV_FUNC(cmd_mergedb
)
3551 struct timeval start
, stop
;
3554 NICKSERV_MIN_PARMS(2);
3555 gettimeofday(&start
, NULL
);
3556 if (!(db
= parse_database(argv
[1]))) {
3557 reply("NSMSG_DB_UNREADABLE", argv
[1]);
3560 nickserv_saxdb_read(db
);
3562 gettimeofday(&stop
, NULL
);
3563 stop
.tv_sec
-= start
.tv_sec
;
3564 stop
.tv_usec
-= start
.tv_usec
;
3565 if (stop
.tv_usec
< 0) {
3567 stop
.tv_usec
+= 1000000;
3569 reply("NSMSG_DB_MERGED", argv
[1], stop
.tv_sec
, stop
.tv_usec
/1000);
3574 expire_handles(UNUSED_ARG(void *data
))
3576 dict_iterator_t it
, next
;
3578 struct handle_info
*hi
;
3580 for (it
=dict_first(nickserv_handle_dict
); it
; it
=next
) {
3581 next
= iter_next(it
);
3583 if ((hi
->opserv_level
> 0)
3585 || HANDLE_FLAGGED(hi
, FROZEN
)
3586 || HANDLE_FLAGGED(hi
, NODELETE
)) {
3589 expiry
= hi
->channels
? nickserv_conf
.handle_expire_delay
: nickserv_conf
.nochan_handle_expire_delay
;
3590 if ((now
- hi
->lastseen
) > expiry
) {
3591 log_module(NS_LOG
, LOG_INFO
, "Expiring account %s for inactivity.", hi
->handle
);
3592 nickserv_unregister_handle(hi
, NULL
);
3596 if (nickserv_conf
.handle_expire_frequency
)
3597 timeq_add(now
+ nickserv_conf
.handle_expire_frequency
, expire_handles
, NULL
);
3601 nickserv_load_dict(const char *fname
)
3605 if (!(file
= fopen(fname
, "r"))) {
3606 log_module(NS_LOG
, LOG_ERROR
, "Unable to open dictionary file %s: %s", fname
, strerror(errno
));
3609 while (!feof(file
)) {
3610 fgets(line
, sizeof(line
), file
);
3613 if (line
[strlen(line
)-1] == '\n')
3614 line
[strlen(line
)-1] = 0;
3615 dict_insert(nickserv_conf
.weak_password_dict
, strdup(line
), NULL
);
3618 log_module(NS_LOG
, LOG_INFO
, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf
.weak_password_dict
));
3621 static enum reclaim_action
3622 reclaim_action_from_string(const char *str
) {
3624 return RECLAIM_NONE
;
3625 else if (!irccasecmp(str
, "warn"))
3626 return RECLAIM_WARN
;
3627 else if (!irccasecmp(str
, "svsnick"))
3628 return RECLAIM_SVSNICK
;
3629 else if (!irccasecmp(str
, "kill"))
3630 return RECLAIM_KILL
;
3632 return RECLAIM_NONE
;
3636 nickserv_conf_read(void)
3638 dict_t conf_node
, child
;
3642 if (!(conf_node
= conf_get_data(NICKSERV_CONF_NAME
, RECDB_OBJECT
))) {
3643 log_module(NS_LOG
, LOG_ERROR
, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME
);
3646 str
= database_get_data(conf_node
, KEY_VALID_HANDLE_REGEX
, RECDB_QSTRING
);
3648 str
= database_get_data(conf_node
, KEY_VALID_ACCOUNT_REGEX
, RECDB_QSTRING
);
3649 if (nickserv_conf
.valid_handle_regex_set
)
3650 regfree(&nickserv_conf
.valid_handle_regex
);
3652 int err
= regcomp(&nickserv_conf
.valid_handle_regex
, str
, REG_EXTENDED
|REG_ICASE
|REG_NOSUB
);
3653 nickserv_conf
.valid_handle_regex_set
= !err
;
3654 if (err
) log_module(NS_LOG
, LOG_ERROR
, "Bad valid_account_regex (error %d)", err
);
3656 nickserv_conf
.valid_handle_regex_set
= 0;
3658 str
= database_get_data(conf_node
, KEY_VALID_NICK_REGEX
, RECDB_QSTRING
);
3659 if (nickserv_conf
.valid_nick_regex_set
)
3660 regfree(&nickserv_conf
.valid_nick_regex
);
3662 int err
= regcomp(&nickserv_conf
.valid_nick_regex
, str
, REG_EXTENDED
|REG_ICASE
|REG_NOSUB
);
3663 nickserv_conf
.valid_nick_regex_set
= !err
;
3664 if (err
) log_module(NS_LOG
, LOG_ERROR
, "Bad valid_nick_regex (error %d)", err
);
3666 nickserv_conf
.valid_nick_regex_set
= 0;
3668 str
= database_get_data(conf_node
, KEY_NICKS_PER_HANDLE
, RECDB_QSTRING
);
3670 str
= database_get_data(conf_node
, KEY_NICKS_PER_ACCOUNT
, RECDB_QSTRING
);
3671 nickserv_conf
.nicks_per_handle
= str
? strtoul(str
, NULL
, 0) : 4;
3672 str
= database_get_data(conf_node
, KEY_DISABLE_NICKS
, RECDB_QSTRING
);
3673 nickserv_conf
.disable_nicks
= str
? strtoul(str
, NULL
, 0) : 0;
3674 str
= database_get_data(conf_node
, KEY_DEFAULT_HOSTMASK
, RECDB_QSTRING
);
3675 nickserv_conf
.default_hostmask
= str
? !disabled_string(str
) : 0;
3676 str
= database_get_data(conf_node
, KEY_PASSWORD_MIN_LENGTH
, RECDB_QSTRING
);
3677 nickserv_conf
.password_min_length
= str
? strtoul(str
, NULL
, 0) : 0;
3678 str
= database_get_data(conf_node
, KEY_PASSWORD_MIN_DIGITS
, RECDB_QSTRING
);
3679 nickserv_conf
.password_min_digits
= str
? strtoul(str
, NULL
, 0) : 0;
3680 str
= database_get_data(conf_node
, KEY_PASSWORD_MIN_UPPER
, RECDB_QSTRING
);
3681 nickserv_conf
.password_min_upper
= str
? strtoul(str
, NULL
, 0) : 0;
3682 str
= database_get_data(conf_node
, KEY_PASSWORD_MIN_LOWER
, RECDB_QSTRING
);
3683 nickserv_conf
.password_min_lower
= str
? strtoul(str
, NULL
, 0) : 0;
3684 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
3685 nickserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
3686 str
= database_get_data(conf_node
, KEY_MODOPER_LEVEL
, RECDB_QSTRING
);
3687 nickserv_conf
.modoper_level
= str
? strtoul(str
, NULL
, 0) : 900;
3688 str
= database_get_data(conf_node
, KEY_SET_EPITHET_LEVEL
, RECDB_QSTRING
);
3689 nickserv_conf
.set_epithet_level
= str
? strtoul(str
, NULL
, 0) : 1;
3690 str
= database_get_data(conf_node
, KEY_SET_TITLE_LEVEL
, RECDB_QSTRING
);
3691 nickserv_conf
.set_title_level
= str
? strtoul(str
, NULL
, 0) : 900;
3692 str
= database_get_data(conf_node
, KEY_SET_FAKEHOST_LEVEL
, RECDB_QSTRING
);
3693 nickserv_conf
.set_fakehost_level
= str
? strtoul(str
, NULL
, 0) : 1000;
3694 str
= database_get_data(conf_node
, KEY_HANDLE_EXPIRE_FREQ
, RECDB_QSTRING
);
3696 str
= database_get_data(conf_node
, KEY_ACCOUNT_EXPIRE_FREQ
, RECDB_QSTRING
);
3697 nickserv_conf
.handle_expire_frequency
= str
? ParseInterval(str
) : 86400;
3698 str
= database_get_data(conf_node
, KEY_HANDLE_EXPIRE_DELAY
, RECDB_QSTRING
);
3700 str
= database_get_data(conf_node
, KEY_ACCOUNT_EXPIRE_DELAY
, RECDB_QSTRING
);
3701 nickserv_conf
.handle_expire_delay
= str
? ParseInterval(str
) : 86400*30;
3702 str
= database_get_data(conf_node
, KEY_NOCHAN_HANDLE_EXPIRE_DELAY
, RECDB_QSTRING
);
3704 str
= database_get_data(conf_node
, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY
, RECDB_QSTRING
);
3705 nickserv_conf
.nochan_handle_expire_delay
= str
? ParseInterval(str
) : 86400*15;
3706 str
= database_get_data(conf_node
, "warn_clone_auth", RECDB_QSTRING
);
3707 nickserv_conf
.warn_clone_auth
= str
? !disabled_string(str
) : 1;
3708 str
= database_get_data(conf_node
, "default_maxlogins", RECDB_QSTRING
);
3709 nickserv_conf
.default_maxlogins
= str
? strtoul(str
, NULL
, 0) : 2;
3710 str
= database_get_data(conf_node
, "hard_maxlogins", RECDB_QSTRING
);
3711 nickserv_conf
.hard_maxlogins
= str
? strtoul(str
, NULL
, 0) : 10;
3712 if (!nickserv_conf
.disable_nicks
) {
3713 str
= database_get_data(conf_node
, "reclaim_action", RECDB_QSTRING
);
3714 nickserv_conf
.reclaim_action
= str
? reclaim_action_from_string(str
) : RECLAIM_NONE
;
3715 str
= database_get_data(conf_node
, "warn_nick_owned", RECDB_QSTRING
);
3716 nickserv_conf
.warn_nick_owned
= str
? enabled_string(str
) : 0;
3717 str
= database_get_data(conf_node
, "auto_reclaim_action", RECDB_QSTRING
);
3718 nickserv_conf
.auto_reclaim_action
= str
? reclaim_action_from_string(str
) : RECLAIM_NONE
;
3719 str
= database_get_data(conf_node
, "auto_reclaim_delay", RECDB_QSTRING
);
3720 nickserv_conf
.auto_reclaim_delay
= str
? ParseInterval(str
) : 0;
3722 child
= database_get_data(conf_node
, KEY_FLAG_LEVELS
, RECDB_OBJECT
);
3723 for (it
=dict_first(child
); it
; it
=iter_next(it
)) {
3724 const char *key
= iter_key(it
), *value
;
3728 if (!strncasecmp(key
, "uc_", 3))
3729 flag
= toupper(key
[3]);
3730 else if (!strncasecmp(key
, "lc_", 3))
3731 flag
= tolower(key
[3]);
3735 if ((pos
= handle_inverse_flags
[flag
])) {
3736 value
= GET_RECORD_QSTRING((struct record_data
*)iter_data(it
));
3737 flag_access_levels
[pos
- 1] = strtoul(value
, NULL
, 0);
3740 if (nickserv_conf
.weak_password_dict
)
3741 dict_delete(nickserv_conf
.weak_password_dict
);
3742 nickserv_conf
.weak_password_dict
= dict_new();
3743 dict_set_free_keys(nickserv_conf
.weak_password_dict
, free
);
3744 dict_insert(nickserv_conf
.weak_password_dict
, strdup("password"), NULL
);
3745 dict_insert(nickserv_conf
.weak_password_dict
, strdup("<password>"), NULL
);
3746 str
= database_get_data(conf_node
, KEY_DICT_FILE
, RECDB_QSTRING
);
3748 nickserv_load_dict(str
);
3749 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
3750 if (nickserv
&& str
)
3751 NickChange(nickserv
, str
, 0);
3752 str
= database_get_data(conf_node
, KEY_AUTOGAG_ENABLED
, RECDB_QSTRING
);
3753 nickserv_conf
.autogag_enabled
= str
? strtoul(str
, NULL
, 0) : 1;
3754 str
= database_get_data(conf_node
, KEY_AUTOGAG_DURATION
, RECDB_QSTRING
);
3755 nickserv_conf
.autogag_duration
= str
? ParseInterval(str
) : 1800;
3756 str
= database_get_data(conf_node
, KEY_EMAIL_VISIBLE_LEVEL
, RECDB_QSTRING
);
3757 nickserv_conf
.email_visible_level
= str
? strtoul(str
, NULL
, 0) : 800;
3758 str
= database_get_data(conf_node
, KEY_EMAIL_ENABLED
, RECDB_QSTRING
);
3759 nickserv_conf
.email_enabled
= str
? enabled_string(str
) : 0;
3760 str
= database_get_data(conf_node
, KEY_SYNC_LOG
, RECDB_QSTRING
);
3761 nickserv_conf
.sync_log
= str
? enabled_string(str
) : 0;
3762 str
= database_get_data(conf_node
, KEY_COOKIE_TIMEOUT
, RECDB_QSTRING
);
3763 nickserv_conf
.cookie_timeout
= str
? ParseInterval(str
) : 24*3600;
3764 str
= database_get_data(conf_node
, KEY_EMAIL_REQUIRED
, RECDB_QSTRING
);
3765 nickserv_conf
.email_required
= (nickserv_conf
.email_enabled
&& str
) ? enabled_string(str
) : 0;
3766 str
= database_get_data(conf_node
, KEY_ACCOUNTS_PER_EMAIL
, RECDB_QSTRING
);
3767 nickserv_conf
.handles_per_email
= str
? strtoul(str
, NULL
, 0) : 1;
3768 str
= database_get_data(conf_node
, KEY_EMAIL_SEARCH_LEVEL
, RECDB_QSTRING
);
3769 nickserv_conf
.email_search_level
= str
? strtoul(str
, NULL
, 0) : 600;
3770 str
= database_get_data(conf_node
, KEY_TITLEHOST_SUFFIX
, RECDB_QSTRING
);
3771 nickserv_conf
.titlehost_suffix
= str
? str
: "example.net";
3772 str
= conf_get_data("server/network", RECDB_QSTRING
);
3773 nickserv_conf
.network_name
= str
? str
: "some IRC network";
3774 if (!nickserv_conf
.auth_policer_params
) {
3775 nickserv_conf
.auth_policer_params
= policer_params_new();
3776 policer_params_set(nickserv_conf
.auth_policer_params
, "size", "5");
3777 policer_params_set(nickserv_conf
.auth_policer_params
, "drain-rate", "0.05");
3779 child
= database_get_data(conf_node
, KEY_AUTH_POLICER
, RECDB_OBJECT
);
3780 for (it
=dict_first(child
); it
; it
=iter_next(it
))
3781 set_policer_param(iter_key(it
), iter_data(it
), nickserv_conf
.auth_policer_params
);
3785 nickserv_reclaim(struct userNode
*user
, struct nick_info
*ni
, enum reclaim_action action
) {
3787 char newnick
[NICKLEN
+1];
3796 send_message(user
, nickserv
, "NSMSG_RECLAIM_WARN", ni
->nick
, ni
->owner
->handle
);
3798 case RECLAIM_SVSNICK
:
3800 snprintf(newnick
, sizeof(newnick
), "Guest%d", rand()%10000
);
3801 } while (GetUserH(newnick
));
3802 irc_svsnick(nickserv
, user
, newnick
);
3805 msg
= user_find_message(user
, "NSMSG_RECLAIM_KILL");
3806 irc_kill(nickserv
, user
, msg
);
3812 nickserv_reclaim_p(void *data
) {
3813 struct userNode
*user
= data
;
3814 struct nick_info
*ni
= get_nick_info(user
->nick
);
3816 nickserv_reclaim(user
, ni
, nickserv_conf
.auto_reclaim_action
);
3820 check_user_nick(struct userNode
*user
) {
3821 struct nick_info
*ni
;
3822 user
->modes
&= ~FLAGS_REGNICK
;
3823 if (!(ni
= get_nick_info(user
->nick
)))
3825 if (user
->handle_info
== ni
->owner
) {
3826 user
->modes
|= FLAGS_REGNICK
;
3830 if (nickserv_conf
.warn_nick_owned
)
3831 send_message(user
, nickserv
, "NSMSG_RECLAIM_WARN", ni
->nick
, ni
->owner
->handle
);
3832 if (nickserv_conf
.auto_reclaim_action
== RECLAIM_NONE
)
3834 if (nickserv_conf
.auto_reclaim_delay
)
3835 timeq_add(now
+ nickserv_conf
.auto_reclaim_delay
, nickserv_reclaim_p
, user
);
3837 nickserv_reclaim(user
, ni
, nickserv_conf
.auto_reclaim_action
);
3842 handle_new_user(struct userNode
*user
)
3844 return check_user_nick(user
);
3848 handle_account(struct userNode
*user
, const char *stamp
)
3850 struct handle_info
*hi
;
3853 #ifdef WITH_PROTOCOL_P10
3854 time_t timestamp
= 0;
3856 colon
= strchr(stamp
, ':');
3857 if(colon
&& colon
[1])
3860 timestamp
= atoi(colon
+1);
3862 hi
= dict_find(nickserv_handle_dict
, stamp
, NULL
);
3863 if(hi
&& timestamp
&& hi
->registered
!= timestamp
)
3865 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
);
3869 hi
= dict_find(nickserv_id_dict
, stamp
, NULL
);
3870 log_module(MAIN_LOG
, LOG_WARNING
, "Using non-P10 code in accounts, not tested at all!");
3874 if (HANDLE_FLAGGED(hi
, SUSPENDED
)) {
3877 set_user_handle_info(user
, hi
, 0);
3879 log_module(MAIN_LOG
, LOG_WARNING
, "%s had unknown account stamp %s.", user
->nick
, stamp
);
3884 handle_nick_change(struct userNode
*user
, const char *old_nick
)
3886 struct handle_info
*hi
;
3888 if ((hi
= dict_find(nickserv_allow_auth_dict
, old_nick
, 0))) {
3889 dict_remove(nickserv_allow_auth_dict
, old_nick
);
3890 dict_insert(nickserv_allow_auth_dict
, user
->nick
, hi
);
3892 timeq_del(0, nickserv_reclaim_p
, user
, TIMEQ_IGNORE_WHEN
);
3893 check_user_nick(user
);
3897 nickserv_remove_user(struct userNode
*user
, UNUSED_ARG(struct userNode
*killer
), UNUSED_ARG(const char *why
))
3899 dict_remove(nickserv_allow_auth_dict
, user
->nick
);
3900 timeq_del(0, nickserv_reclaim_p
, user
, TIMEQ_IGNORE_WHEN
);
3901 set_user_handle_info(user
, NULL
, 0);
3904 static struct modcmd
*
3905 nickserv_define_func(const char *name
, modcmd_func_t func
, int min_level
, int must_auth
, int must_be_qualified
)
3907 if (min_level
> 0) {
3909 sprintf(buf
, "%u", min_level
);
3910 if (must_be_qualified
) {
3911 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), "level", buf
, "flags", "+qualified,+loghostmask", NULL
);
3913 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), "level", buf
, NULL
);
3915 } else if (min_level
== 0) {
3916 if (must_be_qualified
) {
3917 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), "flags", "+helping", NULL
);
3919 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), "flags", "+helping", NULL
);
3922 if (must_be_qualified
) {
3923 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), "flags", "+qualified,+loghostmask", NULL
);
3925 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), NULL
);
3931 nickserv_db_cleanup(void)
3933 unreg_del_user_func(nickserv_remove_user
);
3934 userList_clean(&curr_helpers
);
3935 policer_params_delete(nickserv_conf
.auth_policer_params
);
3936 dict_delete(nickserv_handle_dict
);
3937 dict_delete(nickserv_nick_dict
);
3938 dict_delete(nickserv_opt_dict
);
3939 dict_delete(nickserv_allow_auth_dict
);
3940 dict_delete(nickserv_email_dict
);
3941 dict_delete(nickserv_id_dict
);
3942 dict_delete(nickserv_conf
.weak_password_dict
);
3943 free(auth_func_list
);
3944 free(unreg_func_list
);
3946 free(allowauth_func_list
);
3947 free(handle_merge_func_list
);
3948 free(failpw_func_list
);
3949 if (nickserv_conf
.valid_handle_regex_set
)
3950 regfree(&nickserv_conf
.valid_handle_regex
);
3951 if (nickserv_conf
.valid_nick_regex_set
)
3952 regfree(&nickserv_conf
.valid_nick_regex
);
3956 init_nickserv(const char *nick
)
3959 NS_LOG
= log_register_type("NickServ", "file:nickserv.log");
3960 reg_new_user_func(handle_new_user
);
3961 reg_nick_change_func(handle_nick_change
);
3962 reg_del_user_func(nickserv_remove_user
);
3963 reg_account_func(handle_account
);
3965 /* set up handle_inverse_flags */
3966 memset(handle_inverse_flags
, 0, sizeof(handle_inverse_flags
));
3967 for (i
=0; handle_flags
[i
]; i
++) {
3968 handle_inverse_flags
[(unsigned char)handle_flags
[i
]] = i
+ 1;
3969 flag_access_levels
[i
] = 0;
3972 conf_register_reload(nickserv_conf_read
);
3973 nickserv_opt_dict
= dict_new();
3974 nickserv_email_dict
= dict_new();
3975 dict_set_free_keys(nickserv_email_dict
, free
);
3976 dict_set_free_data(nickserv_email_dict
, nickserv_free_email_addr
);
3978 nickserv_module
= module_register("NickServ", NS_LOG
, "nickserv.help", NULL
);
3979 /* Removed qualified_host as default requirement for AUTH, REGISTER, PASS, etc. nets
3980 * can enable it per command using modcmd. (its a shitty default IMO, and now in 1.3
3981 * a big pain to disable since its nolonger in the config file. ) -Rubin
3983 modcmd_register(nickserv_module
, "AUTH", cmd_auth
, 2, MODCMD_KEEP_BOUND
, "flags", "+loghostmask", NULL
);
3984 nickserv_define_func("ALLOWAUTH", cmd_allowauth
, 0, 1, 0);
3985 nickserv_define_func("REGISTER", cmd_register
, -1, 0, 0);
3986 nickserv_define_func("OREGISTER", cmd_oregister
, 0, 1, 0);
3987 nickserv_define_func("UNREGISTER", cmd_unregister
, -1, 1, 0);
3988 nickserv_define_func("OUNREGISTER", cmd_ounregister
, 0, 1, 0);
3989 nickserv_define_func("ADDMASK", cmd_addmask
, -1, 1, 0);
3990 nickserv_define_func("OADDMASK", cmd_oaddmask
, 0, 1, 0);
3991 nickserv_define_func("DELMASK", cmd_delmask
, -1, 1, 0);
3992 nickserv_define_func("ODELMASK", cmd_odelmask
, 0, 1, 0);
3993 nickserv_define_func("PASS", cmd_pass
, -1, 1, 0);
3994 nickserv_define_func("SET", cmd_set
, -1, 1, 0);
3995 nickserv_define_func("OSET", cmd_oset
, 0, 1, 0);
3996 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo
, -1, 0, 0);
3997 nickserv_define_func("USERINFO", cmd_userinfo
, -1, 1, 0);
3998 nickserv_define_func("RENAME", cmd_rename_handle
, -1, 1, 0);
3999 nickserv_define_func("VACATION", cmd_vacation
, -1, 1, 0);
4000 nickserv_define_func("MERGE", cmd_merge
, 0, 1, 0);
4001 if (!nickserv_conf
.disable_nicks
) {
4002 /* nick management commands */
4003 nickserv_define_func("REGNICK", cmd_regnick
, -1, 1, 0);
4004 nickserv_define_func("OREGNICK", cmd_oregnick
, 0, 1, 0);
4005 nickserv_define_func("UNREGNICK", cmd_unregnick
, -1, 1, 0);
4006 nickserv_define_func("OUNREGNICK", cmd_ounregnick
, 0, 1, 0);
4007 nickserv_define_func("NICKINFO", cmd_nickinfo
, -1, 1, 0);
4008 nickserv_define_func("RECLAIM", cmd_reclaim
, -1, 1, 0);
4010 if (nickserv_conf
.email_enabled
) {
4011 nickserv_define_func("AUTHCOOKIE", cmd_authcookie
, -1, 0, 0);
4012 nickserv_define_func("RESETPASS", cmd_resetpass
, -1, 0, 0);
4013 nickserv_define_func("COOKIE", cmd_cookie
, -1, 0, 0);
4014 nickserv_define_func("DELCOOKIE", cmd_delcookie
, -1, 1, 0);
4015 nickserv_define_func("ODELCOOKIE", cmd_odelcookie
, 0, 1, 0);
4016 dict_insert(nickserv_opt_dict
, "EMAIL", opt_email
);
4018 nickserv_define_func("GHOST", cmd_ghost
, -1, 1, 0);
4019 /* miscellaneous commands */
4020 nickserv_define_func("STATUS", cmd_status
, -1, 0, 0);
4021 nickserv_define_func("SEARCH", cmd_search
, 100, 1, 0);
4022 nickserv_define_func("SEARCH UNREGISTER", NULL
, 800, 1, 0);
4023 nickserv_define_func("MERGEDB", cmd_mergedb
, 999, 1, 0);
4024 nickserv_define_func("CHECKPASS", cmd_checkpass
, 601, 1, 0);
4026 dict_insert(nickserv_opt_dict
, "INFO", opt_info
);
4027 dict_insert(nickserv_opt_dict
, "WIDTH", opt_width
);
4028 dict_insert(nickserv_opt_dict
, "TABLEWIDTH", opt_tablewidth
);
4029 dict_insert(nickserv_opt_dict
, "COLOR", opt_color
);
4030 dict_insert(nickserv_opt_dict
, "PRIVMSG", opt_privmsg
);
4031 dict_insert(nickserv_opt_dict
, "STYLE", opt_style
);
4032 dict_insert(nickserv_opt_dict
, "PASS", opt_password
);
4033 dict_insert(nickserv_opt_dict
, "PASSWORD", opt_password
);
4034 dict_insert(nickserv_opt_dict
, "FLAGS", opt_flags
);
4035 dict_insert(nickserv_opt_dict
, "ACCESS", opt_level
);
4036 dict_insert(nickserv_opt_dict
, "LEVEL", opt_level
);
4037 dict_insert(nickserv_opt_dict
, "EPITHET", opt_epithet
);
4038 if (nickserv_conf
.titlehost_suffix
) {
4039 dict_insert(nickserv_opt_dict
, "TITLE", opt_title
);
4040 dict_insert(nickserv_opt_dict
, "FAKEHOST", opt_fakehost
);
4042 dict_insert(nickserv_opt_dict
, "ANNOUNCEMENTS", opt_announcements
);
4043 dict_insert(nickserv_opt_dict
, "MAXLOGINS", opt_maxlogins
);
4044 dict_insert(nickserv_opt_dict
, "LANGUAGE", opt_language
);
4046 nickserv_handle_dict
= dict_new();
4047 dict_set_free_keys(nickserv_handle_dict
, free
);
4048 dict_set_free_data(nickserv_handle_dict
, free_handle_info
);
4050 nickserv_id_dict
= dict_new();
4051 dict_set_free_keys(nickserv_id_dict
, free
);
4053 nickserv_nick_dict
= dict_new();
4054 dict_set_free_data(nickserv_nick_dict
, free
);
4056 nickserv_allow_auth_dict
= dict_new();
4058 userList_init(&curr_helpers
);
4061 const char *modes
= conf_get_data("services/nickserv/modes", RECDB_QSTRING
);
4062 nickserv
= AddService(nick
, modes
? modes
: NULL
, "Nick Services", NULL
);
4063 nickserv_service
= service_register(nickserv
);
4065 saxdb_register("NickServ", nickserv_saxdb_read
, nickserv_saxdb_write
);
4066 reg_exit_func(nickserv_db_cleanup
);
4067 if(nickserv_conf
.handle_expire_frequency
)
4068 timeq_add(now
+ nickserv_conf
.handle_expire_frequency
, expire_handles
, NULL
);
4069 message_register_table(msgtab
);