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_brief(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 is too simple. You must choose a password that is not just a word or name." },
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_SETTING_LIST_HEADER", "----------------------------------------" },
279 { "NSMSG_SETTING_LIST_END", "-------------End Of Settings------------" },
280 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
281 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
282 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
283 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
284 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
285 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
286 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
287 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
288 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
289 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
290 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
291 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
292 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
293 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
294 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
295 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
296 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
297 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
298 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
299 { "NSEMAIL_ACTIVATION_BODY",
300 "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"
302 "To verify your email address and complete the account registration, log on to %1$s and type the following command:\n"
303 "/msg %3$s@%4$s COOKIE %5$s %2$s\n"
304 "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"
305 "/msg %3$s@%4$s AUTH %5$s your-password\n"
306 "(Please remember to fill in 'your-password' with the actual password you gave to us when you registered.)\n"
307 "OR configure Login-On-Connect (see http://www.afternet.org/login-on-connect for instructions) to connect pre-logged in every time.\n"
309 "If you did NOT request this account, you do not need to do anything.\n"
310 "Please contact the %1$s staff if you have questions, and be sure to check our website." },
311 { "NSEMAIL_ACTIVATION_BODY_WEB",
312 "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"
314 "To verify your email address and complete the account registration, visit the following URL:\n"
315 "http://www.afternet.org/index.php?option=com_registration&task=activate&username=%5$s&cookie=%2$s\n"
317 "If you did NOT request this account, you do not need to do anything.\n"
318 "Please contact the %1$s staff if you have questions, and be sure to check our website." },
319 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
320 { "NSEMAIL_PASSWORD_CHANGE_BODY",
321 "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"
322 "To complete the password change, log on to %1$s and type the following command:\n"
323 "/msg %3$s@%4$s COOKIE %5$s %2$s\n"
324 "If you did NOT request your password to be changed, you do not need to do anything.\n"
325 "Please contact the %1$s staff if you have questions." },
326 { "NSEMAIL_PASSWORD_CHANGE_BODY_WEB",
327 "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"
328 "To complete the password change, click the following URL:\n"
329 "http://www.afternet.org/index.php?option=com_registration&task=passcookie&username=%5$s&cookie=%2$s\n"
330 "If you did NOT request your password to be changed, you do not need to do anything.\n"
331 "Please contact the %1$s staff if you have questions." },
332 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
333 #ifdef stupid_verify_old_email
334 { "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." },
335 { "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." },
337 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
338 { "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." },
339 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
340 { "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." },
341 { "CHECKPASS_YES", "Yes." },
342 { "CHECKPASS_NO", "No." },
346 enum reclaim_action
{
352 static void nickserv_reclaim(struct userNode
*user
, struct nick_info
*ni
, enum reclaim_action action
);
353 static void nickserv_reclaim_p(void *data
);
356 unsigned int disable_nicks
: 1;
357 unsigned int valid_handle_regex_set
: 1;
358 unsigned int valid_nick_regex_set
: 1;
359 unsigned int autogag_enabled
: 1;
360 unsigned int email_enabled
: 1;
361 unsigned int email_required
: 1;
362 unsigned int default_hostmask
: 1;
363 unsigned int warn_nick_owned
: 1;
364 unsigned int warn_clone_auth
: 1;
365 unsigned int sync_log
: 1;
366 unsigned long nicks_per_handle
;
367 unsigned long password_min_length
;
368 unsigned long password_min_digits
;
369 unsigned long password_min_upper
;
370 unsigned long password_min_lower
;
371 unsigned long db_backup_frequency
;
372 unsigned long handle_expire_frequency
;
373 unsigned long autogag_duration
;
374 unsigned long email_visible_level
;
375 unsigned long cookie_timeout
;
376 unsigned long handle_expire_delay
;
377 unsigned long nochan_handle_expire_delay
;
378 unsigned long modoper_level
;
379 unsigned long set_epithet_level
;
380 unsigned long set_title_level
;
381 unsigned long set_fakehost_level
;
382 unsigned long handles_per_email
;
383 unsigned long email_search_level
;
384 const char *network_name
;
385 const char *titlehost_suffix
;
386 regex_t valid_handle_regex
;
387 regex_t valid_nick_regex
;
388 dict_t weak_password_dict
;
389 struct policer_params
*auth_policer_params
;
390 enum reclaim_action reclaim_action
;
391 enum reclaim_action auto_reclaim_action
;
392 unsigned long auto_reclaim_delay
;
393 unsigned char default_maxlogins
;
394 unsigned char hard_maxlogins
;
397 /* We have 2^32 unique account IDs to use. */
398 unsigned long int highest_id
= 0;
401 canonicalize_hostmask(char *mask
)
403 char *out
= mask
, *temp
;
404 if ((temp
= strchr(mask
, '!'))) {
406 while (*temp
) *out
++ = *temp
++;
412 static struct handle_info
*
413 register_handle(const char *handle
, const char *passwd
, UNUSED_ARG(unsigned long id
))
415 struct handle_info
*hi
;
417 #ifdef WITH_PROTOCOL_BAHAMUT
418 char id_base64
[IDLEN
+ 1];
421 /* Assign a unique account ID to the account; note that 0 is
422 an invalid account ID. 1 is therefore the first account ID. */
424 id
= 1 + highest_id
++;
426 /* Note: highest_id is and must always be the highest ID. */
427 if(id
> highest_id
) {
431 inttobase64(id_base64
, id
, IDLEN
);
433 /* Make sure an account with the same ID doesn't exist. If a
434 duplicate is found, log some details and assign a new one.
435 This should be impossible, but it never hurts to expect it. */
436 if ((hi
= dict_find(nickserv_id_dict
, id_base64
, NULL
))) {
437 log_module(NS_LOG
, LOG_WARNING
, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id
, id_base64
, hi
->handle
, handle
);
443 hi
= calloc(1, sizeof(*hi
));
444 hi
->userlist_style
= HI_DEFAULT_STYLE
;
445 hi
->announcements
= '?';
446 hi
->handle
= strdup(handle
);
447 safestrncpy(hi
->passwd
, passwd
, sizeof(hi
->passwd
));
449 dict_insert(nickserv_handle_dict
, hi
->handle
, hi
);
451 #ifdef WITH_PROTOCOL_BAHAMUT
453 dict_insert(nickserv_id_dict
, strdup(id_base64
), hi
);
460 register_nick(const char *nick
, struct handle_info
*owner
)
462 struct nick_info
*ni
;
463 ni
= malloc(sizeof(struct nick_info
));
464 safestrncpy(ni
->nick
, nick
, sizeof(ni
->nick
));
466 ni
->next
= owner
->nicks
;
468 dict_insert(nickserv_nick_dict
, ni
->nick
, ni
);
472 delete_nick(struct nick_info
*ni
)
474 struct nick_info
*last
, *next
;
475 struct userNode
*user
;
476 /* Check to see if we should mark a user as unregistered. */
477 if ((user
= GetUserH(ni
->nick
)) && IsReggedNick(user
)) {
478 user
->modes
&= ~FLAGS_REGNICK
;
481 /* Remove ni from the nick_info linked list. */
482 if (ni
== ni
->owner
->nicks
) {
483 ni
->owner
->nicks
= ni
->next
;
485 last
= ni
->owner
->nicks
;
491 last
->next
= next
->next
;
493 dict_remove(nickserv_nick_dict
, ni
->nick
);
496 static unreg_func_t
*unreg_func_list
;
497 static unsigned int unreg_func_size
= 0, unreg_func_used
= 0;
500 reg_unreg_func(unreg_func_t func
)
502 if (unreg_func_used
== unreg_func_size
) {
503 if (unreg_func_size
) {
504 unreg_func_size
<<= 1;
505 unreg_func_list
= realloc(unreg_func_list
, unreg_func_size
*sizeof(unreg_func_t
));
508 unreg_func_list
= malloc(unreg_func_size
*sizeof(unreg_func_t
));
511 unreg_func_list
[unreg_func_used
++] = func
;
515 nickserv_free_cookie(void *data
)
517 struct handle_cookie
*cookie
= data
;
518 if (cookie
->hi
) cookie
->hi
->cookie
= NULL
;
519 if (cookie
->data
) free(cookie
->data
);
524 free_handle_info(void *vhi
)
526 struct handle_info
*hi
= vhi
;
528 #ifdef WITH_PROTOCOL_BAHAMUT
531 inttobase64(id
, hi
->id
, IDLEN
);
532 dict_remove(nickserv_id_dict
, id
);
535 free_string_list(hi
->masks
);
539 delete_nick(hi
->nicks
);
544 timeq_del(hi
->cookie
->expires
, nickserv_free_cookie
, hi
->cookie
, 0);
545 nickserv_free_cookie(hi
->cookie
);
547 if (hi
->email_addr
) {
548 struct handle_info_list
*hil
= dict_find(nickserv_email_dict
, hi
->email_addr
, NULL
);
549 handle_info_list_remove(hil
, hi
);
551 dict_remove(nickserv_email_dict
, hi
->email_addr
);
556 static void set_user_handle_info(struct userNode
*user
, struct handle_info
*hi
, int stamp
);
559 nickserv_unregister_handle(struct handle_info
*hi
, struct userNode
*notify
)
563 for (n
=0; n
<unreg_func_used
; n
++)
564 unreg_func_list
[n
](notify
, hi
);
566 set_user_handle_info(hi
->users
, NULL
, 0);
568 if (nickserv_conf
.disable_nicks
)
569 send_message(notify
, nickserv
, "NSMSG_UNREGISTER_SUCCESS", hi
->handle
);
571 send_message(notify
, nickserv
, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi
->handle
);
574 if (nickserv_conf
.sync_log
)
575 SyncLog("UNREGISTER %s", hi
->handle
);
577 dict_remove(nickserv_handle_dict
, hi
->handle
);
581 get_handle_info(const char *handle
)
583 return dict_find(nickserv_handle_dict
, handle
, 0);
587 get_nick_info(const char *nick
)
589 return nickserv_conf
.disable_nicks
? 0 : dict_find(nickserv_nick_dict
, nick
, 0);
593 find_handle_in_channel(struct chanNode
*channel
, struct handle_info
*handle
, struct userNode
*except
)
598 for (nn
=0; nn
<channel
->members
.used
; ++nn
) {
599 mn
= channel
->members
.list
[nn
];
600 if ((mn
->user
!= except
) && (mn
->user
->handle_info
== handle
))
607 oper_has_access(struct userNode
*user
, struct userNode
*bot
, unsigned int min_level
, unsigned int quiet
) {
608 if (!user
->handle_info
) {
610 send_message(user
, bot
, "MSG_AUTHENTICATE");
614 if (!IsOper(user
) && (!IsHelping(user
) || min_level
)) {
616 send_message(user
, bot
, "NSMSG_NO_ACCESS");
620 if (HANDLE_FLAGGED(user
->handle_info
, OPER_SUSPENDED
)) {
622 send_message(user
, bot
, "MSG_OPER_SUSPENDED");
626 if (user
->handle_info
->opserv_level
< min_level
) {
628 send_message(user
, bot
, "NSMSG_NO_ACCESS");
636 is_valid_handle(const char *handle
)
638 struct userNode
*user
;
639 /* cant register a juped nick/service nick as handle, to prevent confusion */
640 user
= GetUserH(handle
);
641 if (user
&& IsLocal(user
))
643 /* check against maximum length */
644 if (strlen(handle
) > NICKSERV_HANDLE_LEN
)
646 /* for consistency, only allow account names that could be nicks */
647 if (!is_valid_nick(handle
))
649 /* disallow account names that look like bad words */
650 if (opserv_bad_channel(handle
))
652 /* test either regex or containing all valid chars */
653 if (nickserv_conf
.valid_handle_regex_set
) {
654 int err
= regexec(&nickserv_conf
.valid_handle_regex
, handle
, 0, 0, 0);
657 buff
[regerror(err
, &nickserv_conf
.valid_handle_regex
, buff
, sizeof(buff
))] = 0;
658 log_module(NS_LOG
, LOG_INFO
, "regexec error: %s (%d)", buff
, err
);
662 return !handle
[strspn(handle
, NICKSERV_VALID_CHARS
)];
667 is_registerable_nick(const char *nick
)
669 /* make sure it could be used as an account name */
670 if (!is_valid_handle(nick
))
673 if (strlen(nick
) > NICKLEN
)
675 /* test either regex or as valid handle */
676 if (nickserv_conf
.valid_nick_regex_set
) {
677 int err
= regexec(&nickserv_conf
.valid_nick_regex
, nick
, 0, 0, 0);
680 buff
[regerror(err
, &nickserv_conf
.valid_nick_regex
, buff
, sizeof(buff
))] = 0;
681 log_module(NS_LOG
, LOG_INFO
, "regexec error: %s (%d)", buff
, err
);
689 is_valid_email_addr(const char *email
)
691 return strchr(email
, '@') != NULL
;
695 visible_email_addr(struct userNode
*user
, struct handle_info
*hi
)
697 if (hi
->email_addr
) {
698 if (oper_has_access(user
, nickserv
, nickserv_conf
.email_visible_level
, 1)) {
699 return hi
->email_addr
;
709 smart_get_handle_info(struct userNode
*service
, struct userNode
*user
, const char *name
)
711 struct handle_info
*hi
;
712 struct userNode
*target
;
716 if (!(hi
= get_handle_info(++name
))) {
717 send_message(user
, service
, "MSG_HANDLE_UNKNOWN", name
);
722 if (!(target
= GetUserH(name
))) {
723 send_message(user
, service
, "MSG_NICK_UNKNOWN", name
);
726 if (IsLocal(target
)) {
727 if (IsService(target
))
728 send_message(user
, service
, "NSMSG_USER_IS_SERVICE", target
->nick
);
730 send_message(user
, service
, "MSG_USER_AUTHENTICATE", target
->nick
);
733 if (!(hi
= target
->handle_info
)) {
734 send_message(user
, service
, "MSG_USER_AUTHENTICATE", target
->nick
);
742 oper_outranks(struct userNode
*user
, struct handle_info
*hi
) {
743 if (user
->handle_info
->opserv_level
> hi
->opserv_level
)
745 if (user
->handle_info
->opserv_level
== hi
->opserv_level
) {
746 if ((user
->handle_info
->opserv_level
== 1000)
747 || (user
->handle_info
== hi
)
748 || ((user
->handle_info
->opserv_level
== 0)
749 && !(HANDLE_FLAGGED(hi
, SUPPORT_HELPER
) || HANDLE_FLAGGED(hi
, NETWORK_HELPER
))
750 && HANDLE_FLAGGED(user
->handle_info
, HELPING
))) {
754 send_message(user
, nickserv
, "MSG_USER_OUTRANKED", hi
->handle
);
758 static struct handle_info
*
759 get_victim_oper(struct userNode
*user
, const char *target
)
761 struct handle_info
*hi
;
762 if (!(hi
= smart_get_handle_info(nickserv
, user
, target
)))
764 if (HANDLE_FLAGGED(user
->handle_info
, OPER_SUSPENDED
)) {
765 send_message(user
, nickserv
, "MSG_OPER_SUSPENDED");
768 return oper_outranks(user
, hi
) ? hi
: NULL
;
772 valid_user_for(struct userNode
*user
, struct handle_info
*hi
)
776 /* If no hostmasks on the account, allow it. */
777 if (!hi
->masks
->used
)
779 /* If any hostmask matches, allow it. */
780 for (ii
=0; ii
<hi
->masks
->used
; ii
++)
781 if (user_matches_glob(user
, hi
->masks
->list
[ii
], 0))
783 /* If they are allowauthed to this account, allow it (removing the aa). */
784 if (dict_find(nickserv_allow_auth_dict
, user
->nick
, NULL
) == hi
) {
785 dict_remove(nickserv_allow_auth_dict
, user
->nick
);
788 /* The user is not allowed to use this account. */
793 is_secure_password(const char *handle
, const char *pass
, struct userNode
*user
)
796 unsigned int cnt_digits
= 0, cnt_upper
= 0, cnt_lower
= 0;
798 if (len
< nickserv_conf
.password_min_length
) {
800 send_message(user
, nickserv
, "NSMSG_PASSWORD_SHORT", nickserv_conf
.password_min_length
);
803 if (!irccasecmp(pass
, handle
)) {
805 send_message(user
, nickserv
, "NSMSG_PASSWORD_ACCOUNT");
808 dict_find(nickserv_conf
.weak_password_dict
, pass
, &i
);
811 send_message(user
, nickserv
, "NSMSG_PASSWORD_DICTIONARY");
814 for (i
=0; i
<len
; i
++) {
815 if (isdigit(pass
[i
]))
817 if (isupper(pass
[i
]))
819 if (islower(pass
[i
]))
822 if ((cnt_lower
< nickserv_conf
.password_min_lower
)
823 || (cnt_upper
< nickserv_conf
.password_min_upper
)
824 || (cnt_digits
< nickserv_conf
.password_min_digits
)) {
826 send_message(user
, nickserv
, "NSMSG_PASSWORD_READABLE", nickserv_conf
.password_min_digits
, nickserv_conf
.password_min_upper
, nickserv_conf
.password_min_lower
);
832 static auth_func_t
*auth_func_list
;
833 static unsigned int auth_func_size
= 0, auth_func_used
= 0;
836 reg_auth_func(auth_func_t func
)
838 if (auth_func_used
== auth_func_size
) {
839 if (auth_func_size
) {
840 auth_func_size
<<= 1;
841 auth_func_list
= realloc(auth_func_list
, auth_func_size
*sizeof(auth_func_t
));
844 auth_func_list
= malloc(auth_func_size
*sizeof(auth_func_t
));
847 auth_func_list
[auth_func_used
++] = func
;
850 static handle_rename_func_t
*rf_list
;
851 static unsigned int rf_list_size
, rf_list_used
;
854 reg_handle_rename_func(handle_rename_func_t func
)
856 if (rf_list_used
== rf_list_size
) {
859 rf_list
= realloc(rf_list
, rf_list_size
*sizeof(rf_list
[0]));
862 rf_list
= malloc(rf_list_size
*sizeof(rf_list
[0]));
865 rf_list
[rf_list_used
++] = func
;
869 generate_fakehost(struct handle_info
*handle
)
871 extern const char *hidden_host_suffix
;
872 static char buffer
[HOSTLEN
+1];
874 if (!handle
->fakehost
) {
875 snprintf(buffer
, sizeof(buffer
), "%s.%s", handle
->handle
, hidden_host_suffix
);
877 } else if (handle
->fakehost
[0] == '.') {
878 /* A leading dot indicates the stored value is actually a title. */
879 snprintf(buffer
, sizeof(buffer
), "%s.%s.%s", handle
->handle
, handle
->fakehost
+1, nickserv_conf
.titlehost_suffix
);
882 return handle
->fakehost
;
886 apply_fakehost(struct handle_info
*handle
)
888 struct userNode
*target
;
893 fake
= generate_fakehost(handle
);
894 for (target
= handle
->users
; target
; target
= target
->next_authed
)
895 assign_fakehost(target
, fake
, 1);
899 set_user_handle_info(struct userNode
*user
, struct handle_info
*hi
, int stamp
)
902 struct handle_info
*old_info
;
904 /* This can happen if somebody uses COOKIE while authed, or if
905 * they re-auth to their current handle (which is silly, but users
907 if (user
->handle_info
== hi
)
910 if (user
->handle_info
) {
911 struct userNode
*other
;
914 userList_remove(&curr_helpers
, user
);
916 /* remove from next_authed linked list */
917 if (user
->handle_info
->users
== user
) {
918 user
->handle_info
->users
= user
->next_authed
;
920 for (other
= user
->handle_info
->users
;
921 other
->next_authed
!= user
;
922 other
= other
->next_authed
) ;
923 other
->next_authed
= user
->next_authed
;
925 /* if nobody left on old handle, and they're not an oper, remove !god */
926 if (!user
->handle_info
->users
&& !user
->handle_info
->opserv_level
)
927 HANDLE_CLEAR_FLAG(user
->handle_info
, HELPING
);
928 /* record them as being last seen at this time */
929 user
->handle_info
->lastseen
= now
;
930 /* and record their hostmask */
931 snprintf(user
->handle_info
->last_quit_host
, sizeof(user
->handle_info
->last_quit_host
), "%s@%s", user
->ident
, user
->hostname
);
933 old_info
= user
->handle_info
;
934 user
->handle_info
= hi
;
935 if (hi
&& !hi
->users
&& !hi
->opserv_level
)
936 HANDLE_CLEAR_FLAG(hi
, HELPING
);
937 for (n
=0; n
<auth_func_used
; n
++)
938 auth_func_list
[n
](user
, old_info
);
940 struct nick_info
*ni
;
942 HANDLE_CLEAR_FLAG(hi
, FROZEN
);
943 if (nickserv_conf
.warn_clone_auth
) {
944 struct userNode
*other
;
945 for (other
= hi
->users
; other
; other
= other
->next_authed
)
946 send_message(other
, nickserv
, "NSMSG_CLONE_AUTH", user
->nick
, user
->ident
, user
->hostname
);
948 user
->next_authed
= hi
->users
;
952 userList_append(&curr_helpers
, user
);
954 if (hi
->fakehost
|| old_info
)
958 #ifdef WITH_PROTOCOL_BAHAMUT
959 /* Stamp users with their account ID. */
961 inttobase64(id
, hi
->id
, IDLEN
);
962 #elif WITH_PROTOCOL_P10
963 /* Stamp users with their account name. */
964 char *id
= hi
->handle
;
966 const char *id
= "???";
968 if (!nickserv_conf
.disable_nicks
) {
969 struct nick_info
*ni
;
970 for (ni
= hi
->nicks
; ni
; ni
= ni
->next
) {
971 if (!irccasecmp(user
->nick
, ni
->nick
)) {
972 user
->modes
|= FLAGS_REGNICK
;
977 StampUser(user
, id
, hi
->registered
);
980 if ((ni
= get_nick_info(user
->nick
)) && (ni
->owner
== hi
))
981 timeq_del(0, nickserv_reclaim_p
, user
, TIMEQ_IGNORE_WHEN
);
983 /* We cannot clear the user's account ID, unfortunately. */
984 user
->next_authed
= NULL
;
988 static struct handle_info
*
989 nickserv_register(struct userNode
*user
, struct userNode
*settee
, const char *handle
, const char *passwd
, int no_auth
)
991 struct handle_info
*hi
;
992 struct nick_info
*ni
;
993 char crypted
[MD5_CRYPT_LENGTH
];
995 if ((hi
= dict_find(nickserv_handle_dict
, handle
, NULL
))) {
996 send_message(user
, nickserv
, "NSMSG_HANDLE_EXISTS", handle
);
1000 if(strlen(handle
) > 15)
1002 send_message(user
, nickserv
, "NSMSG_HANDLE_TOLONG", handle
, 15);
1006 if (!is_secure_password(handle
, passwd
, user
))
1009 cryptpass(passwd
, crypted
);
1010 hi
= register_handle(handle
, crypted
, 0);
1011 hi
->masks
= alloc_string_list(1);
1013 hi
->language
= lang_C
;
1014 hi
->registered
= now
;
1016 hi
->flags
= HI_DEFAULT_FLAGS
;
1017 if (settee
&& !no_auth
)
1018 set_user_handle_info(settee
, hi
, 1);
1021 send_message(user
, nickserv
, "NSMSG_OREGISTER_H_SUCCESS");
1022 else if (nickserv_conf
.disable_nicks
)
1023 send_message(user
, nickserv
, "NSMSG_REGISTER_H_SUCCESS");
1024 else if ((ni
= dict_find(nickserv_nick_dict
, user
->nick
, NULL
)))
1025 send_message(user
, nickserv
, "NSMSG_PARTIAL_REGISTER");
1027 register_nick(user
->nick
, hi
);
1028 send_message(user
, nickserv
, "NSMSG_REGISTER_HN_SUCCESS");
1030 if (settee
&& (user
!= settee
))
1031 send_message(settee
, nickserv
, "NSMSG_OREGISTER_VICTIM", user
->nick
, hi
->handle
);
1036 nickserv_bake_cookie(struct handle_cookie
*cookie
)
1038 cookie
->hi
->cookie
= cookie
;
1039 timeq_add(cookie
->expires
, nickserv_free_cookie
, cookie
);
1042 /* Contributed by the great sneep of afternet ;) */
1043 /* Since this gets used in a URL, we want to avoid stuff that confuses
1044 * email clients such as ] and ?. a-z, 0-9 only.
1046 void genpass(char *str
, int len
)
1051 for(i
= 0; i
< len
; i
++)
1055 c
= (char)((float)rand() / (float)RAND_MAX
* (float)256);
1056 } while(!((c
>= 'a' && c
<= 'z') || (c
>= 'A' && c
<= 'Z') || (c
>= '0' && c
<= '9')));
1064 nickserv_make_cookie(struct userNode
*user
, struct handle_info
*hi
, enum cookie_type type
, const char *cookie_data
, int weblink
)
1066 struct handle_cookie
*cookie
;
1067 char subject
[128], body
[4096], *misc
;
1068 const char *netname
, *fmt
;
1072 send_message(user
, nickserv
, "NSMSG_COOKIE_LIVE", hi
->handle
);
1076 cookie
= calloc(1, sizeof(*cookie
));
1078 cookie
->type
= type
;
1079 cookie
->data
= cookie_data
? strdup(cookie_data
) : NULL
;
1081 cookie
->expires
= now
+ nickserv_conf
.cookie_timeout
;
1082 /* Adding dedicated password gen function for more control -Rubin */
1083 genpass(cookie
->cookie
, 10);
1085 *inttobase64(cookie->cookie, rand(), 5);
1086 *inttobase64(cookie->cookie+5, rand(), 5);
1089 netname
= nickserv_conf
.network_name
;
1092 switch (cookie
->type
) {
1094 hi
->passwd
[0] = 0; /* invalidate password */
1095 send_message(user
, nickserv
, "NSMSG_USE_COOKIE_REGISTER");
1096 fmt
= handle_find_message(hi
, "NSEMAIL_ACTIVATION_SUBJECT");
1097 snprintf(subject
, sizeof(subject
), fmt
, netname
);
1100 fmt
= handle_find_message(hi
, "NSEMAIL_ACTIVATION_BODY_WEB");
1102 fmt
= handle_find_message(hi
, "NSEMAIL_ACTIVATION_BODY");
1104 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
, nickserv
->nick
, self
->name
, hi
->handle
);
1107 case PASSWORD_CHANGE
:
1108 send_message(user
, nickserv
, "NSMSG_USE_COOKIE_RESETPASS");
1109 fmt
= handle_find_message(hi
, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1110 snprintf(subject
, sizeof(subject
), fmt
, netname
);
1112 fmt
= handle_find_message(hi
, "NSEMAIL_PASSWORD_CHANGE_BODY_WEB");
1114 fmt
= handle_find_message(hi
, "NSEMAIL_PASSWORD_CHANGE_BODY");
1115 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
, nickserv
->nick
, self
->name
, hi
->handle
);
1118 misc
= hi
->email_addr
;
1119 hi
->email_addr
= cookie
->data
;
1120 #ifdef stupid_verify_old_email
1122 send_message(user
, nickserv
, "NSMSG_USE_COOKIE_EMAIL_2");
1123 fmt
= handle_find_message(hi
, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1124 snprintf(subject
, sizeof(subject
), fmt
, netname
);
1125 fmt
= handle_find_message(hi
, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1126 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
+COOKIELEN
/2, nickserv
->nick
, self
->name
, hi
->handle
, COOKIELEN
/2);
1127 sendmail(nickserv
, hi
, subject
, body
, 1);
1128 fmt
= handle_find_message(hi
, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1129 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
, nickserv
->nick
, self
->name
, hi
->handle
, COOKIELEN
/2, hi
->email_addr
);
1132 send_message(user
, nickserv
, "NSMSG_USE_COOKIE_EMAIL_1");
1133 fmt
= handle_find_message(hi
, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1134 snprintf(subject
, sizeof(subject
), fmt
, netname
);
1135 fmt
= handle_find_message(hi
, "NSEMAIL_EMAIL_VERIFY_BODY");
1136 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
, nickserv
->nick
, self
->name
, hi
->handle
);
1137 sendmail(nickserv
, hi
, subject
, body
, 1);
1139 #ifdef stupid_verify_old_email
1142 hi
->email_addr
= misc
;
1145 fmt
= handle_find_message(hi
, "NSEMAIL_ALLOWAUTH_SUBJECT");
1146 snprintf(subject
, sizeof(subject
), fmt
, netname
);
1147 fmt
= handle_find_message(hi
, "NSEMAIL_ALLOWAUTH_BODY");
1148 snprintf(body
, sizeof(body
), fmt
, netname
, cookie
->cookie
, nickserv
->nick
, self
->name
, hi
->handle
);
1149 send_message(user
, nickserv
, "NSMSG_USE_COOKIE_AUTH");
1152 log_module(NS_LOG
, LOG_ERROR
, "Bad cookie type %d in nickserv_make_cookie.", cookie
->type
);
1156 sendmail(nickserv
, hi
, subject
, body
, first_time
);
1157 nickserv_bake_cookie(cookie
);
1161 nickserv_eat_cookie(struct handle_cookie
*cookie
)
1163 cookie
->hi
->cookie
= NULL
;
1164 timeq_del(cookie
->expires
, nickserv_free_cookie
, cookie
, 0);
1165 nickserv_free_cookie(cookie
);
1169 nickserv_free_email_addr(void *data
)
1171 handle_info_list_clean(data
);
1176 nickserv_set_email_addr(struct handle_info
*hi
, const char *new_email_addr
)
1178 struct handle_info_list
*hil
;
1179 /* Remove from old handle_info_list ... */
1180 if (hi
->email_addr
&& (hil
= dict_find(nickserv_email_dict
, hi
->email_addr
, 0))) {
1181 handle_info_list_remove(hil
, hi
);
1182 if (!hil
->used
) dict_remove(nickserv_email_dict
, hil
->tag
);
1183 hi
->email_addr
= NULL
;
1185 /* Add to the new list.. */
1186 if (new_email_addr
) {
1187 if (!(hil
= dict_find(nickserv_email_dict
, new_email_addr
, 0))) {
1188 hil
= calloc(1, sizeof(*hil
));
1189 hil
->tag
= strdup(new_email_addr
);
1190 handle_info_list_init(hil
);
1191 dict_insert(nickserv_email_dict
, hil
->tag
, hil
);
1193 handle_info_list_append(hil
, hi
);
1194 hi
->email_addr
= hil
->tag
;
1198 static NICKSERV_FUNC(cmd_register
)
1200 struct handle_info
*hi
;
1201 const char *email_addr
, *password
;
1202 char syncpass
[MD5_CRYPT_LENGTH
];
1203 int no_auth
, weblink
;
1205 if (!IsOper(user
) && !dict_size(nickserv_handle_dict
)) {
1206 /* Require the first handle registered to belong to someone +o. */
1207 reply("NSMSG_REQUIRE_OPER");
1211 if (user
->handle_info
) {
1212 reply("NSMSG_USE_RENAME", user
->handle_info
->handle
);
1216 if (IsRegistering(user
)) {
1217 reply("NSMSG_ALREADY_REGISTERING");
1221 if (IsStamped(user
)) {
1222 /* Unauthenticated users might still have been stamped
1223 previously and could therefore have a hidden host;
1224 do not allow them to register a new account. */
1225 reply("NSMSG_STAMPED_REGISTER");
1229 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf
.email_required
);
1231 if (!is_valid_handle(argv
[1])) {
1232 reply("NSMSG_BAD_HANDLE", argv
[1]);
1237 if ((argc
>= 4) && nickserv_conf
.email_enabled
) {
1238 struct handle_info_list
*hil
;
1241 /* Remember email address. */
1242 email_addr
= argv
[3];
1244 /* Check that the email address looks valid.. */
1245 if (!is_valid_email_addr(email_addr
)) {
1246 reply("NSMSG_BAD_EMAIL_ADDR");
1250 /* .. and that we are allowed to send to it. */
1251 if ((str
= sendmail_prohibited_address(email_addr
))) {
1252 reply("NSMSG_EMAIL_PROHIBITED", email_addr
, str
);
1256 /* If we do email verify, make sure we don't spam the address. */
1257 if ((hil
= dict_find(nickserv_email_dict
, email_addr
, NULL
))) {
1259 for (nn
=0; nn
<hil
->used
; nn
++) {
1260 if (hil
->list
[nn
]->cookie
) {
1261 reply("NSMSG_EMAIL_UNACTIVATED");
1265 if (hil
->used
>= nickserv_conf
.handles_per_email
) {
1266 reply("NSMSG_EMAIL_OVERUSED");
1279 /* Webregister hack - send URL instead of IRC cookie
1282 if((argc
>= 5) && !strcmp(argv
[4],"WEBLINK"))
1286 if (!(hi
= nickserv_register(user
, user
, argv
[1], password
, no_auth
)))
1288 /* Add any masks they should get. */
1289 if (nickserv_conf
.default_hostmask
) {
1290 string_list_append(hi
->masks
, strdup("*@*"));
1292 string_list_append(hi
->masks
, generate_hostmask(user
, GENMASK_OMITNICK
|GENMASK_NO_HIDING
|GENMASK_ANY_IDENT
));
1293 if (user
->ip
.s_addr
&& user
->hostname
[strspn(user
->hostname
, "0123456789.")])
1294 string_list_append(hi
->masks
, generate_hostmask(user
, GENMASK_OMITNICK
|GENMASK_BYIP
|GENMASK_NO_HIDING
|GENMASK_ANY_IDENT
));
1297 /* If they're the first to register, give them level 1000. */
1298 if (dict_size(nickserv_handle_dict
) == 1) {
1299 hi
->opserv_level
= 1000;
1300 reply("NSMSG_ROOT_HANDLE", argv
[1]);
1303 /* Set their email address. */
1305 nickserv_set_email_addr(hi
, email_addr
);
1307 /* If they need to do email verification, tell them. */
1309 nickserv_make_cookie(user
, hi
, ACTIVATION
, hi
->passwd
, weblink
);
1311 /* Set registering flag.. */
1312 user
->modes
|= FLAGS_REGISTERING
;
1314 if (nickserv_conf
.sync_log
) {
1315 cryptpass(password
, syncpass
);
1317 * An 0 is only sent if theres no email address. Thios should only happen if email functions are
1318 * disabled which they wont be for us. Email Required MUST be set on if you are using this.
1321 SyncLog("REGISTER %s %s %s %s", hi
->handle
, syncpass
, email_addr
? email_addr
: "0", user
->info
);
1327 static NICKSERV_FUNC(cmd_oregister
)
1330 struct userNode
*settee
;
1331 struct handle_info
*hi
;
1333 NICKSERV_MIN_PARMS(4);
1335 if (!is_valid_handle(argv
[1])) {
1336 reply("NSMSG_BAD_HANDLE", argv
[1]);
1340 if (strchr(argv
[3], '@')) {
1341 mask
= canonicalize_hostmask(strdup(argv
[3]));
1343 settee
= GetUserH(argv
[4]);
1345 reply("MSG_NICK_UNKNOWN", argv
[4]);
1352 } else if ((settee
= GetUserH(argv
[3]))) {
1353 mask
= generate_hostmask(settee
, GENMASK_OMITNICK
|GENMASK_NO_HIDING
|GENMASK_ANY_IDENT
);
1355 reply("NSMSG_REGISTER_BAD_NICKMASK", argv
[3]);
1358 if (settee
&& settee
->handle_info
) {
1359 reply("NSMSG_USER_PREV_AUTH", settee
->nick
);
1363 if (!(hi
= nickserv_register(user
, settee
, argv
[1], argv
[2], 0))) {
1367 string_list_append(hi
->masks
, mask
);
1371 static NICKSERV_FUNC(cmd_handleinfo
)
1374 unsigned int i
, pos
=0, herelen
;
1375 struct userNode
*target
, *next_un
;
1376 struct handle_info
*hi
;
1377 const char *nsmsg_none
;
1380 if (!(hi
= user
->handle_info
)) {
1381 reply("NSMSG_MUST_AUTH");
1384 } else if (!(hi
= modcmd_get_handle_info(user
, argv
[1]))) {
1388 nsmsg_none
= handle_find_message(hi
, "MSG_NONE");
1389 reply("NSMSG_HANDLEINFO_ON", hi
->handle
);
1391 #ifdef WITH_PROTOCOL_BAHAMUT
1392 reply("NSMSG_HANDLEINFO_ID", hi
->id
);
1394 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi
->registered
));
1397 intervalString(buff
, now
- hi
->lastseen
, user
->handle_info
);
1398 reply("NSMSG_HANDLEINFO_LASTSEEN", buff
);
1400 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1403 reply("NSMSG_HANDLEINFO_INFOLINE", (hi
->infoline
? hi
->infoline
: nsmsg_none
));
1404 if (HANDLE_FLAGGED(hi
, FROZEN
))
1405 reply("NSMSG_HANDLEINFO_VACATION");
1407 if (oper_has_access(user
, cmd
->parent
->bot
, 0, 1)) {
1408 struct do_not_register
*dnr
;
1409 if ((dnr
= chanserv_is_dnr(NULL
, hi
)))
1410 reply("NSMSG_HANDLEINFO_DNR", dnr
->setter
, dnr
->reason
);
1411 if (!oper_outranks(user
, hi
))
1413 } else if (hi
!= user
->handle_info
) {
1414 reply("NSMSG_HANDLEINFO_END");
1418 if (nickserv_conf
.email_enabled
)
1419 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user
, hi
));
1423 switch (hi
->cookie
->type
) {
1424 case ACTIVATION
: type
= "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1425 case PASSWORD_CHANGE
: type
= "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1426 case EMAIL_CHANGE
: type
= "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1427 case ALLOWAUTH
: type
= "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1428 default: type
= "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1434 unsigned long flen
= 1;
1435 char flags
[34]; /* 32 bits possible plus '+' and '\0' */
1437 for (i
=0, flen
=1; handle_flags
[i
]; i
++)
1438 if (hi
->flags
& 1 << i
)
1439 flags
[flen
++] = handle_flags
[i
];
1441 reply("NSMSG_HANDLEINFO_FLAGS", flags
);
1443 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none
);
1446 if (HANDLE_FLAGGED(hi
, SUPPORT_HELPER
)
1447 || HANDLE_FLAGGED(hi
, NETWORK_HELPER
)
1448 || (hi
->opserv_level
> 0)) {
1449 reply("NSMSG_HANDLEINFO_EPITHET", (hi
->epithet
? hi
->epithet
: nsmsg_none
));
1453 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi
->fakehost
? hi
->fakehost
: handle_find_message(hi
, "MSG_NONE")));
1455 if (hi
->last_quit_host
[0])
1456 reply("NSMSG_HANDLEINFO_LAST_HOST", hi
->last_quit_host
);
1458 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1460 if (nickserv_conf
.disable_nicks
) {
1461 /* nicks disabled; don't show anything about registered nicks */
1462 } else if (hi
->nicks
) {
1463 struct nick_info
*ni
, *next_ni
;
1464 for (ni
= hi
->nicks
; ni
; ni
= next_ni
) {
1465 herelen
= strlen(ni
->nick
);
1466 if (pos
+ herelen
+ 1 > ArrayLength(buff
)) {
1468 goto print_nicks_buff
;
1472 memcpy(buff
+pos
, ni
->nick
, herelen
);
1473 pos
+= herelen
; buff
[pos
++] = ' ';
1477 reply("NSMSG_HANDLEINFO_NICKS", buff
);
1482 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none
);
1485 if (hi
->masks
->used
) {
1486 for (i
=0; i
< hi
->masks
->used
; i
++) {
1487 herelen
= strlen(hi
->masks
->list
[i
]);
1488 if (pos
+ herelen
+ 1 > ArrayLength(buff
)) {
1490 goto print_mask_buff
;
1492 memcpy(buff
+pos
, hi
->masks
->list
[i
], herelen
);
1493 pos
+= herelen
; buff
[pos
++] = ' ';
1494 if (i
+1 == hi
->masks
->used
) {
1497 reply("NSMSG_HANDLEINFO_MASKS", buff
);
1502 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none
);
1506 struct userData
*channel
, *next
;
1509 for (channel
= hi
->channels
; channel
; channel
= next
) {
1510 next
= channel
->u_next
;
1511 name
= channel
->channel
->channel
->name
;
1512 herelen
= strlen(name
);
1513 if (pos
+ herelen
+ 7 > ArrayLength(buff
)) {
1515 goto print_chans_buff
;
1517 if (IsUserSuspended(channel
))
1519 pos
+= sprintf(buff
+pos
, "%s:%s ", user_level_name_from_level(channel
->access
), name
);
1523 reply("NSMSG_HANDLEINFO_CHANNELS", buff
);
1528 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none
);
1531 for (target
= hi
->users
; target
; target
= next_un
) {
1532 herelen
= strlen(target
->nick
);
1533 if (pos
+ herelen
+ 1 > ArrayLength(buff
)) {
1535 goto print_cnick_buff
;
1537 next_un
= target
->next_authed
;
1539 memcpy(buff
+pos
, target
->nick
, herelen
);
1540 pos
+= herelen
; buff
[pos
++] = ' ';
1544 reply("NSMSG_HANDLEINFO_CURRENT", buff
);
1549 reply("NSMSG_HANDLEINFO_END");
1553 static NICKSERV_FUNC(cmd_userinfo
)
1555 struct userNode
*target
;
1557 NICKSERV_MIN_PARMS(2);
1558 if (!(target
= GetUserH(argv
[1]))) {
1559 reply("MSG_NICK_UNKNOWN", argv
[1]);
1562 if (target
->handle_info
)
1563 reply("NSMSG_USERINFO_AUTHED_AS", target
->nick
, target
->handle_info
->handle
);
1565 reply("NSMSG_USERINFO_NOT_AUTHED", target
->nick
);
1569 static NICKSERV_FUNC(cmd_nickinfo
)
1571 struct nick_info
*ni
;
1573 NICKSERV_MIN_PARMS(2);
1574 if (!(ni
= get_nick_info(argv
[1]))) {
1575 reply("MSG_NICK_UNKNOWN", argv
[1]);
1578 reply("NSMSG_NICKINFO_OWNER", ni
->nick
, ni
->owner
->handle
);
1582 static NICKSERV_FUNC(cmd_rename_handle
)
1584 struct handle_info
*hi
;
1585 char msgbuf
[MAXLEN
], *old_handle
;
1588 NICKSERV_MIN_PARMS(3);
1589 if (!(hi
= get_victim_oper(user
, argv
[1])))
1591 if (!is_valid_handle(argv
[2])) {
1592 reply("NSMSG_FAIL_RENAME", argv
[1], argv
[2]);
1595 if (get_handle_info(argv
[2])) {
1596 reply("NSMSG_HANDLE_EXISTS", argv
[2]);
1599 if(strlen(argv
[2]) > 15)
1601 reply("NMSG_HANDLE_TOLONG", argv
[2], 15);
1605 dict_remove2(nickserv_handle_dict
, old_handle
= hi
->handle
, 1);
1606 hi
->handle
= strdup(argv
[2]);
1607 dict_insert(nickserv_handle_dict
, hi
->handle
, hi
);
1608 for (nn
=0; nn
<rf_list_used
; nn
++)
1609 rf_list
[nn
](hi
, old_handle
);
1610 snprintf(msgbuf
, sizeof(msgbuf
), "%s renamed account %s to %s.", user
->handle_info
->handle
, old_handle
, hi
->handle
);
1611 reply("NSMSG_HANDLE_CHANGED", old_handle
, hi
->handle
);
1612 global_message(MESSAGE_RECIPIENT_STAFF
, msgbuf
);
1617 static failpw_func_t
*failpw_func_list
;
1618 static unsigned int failpw_func_size
= 0, failpw_func_used
= 0;
1621 reg_failpw_func(failpw_func_t func
)
1623 if (failpw_func_used
== failpw_func_size
) {
1624 if (failpw_func_size
) {
1625 failpw_func_size
<<= 1;
1626 failpw_func_list
= realloc(failpw_func_list
, failpw_func_size
*sizeof(failpw_func_t
));
1628 failpw_func_size
= 8;
1629 failpw_func_list
= malloc(failpw_func_size
*sizeof(failpw_func_t
));
1632 failpw_func_list
[failpw_func_used
++] = func
;
1636 * Return hi if the handle/pass pair matches, NULL if it doesnt.
1638 * called by nefariouses enhanced AC login-on-connect code
1641 struct handle_info
*loc_auth(char *handle
, char *password
)
1643 int pw_arg
, used
, maxlogins
;
1646 struct handle_info
*hi
;
1647 struct userNode
*other
;
1649 hi
= dict_find(nickserv_handle_dict
, handle
, NULL
);
1655 /* We don't know the users hostname, or anything because they
1656 * havn't registered yet. So we can only allow LOC if your
1657 * account has *@* as a hostmask.
1659 for (ii
=0; ii
<hi
->masks
->used
; ii
++)
1661 if (!strcmp(hi
->masks
->list
[ii
], "*@*"))
1670 /* Responses from here on look up the language used by the handle they asked about. */
1671 if (!checkpass(password
, hi
->passwd
)) {
1674 if (HANDLE_FLAGGED(hi
, SUSPENDED
)) {
1677 maxlogins
= hi
->maxlogins
? hi
->maxlogins
: nickserv_conf
.default_maxlogins
;
1678 for (used
= 0, other
= hi
->users
; other
; other
= other
->next_authed
) {
1679 if (++used
>= maxlogins
) {
1686 static NICKSERV_FUNC(cmd_auth
)
1688 int pw_arg
, used
, maxlogins
;
1689 struct handle_info
*hi
;
1691 struct userNode
*other
;
1693 if (user
->handle_info
) {
1694 reply("NSMSG_ALREADY_AUTHED", user
->handle_info
->handle
);
1697 if (IsStamped(user
)) {
1698 /* Unauthenticated users might still have been stamped
1699 previously and could therefore have a hidden host;
1700 do not allow them to authenticate. */
1701 reply("NSMSG_STAMPED_AUTH");
1705 hi
= dict_find(nickserv_handle_dict
, argv
[1], NULL
);
1707 } else if (argc
== 2) {
1708 if (nickserv_conf
.disable_nicks
) {
1709 if (!(hi
= get_handle_info(user
->nick
))) {
1710 reply("NSMSG_HANDLE_NOT_FOUND");
1714 /* try to look up their handle from their nick */
1715 struct nick_info
*ni
;
1716 ni
= get_nick_info(user
->nick
);
1718 reply("NSMSG_NICK_NOT_REGISTERED", user
->nick
);
1725 reply("MSG_MISSING_PARAMS", argv
[0]);
1726 svccmd_send_help(user
, nickserv
, cmd
);
1730 reply("NSMSG_HANDLE_NOT_FOUND");
1733 /* Responses from here on look up the language used by the handle they asked about. */
1734 passwd
= argv
[pw_arg
];
1735 if (!valid_user_for(user
, hi
)) {
1736 if (hi
->email_addr
&& nickserv_conf
.email_enabled
)
1737 send_message_type(4, user
, cmd
->parent
->bot
,
1738 handle_find_message(hi
, "NSMSG_USE_AUTHCOOKIE"),
1741 send_message_type(4, user
, cmd
->parent
->bot
,
1742 handle_find_message(hi
, "NSMSG_HOSTMASK_INVALID"),
1744 argv
[pw_arg
] = "BADMASK";
1747 if (!checkpass(passwd
, hi
->passwd
)) {
1749 send_message_type(4, user
, cmd
->parent
->bot
,
1750 handle_find_message(hi
, "NSMSG_PASSWORD_INVALID"));
1751 argv
[pw_arg
] = "BADPASS";
1752 for (n
=0; n
<failpw_func_used
; n
++) failpw_func_list
[n
](user
, hi
);
1753 if (nickserv_conf
.autogag_enabled
) {
1754 if (!user
->auth_policer
.params
) {
1755 user
->auth_policer
.last_req
= now
;
1756 user
->auth_policer
.params
= nickserv_conf
.auth_policer_params
;
1758 if (!policer_conforms(&user
->auth_policer
, now
, 1.0)) {
1760 hostmask
= generate_hostmask(user
, GENMASK_STRICT_HOST
|GENMASK_BYIP
|GENMASK_NO_HIDING
);
1761 log_module(NS_LOG
, LOG_INFO
, "%s auto-gagged for repeated password guessing.", hostmask
);
1762 gag_create(hostmask
, nickserv
->nick
, "Repeated password guessing.", now
+nickserv_conf
.autogag_duration
);
1764 argv
[pw_arg
] = "GAGGED";
1769 if (HANDLE_FLAGGED(hi
, SUSPENDED
)) {
1770 send_message_type(4, user
, cmd
->parent
->bot
,
1771 handle_find_message(hi
, "NSMSG_HANDLE_SUSPENDED"));
1772 argv
[pw_arg
] = "SUSPENDED";
1775 maxlogins
= hi
->maxlogins
? hi
->maxlogins
: nickserv_conf
.default_maxlogins
;
1776 for (used
= 0, other
= hi
->users
; other
; other
= other
->next_authed
) {
1777 if (++used
>= maxlogins
) {
1778 send_message_type(4, user
, cmd
->parent
->bot
,
1779 handle_find_message(hi
, "NSMSG_MAX_LOGINS"),
1781 argv
[pw_arg
] = "MAXLOGINS";
1786 set_user_handle_info(user
, hi
, 1);
1787 if (nickserv_conf
.email_required
&& !hi
->email_addr
)
1788 reply("NSMSG_PLEASE_SET_EMAIL");
1789 if (!is_secure_password(hi
->handle
, passwd
, NULL
))
1790 reply("NSMSG_WEAK_PASSWORD");
1791 if (hi
->passwd
[0] != '$')
1792 cryptpass(passwd
, hi
->passwd
);
1793 reply("NSMSG_AUTH_SUCCESS");
1794 argv
[pw_arg
] = "****";
1798 static allowauth_func_t
*allowauth_func_list
;
1799 static unsigned int allowauth_func_size
= 0, allowauth_func_used
= 0;
1802 reg_allowauth_func(allowauth_func_t func
)
1804 if (allowauth_func_used
== allowauth_func_size
) {
1805 if (allowauth_func_size
) {
1806 allowauth_func_size
<<= 1;
1807 allowauth_func_list
= realloc(allowauth_func_list
, allowauth_func_size
*sizeof(allowauth_func_t
));
1809 allowauth_func_size
= 8;
1810 allowauth_func_list
= malloc(allowauth_func_size
*sizeof(allowauth_func_t
));
1813 allowauth_func_list
[allowauth_func_used
++] = func
;
1816 static NICKSERV_FUNC(cmd_allowauth
)
1818 struct userNode
*target
;
1819 struct handle_info
*hi
;
1822 NICKSERV_MIN_PARMS(2);
1823 if (!(target
= GetUserH(argv
[1]))) {
1824 reply("MSG_NICK_UNKNOWN", argv
[1]);
1827 if (target
->handle_info
) {
1828 reply("NSMSG_USER_PREV_AUTH", target
->nick
);
1831 if (IsStamped(target
)) {
1832 /* Unauthenticated users might still have been stamped
1833 previously and could therefore have a hidden host;
1834 do not allow them to authenticate to an account. */
1835 reply("NSMSG_USER_PREV_STAMP", target
->nick
);
1840 else if (!(hi
= get_handle_info(argv
[2]))) {
1841 reply("MSG_HANDLE_UNKNOWN", argv
[2]);
1845 if (hi
->opserv_level
> user
->handle_info
->opserv_level
) {
1846 reply("MSG_USER_OUTRANKED", hi
->handle
);
1849 if (((hi
->flags
& (HI_FLAG_SUPPORT_HELPER
|HI_FLAG_NETWORK_HELPER
))
1850 || (hi
->opserv_level
> 0))
1851 && ((argc
< 4) || irccasecmp(argv
[3], "staff"))) {
1852 reply("NSMSG_ALLOWAUTH_STAFF", hi
->handle
);
1855 dict_insert(nickserv_allow_auth_dict
, target
->nick
, hi
);
1856 reply("NSMSG_AUTH_ALLOWED", target
->nick
, hi
->handle
);
1857 send_message(target
, nickserv
, "NSMSG_AUTH_ALLOWED_MSG", hi
->handle
, hi
->handle
);
1858 if (nickserv_conf
.email_enabled
)
1859 send_message(target
, nickserv
, "NSMSG_AUTH_ALLOWED_EMAIL");
1861 if (dict_remove(nickserv_allow_auth_dict
, target
->nick
))
1862 reply("NSMSG_AUTH_NORMAL_ONLY", target
->nick
);
1864 reply("NSMSG_AUTH_UNSPECIAL", target
->nick
);
1866 for (n
=0; n
<allowauth_func_used
; n
++)
1867 allowauth_func_list
[n
](user
, target
, hi
);
1871 static NICKSERV_FUNC(cmd_authcookie
)
1873 struct handle_info
*hi
;
1875 NICKSERV_MIN_PARMS(2);
1876 if (user
->handle_info
) {
1877 reply("NSMSG_ALREADY_AUTHED", user
->handle_info
->handle
);
1880 if (IsStamped(user
)) {
1881 /* Unauthenticated users might still have been stamped
1882 previously and could therefore have a hidden host;
1883 do not allow them to authenticate to an account. */
1884 reply("NSMSG_STAMPED_AUTHCOOKIE");
1887 if (!(hi
= get_handle_info(argv
[1]))) {
1888 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
1891 if (!hi
->email_addr
) {
1892 reply("MSG_SET_EMAIL_ADDR");
1895 nickserv_make_cookie(user
, hi
, ALLOWAUTH
, NULL
, 0);
1899 static NICKSERV_FUNC(cmd_delcookie
)
1901 struct handle_info
*hi
;
1903 hi
= user
->handle_info
;
1905 reply("NSMSG_NO_COOKIE");
1908 switch (hi
->cookie
->type
) {
1911 reply("NSMSG_MUST_TIME_OUT");
1914 nickserv_eat_cookie(hi
->cookie
);
1915 reply("NSMSG_ATE_COOKIE");
1921 static NICKSERV_FUNC(cmd_odelcookie
)
1923 struct handle_info
*hi
;
1925 NICKSERV_MIN_PARMS(2);
1927 if (!(hi
= get_victim_oper(user
, argv
[1])))
1931 reply("NSMSG_NO_COOKIE_FOREIGN", hi
->handle
);
1935 nickserv_eat_cookie(hi
->cookie
);
1936 reply("NSMSG_ATE_FOREIGN_COOKIE", hi
->handle
);
1941 static NICKSERV_FUNC(cmd_resetpass
)
1943 struct handle_info
*hi
;
1944 char crypted
[MD5_CRYPT_LENGTH
];
1947 NICKSERV_MIN_PARMS(3);
1948 if(argc
>= 4 && !strcmp(argv
[3], "WEBLINK"))
1952 if (user
->handle_info
) {
1953 reply("NSMSG_ALREADY_AUTHED", user
->handle_info
->handle
);
1956 if (IsStamped(user
)) {
1957 /* Unauthenticated users might still have been stamped
1958 previously and could therefore have a hidden host;
1959 do not allow them to activate an account. */
1960 reply("NSMSG_STAMPED_RESETPASS");
1963 if (!(hi
= get_handle_info(argv
[1]))) {
1964 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
1967 if (!hi
->email_addr
) {
1968 reply("MSG_SET_EMAIL_ADDR");
1971 cryptpass(argv
[2], crypted
);
1973 nickserv_make_cookie(user
, hi
, PASSWORD_CHANGE
, crypted
, weblink
);
1977 static NICKSERV_FUNC(cmd_cookie
)
1979 struct handle_info
*hi
;
1982 if ((argc
== 2) && (hi
= user
->handle_info
) && hi
->cookie
&& (hi
->cookie
->type
== EMAIL_CHANGE
)) {
1985 NICKSERV_MIN_PARMS(3);
1986 if (!(hi
= get_handle_info(argv
[1]))) {
1987 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
1993 if (HANDLE_FLAGGED(hi
, SUSPENDED
)) {
1994 reply("NSMSG_HANDLE_SUSPENDED");
1999 reply("NSMSG_NO_COOKIE");
2003 /* Check validity of operation before comparing cookie to
2004 * prohibit guessing by authed users. */
2005 if (user
->handle_info
2006 && (hi
->cookie
->type
!= EMAIL_CHANGE
)
2007 && (hi
->cookie
->type
!= PASSWORD_CHANGE
)) {
2008 reply("NSMSG_CANNOT_COOKIE");
2012 if (strcmp(cookie
, hi
->cookie
->cookie
)) {
2013 reply("NSMSG_BAD_COOKIE");
2017 switch (hi
->cookie
->type
) {
2019 safestrncpy(hi
->passwd
, hi
->cookie
->data
, sizeof(hi
->passwd
));
2020 set_user_handle_info(user
, hi
, 1);
2021 reply("NSMSG_HANDLE_ACTIVATED");
2022 if (nickserv_conf
.sync_log
)
2023 SyncLog("ACCOUNTACC %s", hi
->handle
);
2025 case PASSWORD_CHANGE
:
2026 set_user_handle_info(user
, hi
, 1);
2027 safestrncpy(hi
->passwd
, hi
->cookie
->data
, sizeof(hi
->passwd
));
2028 reply("NSMSG_PASSWORD_CHANGED");
2029 if (nickserv_conf
.sync_log
)
2030 SyncLog("PASSCHANGE %s %s", hi
->handle
, hi
->passwd
);
2033 if (!hi
->email_addr
&& nickserv_conf
.sync_log
) {
2035 * This should only happen if an OREGISTER was sent. Require
2036 * email must be enabled! - SiRVulcaN
2038 SyncLog("REGISTER %s %s %s %s", hi
->handle
, hi
->passwd
, hi
->cookie
->data
, user
->info
);
2040 nickserv_set_email_addr(hi
, hi
->cookie
->data
);
2041 reply("NSMSG_EMAIL_CHANGED");
2042 if (nickserv_conf
.sync_log
)
2043 SyncLog("EMAILCHANGE %s %s", hi
->handle
, hi
->cookie
->data
);
2046 set_user_handle_info(user
, hi
, 1);
2047 reply("NSMSG_AUTH_SUCCESS");
2050 reply("NSMSG_BAD_COOKIE_TYPE", hi
->cookie
->type
);
2051 log_module(NS_LOG
, LOG_ERROR
, "Bad cookie type %d for account %s.", hi
->cookie
->type
, hi
->handle
);
2055 nickserv_eat_cookie(hi
->cookie
);
2060 static NICKSERV_FUNC(cmd_oregnick
) {
2062 struct handle_info
*target
;
2063 struct nick_info
*ni
;
2065 NICKSERV_MIN_PARMS(3);
2066 if (!(target
= modcmd_get_handle_info(user
, argv
[1])))
2069 if (!is_registerable_nick(nick
)) {
2070 reply("NSMSG_BAD_NICK", nick
);
2073 ni
= dict_find(nickserv_nick_dict
, nick
, NULL
);
2075 reply("NSMSG_NICK_EXISTS", nick
);
2078 register_nick(nick
, target
);
2079 reply("NSMSG_OREGNICK_SUCCESS", nick
, target
->handle
);
2083 static NICKSERV_FUNC(cmd_regnick
) {
2085 struct nick_info
*ni
;
2087 if (!is_registerable_nick(user
->nick
)) {
2088 reply("NSMSG_BAD_NICK", user
->nick
);
2091 /* count their nicks, see if it's too many */
2092 for (n
=0,ni
=user
->handle_info
->nicks
; ni
; n
++,ni
=ni
->next
) ;
2093 if (n
>= nickserv_conf
.nicks_per_handle
) {
2094 reply("NSMSG_TOO_MANY_NICKS");
2097 ni
= dict_find(nickserv_nick_dict
, user
->nick
, NULL
);
2099 reply("NSMSG_NICK_EXISTS", user
->nick
);
2102 register_nick(user
->nick
, user
->handle_info
);
2103 reply("NSMSG_REGNICK_SUCCESS", user
->nick
);
2107 static NICKSERV_FUNC(cmd_pass
)
2109 struct handle_info
*hi
;
2110 const char *old_pass
, *new_pass
;
2112 NICKSERV_MIN_PARMS(3);
2113 hi
= user
->handle_info
;
2117 if (!is_secure_password(hi
->handle
, new_pass
, user
)) return 0;
2118 if (!checkpass(old_pass
, hi
->passwd
)) {
2119 argv
[1] = "BADPASS";
2120 reply("NSMSG_PASSWORD_INVALID");
2123 cryptpass(new_pass
, hi
->passwd
);
2124 if (nickserv_conf
.sync_log
)
2125 SyncLog("PASSCHANGE %s %s", hi
->handle
, hi
->passwd
);
2127 reply("NSMSG_PASS_SUCCESS");
2132 nickserv_addmask(struct userNode
*user
, struct handle_info
*hi
, const char *mask
)
2135 char *new_mask
= canonicalize_hostmask(strdup(mask
));
2136 for (i
=0; i
<hi
->masks
->used
; i
++) {
2137 if (!irccasecmp(new_mask
, hi
->masks
->list
[i
])) {
2138 send_message(user
, nickserv
, "NSMSG_ADDMASK_ALREADY", new_mask
);
2143 string_list_append(hi
->masks
, new_mask
);
2144 send_message(user
, nickserv
, "NSMSG_ADDMASK_SUCCESS", new_mask
);
2148 static NICKSERV_FUNC(cmd_addmask
)
2151 char *mask
= generate_hostmask(user
, GENMASK_OMITNICK
|GENMASK_NO_HIDING
|GENMASK_ANY_IDENT
);
2152 int res
= nickserv_addmask(user
, user
->handle_info
, mask
);
2156 if (!is_gline(argv
[1])) {
2157 reply("NSMSG_MASK_INVALID", argv
[1]);
2160 return nickserv_addmask(user
, user
->handle_info
, argv
[1]);
2164 static NICKSERV_FUNC(cmd_oaddmask
)
2166 struct handle_info
*hi
;
2168 NICKSERV_MIN_PARMS(3);
2169 if (!(hi
= get_victim_oper(user
, argv
[1])))
2171 return nickserv_addmask(user
, hi
, argv
[2]);
2175 nickserv_delmask(struct userNode
*user
, struct handle_info
*hi
, const char *del_mask
)
2178 for (i
=0; i
<hi
->masks
->used
; i
++) {
2179 if (!strcmp(del_mask
, hi
->masks
->list
[i
])) {
2180 char *old_mask
= hi
->masks
->list
[i
];
2181 if (hi
->masks
->used
== 1) {
2182 send_message(user
, nickserv
, "NSMSG_DELMASK_NOTLAST");
2185 hi
->masks
->list
[i
] = hi
->masks
->list
[--hi
->masks
->used
];
2186 send_message(user
, nickserv
, "NSMSG_DELMASK_SUCCESS", old_mask
);
2191 send_message(user
, nickserv
, "NSMSG_DELMASK_NOT_FOUND");
2195 static NICKSERV_FUNC(cmd_delmask
)
2197 NICKSERV_MIN_PARMS(2);
2198 return nickserv_delmask(user
, user
->handle_info
, argv
[1]);
2201 static NICKSERV_FUNC(cmd_odelmask
)
2203 struct handle_info
*hi
;
2204 NICKSERV_MIN_PARMS(3);
2205 if (!(hi
= get_victim_oper(user
, argv
[1])))
2207 return nickserv_delmask(user
, hi
, argv
[2]);
2211 nickserv_modify_handle_flags(struct userNode
*user
, struct userNode
*bot
, const char *str
, unsigned long *padded
, unsigned long *premoved
) {
2212 unsigned int nn
, add
= 1, pos
;
2213 unsigned long added
, removed
, flag
;
2215 for (added
=removed
=nn
=0; str
[nn
]; nn
++) {
2217 case '+': add
= 1; break;
2218 case '-': add
= 0; break;
2220 if (!(pos
= handle_inverse_flags
[(unsigned char)str
[nn
]])) {
2221 send_message(user
, bot
, "NSMSG_INVALID_FLAG", str
[nn
]);
2224 if (user
&& (user
->handle_info
->opserv_level
< flag_access_levels
[pos
-1])) {
2225 /* cheesy avoidance of looking up the flag name.. */
2226 send_message(user
, bot
, "NSMSG_FLAG_PRIVILEGED", str
[nn
]);
2229 flag
= 1 << (pos
- 1);
2231 added
|= flag
, removed
&= ~flag
;
2233 removed
|= flag
, added
&= ~flag
;
2238 *premoved
= removed
;
2243 nickserv_apply_flags(struct userNode
*user
, struct handle_info
*hi
, const char *flags
)
2245 unsigned long before
, after
, added
, removed
;
2246 struct userNode
*uNode
;
2248 before
= hi
->flags
& (HI_FLAG_SUPPORT_HELPER
|HI_FLAG_NETWORK_HELPER
);
2249 if (!nickserv_modify_handle_flags(user
, nickserv
, flags
, &added
, &removed
))
2251 hi
->flags
= (hi
->flags
| added
) & ~removed
;
2252 after
= hi
->flags
& (HI_FLAG_SUPPORT_HELPER
|HI_FLAG_NETWORK_HELPER
);
2254 /* Strip helping flag if they're only a support helper and not
2255 * currently in #support. */
2256 if (HANDLE_FLAGGED(hi
, HELPING
) && (after
== HI_FLAG_SUPPORT_HELPER
)) {
2257 struct channelList
*schannels
;
2259 schannels
= chanserv_support_channels();
2260 for (uNode
= hi
->users
; uNode
; uNode
= uNode
->next_authed
) {
2261 for (ii
= 0; ii
< schannels
->used
; ++ii
)
2262 if (GetUserMode(schannels
->list
[ii
], uNode
))
2264 if (ii
< schannels
->used
)
2268 HANDLE_CLEAR_FLAG(hi
, HELPING
);
2271 if (after
&& !before
) {
2272 /* Add user to current helper list. */
2273 for (uNode
= hi
->users
; uNode
; uNode
= uNode
->next_authed
)
2274 userList_append(&curr_helpers
, uNode
);
2275 } else if (!after
&& before
) {
2276 /* Remove user from current helper list. */
2277 for (uNode
= hi
->users
; uNode
; uNode
= uNode
->next_authed
)
2278 userList_remove(&curr_helpers
, uNode
);
2285 set_list(struct userNode
*user
, struct handle_info
*hi
, int override
)
2289 char *set_display
[] = {
2290 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", /* "STYLE", */
2291 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE",
2292 "FAKEHOST", "TITLE", "EPITHET"
2295 send_message(user
, nickserv
, "NSMSG_SETTING_LIST");
2296 send_message(user
, nickserv
, "NSMSG_SETTING_LIST_HEADER");
2298 /* Do this so options are presented in a consistent order. */
2299 for (i
= 0; i
< ArrayLength(set_display
); ++i
)
2300 if ((opt
= dict_find(nickserv_opt_dict
, set_display
[i
], NULL
)))
2301 opt(user
, hi
, override
, 0, NULL
);
2302 send_message(user
, nickserv
, "NSMSG_SETTING_LIST_END");
2305 static NICKSERV_FUNC(cmd_set
)
2307 struct handle_info
*hi
;
2310 hi
= user
->handle_info
;
2312 set_list(user
, hi
, 0);
2315 if (!(opt
= dict_find(nickserv_opt_dict
, argv
[1], NULL
))) {
2316 reply("NSMSG_INVALID_OPTION", argv
[1]);
2319 return opt(user
, hi
, 0, argc
-1, argv
+1);
2322 static NICKSERV_FUNC(cmd_oset
)
2324 struct handle_info
*hi
;
2327 NICKSERV_MIN_PARMS(2);
2329 if (!(hi
= get_victim_oper(user
, argv
[1])))
2333 set_list(user
, hi
, 0);
2337 if (!(opt
= dict_find(nickserv_opt_dict
, argv
[2], NULL
))) {
2338 reply("NSMSG_INVALID_OPTION", argv
[2]);
2342 return opt(user
, hi
, 1, argc
-2, argv
+2);
2345 static OPTION_FUNC(opt_info
)
2349 if ((argv
[1][0] == '*') && (argv
[1][1] == 0)) {
2351 hi
->infoline
= NULL
;
2353 hi
->infoline
= strdup(unsplit_string(argv
+1, argc
-1, NULL
));
2357 info
= hi
->infoline
? hi
->infoline
: user_find_message(user
, "MSG_NONE");
2358 send_message(user
, nickserv
, "NSMSG_SET_INFO", info
);
2362 static OPTION_FUNC(opt_width
)
2365 hi
->screen_width
= strtoul(argv
[1], NULL
, 0);
2367 if ((hi
->screen_width
> 0) && (hi
->screen_width
< MIN_LINE_SIZE
))
2368 hi
->screen_width
= MIN_LINE_SIZE
;
2369 else if (hi
->screen_width
> MAX_LINE_SIZE
)
2370 hi
->screen_width
= MAX_LINE_SIZE
;
2372 send_message(user
, nickserv
, "NSMSG_SET_WIDTH", hi
->screen_width
);
2376 static OPTION_FUNC(opt_tablewidth
)
2379 hi
->table_width
= strtoul(argv
[1], NULL
, 0);
2381 if ((hi
->table_width
> 0) && (hi
->table_width
< MIN_LINE_SIZE
))
2382 hi
->table_width
= MIN_LINE_SIZE
;
2383 else if (hi
->screen_width
> MAX_LINE_SIZE
)
2384 hi
->table_width
= MAX_LINE_SIZE
;
2386 send_message(user
, nickserv
, "NSMSG_SET_TABLEWIDTH", hi
->table_width
);
2390 static OPTION_FUNC(opt_color
)
2393 if (enabled_string(argv
[1]))
2394 HANDLE_SET_FLAG(hi
, MIRC_COLOR
);
2395 else if (disabled_string(argv
[1]))
2396 HANDLE_CLEAR_FLAG(hi
, MIRC_COLOR
);
2398 send_message(user
, nickserv
, "MSG_INVALID_BINARY", argv
[1]);
2403 send_message(user
, nickserv
, "NSMSG_SET_COLOR", user_find_message(user
, HANDLE_FLAGGED(hi
, MIRC_COLOR
) ? "MSG_ON" : "MSG_OFF"));
2407 static OPTION_FUNC(opt_privmsg
)
2410 if (enabled_string(argv
[1]))
2411 HANDLE_SET_FLAG(hi
, USE_PRIVMSG
);
2412 else if (disabled_string(argv
[1]))
2413 HANDLE_CLEAR_FLAG(hi
, USE_PRIVMSG
);
2415 send_message(user
, nickserv
, "MSG_INVALID_BINARY", argv
[1]);
2420 send_message(user
, nickserv
, "NSMSG_SET_PRIVMSG", user_find_message(user
, HANDLE_FLAGGED(hi
, USE_PRIVMSG
) ? "MSG_ON" : "MSG_OFF"));
2425 static OPTION_FUNC(opt_style)
2430 if (!irccasecmp(argv[1], "Zoot"))
2431 hi->userlist_style = HI_STYLE_ZOOT;
2432 else if (!irccasecmp(argv[1], "def"))
2433 hi->userlist_style = HI_STYLE_DEF;
2436 switch (hi->userlist_style) {
2445 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2450 static OPTION_FUNC(opt_announcements
)
2455 if (enabled_string(argv
[1]))
2456 hi
->announcements
= 'y';
2457 else if (disabled_string(argv
[1]))
2458 hi
->announcements
= 'n';
2459 else if (!strcmp(argv
[1], "?") || !irccasecmp(argv
[1], "default"))
2460 hi
->announcements
= '?';
2462 send_message(user
, nickserv
, "NSMSG_INVALID_ANNOUNCE", argv
[1]);
2467 switch (hi
->announcements
) {
2468 case 'y': choice
= user_find_message(user
, "MSG_ON"); break;
2469 case 'n': choice
= user_find_message(user
, "MSG_OFF"); break;
2470 case '?': choice
= "default"; break;
2471 default: choice
= "unknown"; break;
2473 send_message(user
, nickserv
, "NSMSG_SET_ANNOUNCEMENTS", choice
);
2477 static OPTION_FUNC(opt_password
)
2480 send_message(user
, nickserv
, "NSMSG_USE_CMD_PASS");
2485 cryptpass(argv
[1], hi
->passwd
);
2487 send_message(user
, nickserv
, "NSMSG_SET_PASSWORD", "***");
2491 static OPTION_FUNC(opt_flags
)
2494 unsigned int ii
, flen
;
2497 send_message(user
, nickserv
, "MSG_SETTING_PRIVILEGED", argv
[0]);
2502 nickserv_apply_flags(user
, hi
, argv
[1]);
2504 for (ii
= flen
= 0; handle_flags
[ii
]; ii
++)
2505 if (hi
->flags
& (1 << ii
))
2506 flags
[flen
++] = handle_flags
[ii
];
2509 send_message(user
, nickserv
, "NSMSG_SET_FLAGS", flags
);
2511 send_message(user
, nickserv
, "NSMSG_SET_FLAGS", user_find_message(user
, "MSG_NONE"));
2515 static OPTION_FUNC(opt_email
)
2519 if (!is_valid_email_addr(argv
[1])) {
2520 send_message(user
, nickserv
, "NSMSG_BAD_EMAIL_ADDR");
2523 if ((str
= sendmail_prohibited_address(argv
[1]))) {
2524 send_message(user
, nickserv
, "NSMSG_EMAIL_PROHIBITED", argv
[1], str
);
2527 if (hi
->email_addr
&& !irccasecmp(hi
->email_addr
, argv
[1]))
2528 send_message(user
, nickserv
, "NSMSG_EMAIL_SAME");
2530 nickserv_make_cookie(user
, hi
, EMAIL_CHANGE
, argv
[1], 0);
2532 nickserv_set_email_addr(hi
, argv
[1]);
2534 nickserv_eat_cookie(hi
->cookie
);
2535 send_message(user
, nickserv
, "NSMSG_SET_EMAIL", visible_email_addr(user
, hi
));
2538 send_message(user
, nickserv
, "NSMSG_SET_EMAIL", visible_email_addr(user
, hi
));
2542 static OPTION_FUNC(opt_maxlogins
)
2544 unsigned char maxlogins
;
2546 maxlogins
= strtoul(argv
[1], NULL
, 0);
2547 if ((maxlogins
> nickserv_conf
.hard_maxlogins
) && !override
) {
2548 send_message(user
, nickserv
, "NSMSG_BAD_MAX_LOGINS", nickserv_conf
.hard_maxlogins
);
2551 hi
->maxlogins
= maxlogins
;
2553 maxlogins
= hi
->maxlogins
? hi
->maxlogins
: nickserv_conf
.default_maxlogins
;
2554 send_message(user
, nickserv
, "NSMSG_SET_MAXLOGINS", maxlogins
);
2558 static OPTION_FUNC(opt_language
)
2560 struct language
*lang
;
2562 lang
= language_find(argv
[1]);
2563 if (irccasecmp(lang
->name
, argv
[1]))
2564 send_message(user
, nickserv
, "NSMSG_LANGUAGE_NOT_FOUND", argv
[1], lang
->name
);
2565 hi
->language
= lang
;
2567 send_message(user
, nickserv
, "NSMSG_SET_LANGUAGE", hi
->language
->name
);
2572 oper_try_set_access(struct userNode
*user
, struct userNode
*bot
, struct handle_info
*target
, unsigned int new_level
) {
2573 if (!oper_has_access(user
, bot
, nickserv_conf
.modoper_level
, 0))
2575 if ((user
->handle_info
->opserv_level
< target
->opserv_level
)
2576 || ((user
->handle_info
->opserv_level
== target
->opserv_level
)
2577 && (user
->handle_info
->opserv_level
< 1000))) {
2578 send_message(user
, bot
, "MSG_USER_OUTRANKED", target
->handle
);
2581 if ((user
->handle_info
->opserv_level
< new_level
)
2582 || ((user
->handle_info
->opserv_level
== new_level
)
2583 && (user
->handle_info
->opserv_level
< 1000))) {
2584 send_message(user
, bot
, "NSMSG_OPSERV_LEVEL_BAD");
2587 if (user
->handle_info
== target
) {
2588 send_message(user
, bot
, "MSG_STUPID_ACCESS_CHANGE");
2591 if (target
->opserv_level
== new_level
)
2593 log_module(NS_LOG
, LOG_INFO
, "Account %s setting oper level for account %s to %d (from %d).",
2594 user
->handle_info
->handle
, target
->handle
, new_level
, target
->opserv_level
);
2595 target
->opserv_level
= new_level
;
2599 static OPTION_FUNC(opt_level
)
2604 send_message(user
, nickserv
, "MSG_SETTING_PRIVILEGED", argv
[0]);
2608 res
= (argc
> 1) ? oper_try_set_access(user
, nickserv
, hi
, strtoul(argv
[1], NULL
, 0)) : 0;
2609 send_message(user
, nickserv
, "NSMSG_SET_LEVEL", hi
->opserv_level
);
2613 static OPTION_FUNC(opt_epithet
)
2615 if ((argc
> 1) && oper_has_access(user
, nickserv
, nickserv_conf
.set_epithet_level
, 0)) {
2618 send_message(user
, nickserv
, "MSG_SETTING_PRIVILEGED", argv
[0]);
2622 epithet
= unsplit_string(argv
+1, argc
-1, NULL
);
2626 if ((epithet
[0] == '*') && !epithet
[1])
2629 hi
->epithet
= strdup(epithet
);
2633 send_message(user
, nickserv
, "NSMSG_SET_EPITHET", hi
->epithet
);
2635 send_message(user
, nickserv
, "NSMSG_SET_EPITHET", user_find_message(user
, "MSG_NONE"));
2639 static OPTION_FUNC(opt_title
)
2643 if ((argc
> 1) && oper_has_access(user
, nickserv
, nickserv_conf
.set_title_level
, 0)) {
2645 send_message(user
, nickserv
, "MSG_SETTING_PRIVILEGED", argv
[0]);
2650 if (strchr(title
, '.')) {
2651 send_message(user
, nickserv
, "NSMSG_TITLE_INVALID");
2654 if ((strlen(user
->handle_info
->handle
) + strlen(title
) +
2655 strlen(nickserv_conf
.titlehost_suffix
) + 2) > HOSTLEN
) {
2656 send_message(user
, nickserv
, "NSMSG_TITLE_TRUNCATED");
2661 if (!strcmp(title
, "*")) {
2662 hi
->fakehost
= NULL
;
2664 hi
->fakehost
= malloc(strlen(title
)+2);
2665 hi
->fakehost
[0] = '.';
2666 strcpy(hi
->fakehost
+1, title
);
2669 } else if (hi
->fakehost
&& (hi
->fakehost
[0] == '.'))
2670 title
= hi
->fakehost
+ 1;
2674 title
= user_find_message(user
, "MSG_NONE");
2675 send_message(user
, nickserv
, "NSMSG_SET_TITLE", title
);
2679 static OPTION_FUNC(opt_fakehost
)
2683 if ((argc
> 1) && oper_has_access(user
, nickserv
, nickserv_conf
.set_fakehost_level
, 0)) {
2685 send_message(user
, nickserv
, "MSG_SETTING_PRIVILEGED", argv
[0]);
2690 if ((strlen(fake
) > HOSTLEN
) || (fake
[0] == '.')) {
2691 send_message(user
, nickserv
, "NSMSG_FAKEHOST_INVALID", HOSTLEN
);
2695 if (!strcmp(fake
, "*"))
2696 hi
->fakehost
= NULL
;
2698 hi
->fakehost
= strdup(fake
);
2699 fake
= hi
->fakehost
;
2702 fake
= generate_fakehost(hi
);
2704 fake
= user_find_message(user
, "MSG_NONE");
2705 send_message(user
, nickserv
, "NSMSG_SET_FAKEHOST", fake
);
2709 static NICKSERV_FUNC(cmd_reclaim
)
2711 struct handle_info
*hi
;
2712 struct nick_info
*ni
;
2713 struct userNode
*victim
;
2715 NICKSERV_MIN_PARMS(2);
2716 hi
= user
->handle_info
;
2717 ni
= dict_find(nickserv_nick_dict
, argv
[1], 0);
2719 reply("NSMSG_UNKNOWN_NICK", argv
[1]);
2722 if (ni
->owner
!= user
->handle_info
) {
2723 reply("NSMSG_NOT_YOUR_NICK", ni
->nick
);
2726 victim
= GetUserH(ni
->nick
);
2728 reply("MSG_NICK_UNKNOWN", ni
->nick
);
2731 if (victim
== user
) {
2732 reply("NSMSG_NICK_USER_YOU");
2735 nickserv_reclaim(victim
, ni
, nickserv_conf
.reclaim_action
);
2736 switch (nickserv_conf
.reclaim_action
) {
2737 case RECLAIM_NONE
: reply("NSMSG_RECLAIMED_NONE"); break;
2738 case RECLAIM_WARN
: reply("NSMSG_RECLAIMED_WARN", victim
->nick
); break;
2739 case RECLAIM_SVSNICK
: reply("NSMSG_RECLAIMED_SVSNICK", victim
->nick
); break;
2740 case RECLAIM_KILL
: reply("NSMSG_RECLAIMED_KILL", victim
->nick
); break;
2745 static NICKSERV_FUNC(cmd_unregnick
)
2748 struct handle_info
*hi
;
2749 struct nick_info
*ni
;
2751 hi
= user
->handle_info
;
2752 nick
= (argc
< 2) ? user
->nick
: (const char*)argv
[1];
2753 ni
= dict_find(nickserv_nick_dict
, nick
, NULL
);
2755 reply("NSMSG_UNKNOWN_NICK", nick
);
2758 if (hi
!= ni
->owner
) {
2759 reply("NSMSG_NOT_YOUR_NICK", nick
);
2762 reply("NSMSG_UNREGNICK_SUCCESS", ni
->nick
);
2767 static NICKSERV_FUNC(cmd_ounregnick
)
2769 struct nick_info
*ni
;
2771 NICKSERV_MIN_PARMS(2);
2772 if (!(ni
= get_nick_info(argv
[1]))) {
2773 reply("NSMSG_NICK_NOT_REGISTERED", argv
[1]);
2776 if (ni
->owner
->opserv_level
>= user
->handle_info
->opserv_level
) {
2777 reply("MSG_USER_OUTRANKED", ni
->nick
);
2780 reply("NSMSG_UNREGNICK_SUCCESS", ni
->nick
);
2785 static NICKSERV_FUNC(cmd_unregister
)
2787 struct handle_info
*hi
;
2790 NICKSERV_MIN_PARMS(2);
2791 hi
= user
->handle_info
;
2794 if (checkpass(passwd
, hi
->passwd
)) {
2795 nickserv_unregister_handle(hi
, user
);
2798 log_module(NS_LOG
, LOG_INFO
, "Account '%s' tried to unregister with the wrong password.", hi
->handle
);
2799 reply("NSMSG_PASSWORD_INVALID");
2804 static NICKSERV_FUNC(cmd_ounregister
)
2806 struct handle_info
*hi
;
2808 NICKSERV_MIN_PARMS(2);
2809 if (!(hi
= get_victim_oper(user
, argv
[1])))
2811 nickserv_unregister_handle(hi
, user
);
2815 static NICKSERV_FUNC(cmd_status
)
2817 if (nickserv_conf
.disable_nicks
) {
2818 reply("NSMSG_GLOBAL_STATS_NONICK",
2819 dict_size(nickserv_handle_dict
));
2821 if (user
->handle_info
) {
2823 struct nick_info
*ni
;
2824 for (ni
=user
->handle_info
->nicks
; ni
; ni
=ni
->next
) cnt
++;
2825 reply("NSMSG_HANDLE_STATS", cnt
);
2827 reply("NSMSG_HANDLE_NONE");
2829 reply("NSMSG_GLOBAL_STATS",
2830 dict_size(nickserv_handle_dict
),
2831 dict_size(nickserv_nick_dict
));
2836 static NICKSERV_FUNC(cmd_ghost
)
2838 struct userNode
*target
;
2839 char reason
[MAXLEN
];
2841 NICKSERV_MIN_PARMS(2);
2842 if (!(target
= GetUserH(argv
[1]))) {
2843 reply("MSG_NICK_UNKNOWN", argv
[1]);
2846 if (target
== user
) {
2847 reply("NSMSG_CANNOT_GHOST_SELF");
2850 if (!target
->handle_info
|| (target
->handle_info
!= user
->handle_info
)) {
2851 reply("NSMSG_CANNOT_GHOST_USER", target
->nick
);
2854 snprintf(reason
, sizeof(reason
), "Ghost kill on account %s (requested by %s).", target
->handle_info
->handle
, user
->nick
);
2855 DelUser(target
, nickserv
, 1, reason
);
2856 reply("NSMSG_GHOST_KILLED", argv
[1]);
2860 static NICKSERV_FUNC(cmd_vacation
)
2862 HANDLE_SET_FLAG(user
->handle_info
, FROZEN
);
2863 reply("NSMSG_ON_VACATION");
2868 nickserv_saxdb_write(struct saxdb_context
*ctx
) {
2870 struct handle_info
*hi
;
2873 for (it
= dict_first(nickserv_handle_dict
); it
; it
= iter_next(it
)) {
2875 #ifdef WITH_PROTOCOL_BAHAMUT
2878 saxdb_start_record(ctx
, iter_key(it
), 0);
2879 if (hi
->announcements
!= '?') {
2880 flags
[0] = hi
->announcements
;
2882 saxdb_write_string(ctx
, KEY_ANNOUNCEMENTS
, flags
);
2885 struct handle_cookie
*cookie
= hi
->cookie
;
2888 switch (cookie
->type
) {
2889 case ACTIVATION
: type
= KEY_ACTIVATION
; break;
2890 case PASSWORD_CHANGE
: type
= KEY_PASSWORD_CHANGE
; break;
2891 case EMAIL_CHANGE
: type
= KEY_EMAIL_CHANGE
; break;
2892 case ALLOWAUTH
: type
= KEY_ALLOWAUTH
; break;
2893 default: type
= NULL
; break;
2896 saxdb_start_record(ctx
, KEY_COOKIE
, 0);
2897 saxdb_write_string(ctx
, KEY_COOKIE_TYPE
, type
);
2898 saxdb_write_int(ctx
, KEY_COOKIE_EXPIRES
, cookie
->expires
);
2900 saxdb_write_string(ctx
, KEY_COOKIE_DATA
, cookie
->data
);
2901 saxdb_write_string(ctx
, KEY_COOKIE
, cookie
->cookie
);
2902 saxdb_end_record(ctx
);
2906 saxdb_write_string(ctx
, KEY_EMAIL_ADDR
, hi
->email_addr
);
2908 saxdb_write_string(ctx
, KEY_EPITHET
, hi
->epithet
);
2910 saxdb_write_string(ctx
, KEY_FAKEHOST
, hi
->fakehost
);
2914 for (ii
=flen
=0; handle_flags
[ii
]; ++ii
)
2915 if (hi
->flags
& (1 << ii
))
2916 flags
[flen
++] = handle_flags
[ii
];
2918 saxdb_write_string(ctx
, KEY_FLAGS
, flags
);
2920 #ifdef WITH_PROTOCOL_BAHAMUT
2921 saxdb_write_int(ctx
, KEY_ID
, hi
->id
);
2924 saxdb_write_string(ctx
, KEY_INFO
, hi
->infoline
);
2925 if (hi
->last_quit_host
[0])
2926 saxdb_write_string(ctx
, KEY_LAST_QUIT_HOST
, hi
->last_quit_host
);
2927 saxdb_write_int(ctx
, KEY_LAST_SEEN
, hi
->lastseen
);
2928 if (hi
->masks
->used
)
2929 saxdb_write_string_list(ctx
, KEY_MASKS
, hi
->masks
);
2931 saxdb_write_int(ctx
, KEY_MAXLOGINS
, hi
->maxlogins
);
2933 struct string_list
*slist
;
2934 struct nick_info
*ni
;
2936 slist
= alloc_string_list(nickserv_conf
.nicks_per_handle
);
2937 for (ni
= hi
->nicks
; ni
; ni
= ni
->next
) string_list_append(slist
, ni
->nick
);
2938 saxdb_write_string_list(ctx
, KEY_NICKS
, slist
);
2942 if (hi
->opserv_level
)
2943 saxdb_write_int(ctx
, KEY_OPSERV_LEVEL
, hi
->opserv_level
);
2944 if (hi
->language
!= lang_C
)
2945 saxdb_write_string(ctx
, KEY_LANGUAGE
, hi
->language
->name
);
2946 saxdb_write_string(ctx
, KEY_PASSWD
, hi
->passwd
);
2947 saxdb_write_int(ctx
, KEY_REGISTER_ON
, hi
->registered
);
2948 if (hi
->screen_width
)
2949 saxdb_write_int(ctx
, KEY_SCREEN_WIDTH
, hi
->screen_width
);
2950 if (hi
->table_width
)
2951 saxdb_write_int(ctx
, KEY_TABLE_WIDTH
, hi
->table_width
);
2952 flags
[0] = hi
->userlist_style
;
2954 saxdb_write_string(ctx
, KEY_USERLIST_STYLE
, flags
);
2955 saxdb_end_record(ctx
);
2960 static handle_merge_func_t
*handle_merge_func_list
;
2961 static unsigned int handle_merge_func_size
= 0, handle_merge_func_used
= 0;
2964 reg_handle_merge_func(handle_merge_func_t func
)
2966 if (handle_merge_func_used
== handle_merge_func_size
) {
2967 if (handle_merge_func_size
) {
2968 handle_merge_func_size
<<= 1;
2969 handle_merge_func_list
= realloc(handle_merge_func_list
, handle_merge_func_size
*sizeof(handle_merge_func_t
));
2971 handle_merge_func_size
= 8;
2972 handle_merge_func_list
= malloc(handle_merge_func_size
*sizeof(handle_merge_func_t
));
2975 handle_merge_func_list
[handle_merge_func_used
++] = func
;
2978 static NICKSERV_FUNC(cmd_merge
)
2980 struct handle_info
*hi_from
, *hi_to
;
2981 struct userNode
*last_user
;
2982 struct userData
*cList
, *cListNext
;
2983 unsigned int ii
, jj
, n
;
2984 char buffer
[MAXLEN
];
2986 NICKSERV_MIN_PARMS(3);
2988 if (!(hi_from
= get_victim_oper(user
, argv
[1])))
2990 if (!(hi_to
= get_victim_oper(user
, argv
[2])))
2992 if (hi_to
== hi_from
) {
2993 reply("NSMSG_CANNOT_MERGE_SELF", hi_to
->handle
);
2997 for (n
=0; n
<handle_merge_func_used
; n
++)
2998 handle_merge_func_list
[n
](user
, hi_to
, hi_from
);
3000 /* Append "from" handle's nicks to "to" handle's nick list. */
3002 struct nick_info
*last_ni
;
3003 for (last_ni
=hi_to
->nicks
; last_ni
->next
; last_ni
=last_ni
->next
) ;
3004 last_ni
->next
= hi_from
->nicks
;
3006 while (hi_from
->nicks
) {
3007 hi_from
->nicks
->owner
= hi_to
;
3008 hi_from
->nicks
= hi_from
->nicks
->next
;
3011 /* Merge the hostmasks. */
3012 for (ii
=0; ii
<hi_from
->masks
->used
; ii
++) {
3013 char *mask
= hi_from
->masks
->list
[ii
];
3014 for (jj
=0; jj
<hi_to
->masks
->used
; jj
++)
3015 if (match_ircglobs(hi_to
->masks
->list
[jj
], mask
))
3017 if (jj
==hi_to
->masks
->used
) /* Nothing from the "to" handle covered this mask, so add it. */
3018 string_list_append(hi_to
->masks
, strdup(mask
));
3021 /* Merge the lists of authed users. */
3023 for (last_user
=hi_to
->users
; last_user
->next_authed
; last_user
=last_user
->next_authed
) ;
3024 last_user
->next_authed
= hi_from
->users
;
3026 hi_to
->users
= hi_from
->users
;
3028 /* Repoint the old "from" handle's users. */
3029 for (last_user
=hi_from
->users
; last_user
; last_user
=last_user
->next_authed
) {
3030 last_user
->handle_info
= hi_to
;
3032 hi_from
->users
= NULL
;
3034 /* Merge channel userlists. */
3035 for (cList
=hi_from
->channels
; cList
; cList
=cListNext
) {
3036 struct userData
*cList2
;
3037 cListNext
= cList
->u_next
;
3038 for (cList2
=hi_to
->channels
; cList2
; cList2
=cList2
->u_next
)
3039 if (cList
->channel
== cList2
->channel
)
3041 if (cList2
&& (cList2
->access
>= cList
->access
)) {
3042 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
);
3043 /* keep cList2 in hi_to; remove cList from hi_from */
3044 del_channel_user(cList
, 1);
3047 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
);
3048 /* remove the lower-ranking cList2 from hi_to */
3049 del_channel_user(cList2
, 1);
3051 log_module(NS_LOG
, LOG_INFO
, "Merge: %s had no access in %s", hi_to
->handle
, cList
->channel
->channel
->name
);
3053 /* cList needs to be moved from hi_from to hi_to */
3054 cList
->handle
= hi_to
;
3055 /* Remove from linked list for hi_from */
3056 assert(!cList
->u_prev
);
3057 hi_from
->channels
= cList
->u_next
;
3059 cList
->u_next
->u_prev
= cList
->u_prev
;
3060 /* Add to linked list for hi_to */
3061 cList
->u_prev
= NULL
;
3062 cList
->u_next
= hi_to
->channels
;
3063 if (hi_to
->channels
)
3064 hi_to
->channels
->u_prev
= cList
;
3065 hi_to
->channels
= cList
;
3069 /* Do they get an OpServ level promotion? */
3070 if (hi_from
->opserv_level
> hi_to
->opserv_level
)
3071 hi_to
->opserv_level
= hi_from
->opserv_level
;
3073 /* What about last seen time? */
3074 if (hi_from
->lastseen
> hi_to
->lastseen
)
3075 hi_to
->lastseen
= hi_from
->lastseen
;
3077 /* Does a fakehost carry over? (This intentionally doesn't set it
3078 * for users previously attached to hi_to. They'll just have to
3081 if (hi_from
->fakehost
&& !hi_to
->fakehost
)
3082 hi_to
->fakehost
= strdup(hi_from
->fakehost
);
3084 /* Notify of success. */
3085 sprintf(buffer
, "%s (%s) merged account %s into %s.", user
->nick
, user
->handle_info
->handle
, hi_from
->handle
, hi_to
->handle
);
3086 reply("NSMSG_HANDLES_MERGED", hi_from
->handle
, hi_to
->handle
);
3087 global_message(MESSAGE_RECIPIENT_STAFF
, buffer
);
3089 /* Unregister the "from" handle. */
3090 nickserv_unregister_handle(hi_from
, NULL
);
3095 struct nickserv_discrim
{
3096 unsigned int limit
, min_level
, max_level
;
3097 unsigned long flags_on
, flags_off
;
3098 time_t min_registered
, max_registered
;
3100 enum { SUBSET
, EXACT
, SUPERSET
, LASTQUIT
} hostmask_type
;
3101 const char *nickmask
;
3102 const char *hostmask
;
3103 const char *handlemask
;
3104 const char *emailmask
;
3107 typedef void (*discrim_search_func
)(struct userNode
*source
, struct handle_info
*hi
);
3109 struct discrim_apply_info
{
3110 struct nickserv_discrim
*discrim
;
3111 discrim_search_func func
;
3112 struct userNode
*source
;
3113 unsigned int matched
;
3116 static struct nickserv_discrim
*
3117 nickserv_discrim_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
3120 struct nickserv_discrim
*discrim
;
3122 discrim
= malloc(sizeof(*discrim
));
3123 memset(discrim
, 0, sizeof(*discrim
));
3124 discrim
->min_level
= 0;
3125 discrim
->max_level
= ~0;
3126 discrim
->limit
= 50;
3127 discrim
->min_registered
= 0;
3128 discrim
->max_registered
= INT_MAX
;
3129 discrim
->lastseen
= now
;
3131 for (i
=0; i
<argc
; i
++) {
3132 if (i
== argc
- 1) {
3133 send_message(user
, nickserv
, "MSG_MISSING_PARAMS", argv
[i
]);
3136 if (!irccasecmp(argv
[i
], "limit")) {
3137 discrim
->limit
= strtoul(argv
[++i
], NULL
, 0);
3138 } else if (!irccasecmp(argv
[i
], "flags")) {
3139 nickserv_modify_handle_flags(user
, nickserv
, argv
[++i
], &discrim
->flags_on
, &discrim
->flags_off
);
3140 } else if (!irccasecmp(argv
[i
], "registered")) {
3141 const char *cmp
= argv
[++i
];
3142 if (cmp
[0] == '<') {
3143 if (cmp
[1] == '=') {
3144 discrim
->min_registered
= now
- ParseInterval(cmp
+2);
3146 discrim
->min_registered
= now
- ParseInterval(cmp
+1) + 1;
3148 } else if (cmp
[0] == '=') {
3149 discrim
->min_registered
= discrim
->max_registered
= now
- ParseInterval(cmp
+1);
3150 } else if (cmp
[0] == '>') {
3151 if (cmp
[1] == '=') {
3152 discrim
->max_registered
= now
- ParseInterval(cmp
+2);
3154 discrim
->max_registered
= now
- ParseInterval(cmp
+1) - 1;
3157 send_message(user
, nickserv
, "MSG_INVALID_CRITERIA", cmp
);
3159 } else if (!irccasecmp(argv
[i
], "seen")) {
3160 discrim
->lastseen
= now
- ParseInterval(argv
[++i
]);
3161 } else if (!nickserv_conf
.disable_nicks
&& !irccasecmp(argv
[i
], "nickmask")) {
3162 discrim
->nickmask
= argv
[++i
];
3163 } else if (!irccasecmp(argv
[i
], "hostmask")) {
3165 if (!irccasecmp(argv
[i
], "exact")) {
3166 if (i
== argc
- 1) {
3167 send_message(user
, nickserv
, "MSG_MISSING_PARAMS", argv
[i
]);
3170 discrim
->hostmask_type
= EXACT
;
3171 } else if (!irccasecmp(argv
[i
], "subset")) {
3172 if (i
== argc
- 1) {
3173 send_message(user
, nickserv
, "MSG_MISSING_PARAMS", argv
[i
]);
3176 discrim
->hostmask_type
= SUBSET
;
3177 } else if (!irccasecmp(argv
[i
], "superset")) {
3178 if (i
== argc
- 1) {
3179 send_message(user
, nickserv
, "MSG_MISSING_PARAMS", argv
[i
]);
3182 discrim
->hostmask_type
= SUPERSET
;
3183 } else if (!irccasecmp(argv
[i
], "lastquit") || !irccasecmp(argv
[i
], "lastauth")) {
3184 if (i
== argc
- 1) {
3185 send_message(user
, nickserv
, "MSG_MISSING_PARAMS", argv
[i
]);
3188 discrim
->hostmask_type
= LASTQUIT
;
3191 discrim
->hostmask_type
= SUPERSET
;
3193 discrim
->hostmask
= argv
[++i
];
3194 } else if (!irccasecmp(argv
[i
], "handlemask") || !irccasecmp(argv
[i
], "accountmask")) {
3195 if (!irccasecmp(argv
[++i
], "*")) {
3196 discrim
->handlemask
= 0;
3198 discrim
->handlemask
= argv
[i
];
3200 } else if (!irccasecmp(argv
[i
], "email")) {
3201 if (user
->handle_info
->opserv_level
< nickserv_conf
.email_search_level
) {
3202 send_message(user
, nickserv
, "MSG_NO_SEARCH_ACCESS", "email");
3204 } else if (!irccasecmp(argv
[++i
], "*")) {
3205 discrim
->emailmask
= 0;
3207 discrim
->emailmask
= argv
[i
];
3209 } else if (!irccasecmp(argv
[i
], "access")) {
3210 const char *cmp
= argv
[++i
];
3211 if (cmp
[0] == '<') {
3212 if (discrim
->min_level
== 0) discrim
->min_level
= 1;
3213 if (cmp
[1] == '=') {
3214 discrim
->max_level
= strtoul(cmp
+2, NULL
, 0);
3216 discrim
->max_level
= strtoul(cmp
+1, NULL
, 0) - 1;
3218 } else if (cmp
[0] == '=') {
3219 discrim
->min_level
= discrim
->max_level
= strtoul(cmp
+1, NULL
, 0);
3220 } else if (cmp
[0] == '>') {
3221 if (cmp
[1] == '=') {
3222 discrim
->min_level
= strtoul(cmp
+2, NULL
, 0);
3224 discrim
->min_level
= strtoul(cmp
+1, NULL
, 0) + 1;
3227 send_message(user
, nickserv
, "MSG_INVALID_CRITERIA", cmp
);
3230 send_message(user
, nickserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
3241 nickserv_discrim_match(struct nickserv_discrim
*discrim
, struct handle_info
*hi
)
3243 if (((discrim
->flags_on
& hi
->flags
) != discrim
->flags_on
)
3244 || (discrim
->flags_off
& hi
->flags
)
3245 || (discrim
->min_registered
> hi
->registered
)
3246 || (discrim
->max_registered
< hi
->registered
)
3247 || (discrim
->lastseen
< (hi
->users
?now
:hi
->lastseen
))
3248 || (discrim
->handlemask
&& !match_ircglob(hi
->handle
, discrim
->handlemask
))
3249 || (discrim
->emailmask
&& (!hi
->email_addr
|| !match_ircglob(hi
->email_addr
, discrim
->emailmask
)))
3250 || (discrim
->min_level
> hi
->opserv_level
)
3251 || (discrim
->max_level
< hi
->opserv_level
)) {
3254 if (discrim
->hostmask
) {
3256 for (i
=0; i
<hi
->masks
->used
; i
++) {
3257 const char *mask
= hi
->masks
->list
[i
];
3258 if ((discrim
->hostmask_type
== SUBSET
)
3259 && (match_ircglobs(discrim
->hostmask
, mask
))) break;
3260 else if ((discrim
->hostmask_type
== EXACT
)
3261 && !irccasecmp(discrim
->hostmask
, mask
)) break;
3262 else if ((discrim
->hostmask_type
== SUPERSET
)
3263 && (match_ircglobs(mask
, discrim
->hostmask
))) break;
3264 else if ((discrim
->hostmask_type
== LASTQUIT
)
3265 && (match_ircglobs(discrim
->hostmask
, hi
->last_quit_host
))) break;
3267 if (i
==hi
->masks
->used
) return 0;
3269 if (discrim
->nickmask
) {
3270 struct nick_info
*nick
= hi
->nicks
;
3272 if (match_ircglob(nick
->nick
, discrim
->nickmask
)) break;
3275 if (!nick
) return 0;
3281 nickserv_discrim_search(struct nickserv_discrim
*discrim
, discrim_search_func dsf
, struct userNode
*source
)
3283 dict_iterator_t it
, next
;
3284 unsigned int matched
;
3286 for (it
= dict_first(nickserv_handle_dict
), matched
= 0;
3287 it
&& (matched
< discrim
->limit
);
3289 next
= iter_next(it
);
3290 if (nickserv_discrim_match(discrim
, iter_data(it
))) {
3291 dsf(source
, iter_data(it
));
3299 search_print_func(struct userNode
*source
, struct handle_info
*match
)
3301 send_message(source
, nickserv
, "NSMSG_SEARCH_MATCH", match
->handle
);
3305 search_count_func(UNUSED_ARG(struct userNode
*source
), UNUSED_ARG(struct handle_info
*match
))
3310 search_unregister_func (struct userNode
*source
, struct handle_info
*match
)
3312 if (oper_has_access(source
, nickserv
, match
->opserv_level
, 0))
3313 nickserv_unregister_handle(match
, source
);
3317 nickserv_sort_accounts_by_access(const void *a
, const void *b
)
3319 const struct handle_info
*hi_a
= *(const struct handle_info
**)a
;
3320 const struct handle_info
*hi_b
= *(const struct handle_info
**)b
;
3321 if (hi_a
->opserv_level
!= hi_b
->opserv_level
)
3322 return hi_b
->opserv_level
- hi_a
->opserv_level
;
3323 return irccasecmp(hi_a
->handle
, hi_b
->handle
);
3327 nickserv_show_oper_accounts(struct userNode
*user
, struct svccmd
*cmd
)
3329 struct handle_info_list hil
;
3330 struct helpfile_table tbl
;
3335 memset(&hil
, 0, sizeof(hil
));
3336 for (it
= dict_first(nickserv_handle_dict
); it
; it
= iter_next(it
)) {
3337 struct handle_info
*hi
= iter_data(it
);
3338 if (hi
->opserv_level
)
3339 handle_info_list_append(&hil
, hi
);
3341 qsort(hil
.list
, hil
.used
, sizeof(hil
.list
[0]), nickserv_sort_accounts_by_access
);
3342 tbl
.length
= hil
.used
+ 1;
3344 tbl
.flags
= TABLE_NO_FREE
;
3345 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
3346 tbl
.contents
[0] = ary
= malloc(tbl
.width
* sizeof(ary
[0]));
3349 for (ii
= 0; ii
< hil
.used
; ) {
3350 ary
= malloc(tbl
.width
* sizeof(ary
[0]));
3351 ary
[0] = hil
.list
[ii
]->handle
;
3352 ary
[1] = strtab(hil
.list
[ii
]->opserv_level
);
3353 tbl
.contents
[++ii
] = ary
;
3355 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3356 reply("MSG_MATCH_COUNT", hil
.used
);
3357 for (ii
= 0; ii
< hil
.used
; ii
++)
3358 free(tbl
.contents
[ii
]);
3363 static NICKSERV_FUNC(cmd_search
)
3365 struct nickserv_discrim
*discrim
;
3366 discrim_search_func action
;
3367 struct svccmd
*subcmd
;
3368 unsigned int matches
;
3371 NICKSERV_MIN_PARMS(3);
3372 sprintf(buf
, "search %s", argv
[1]);
3373 subcmd
= dict_find(nickserv_service
->commands
, buf
, NULL
);
3374 if (!irccasecmp(argv
[1], "print"))
3375 action
= search_print_func
;
3376 else if (!irccasecmp(argv
[1], "count"))
3377 action
= search_count_func
;
3378 else if (!irccasecmp(argv
[1], "unregister"))
3379 action
= search_unregister_func
;
3381 reply("NSMSG_INVALID_ACTION", argv
[1]);
3385 if (subcmd
&& !svccmd_can_invoke(user
, nickserv
, subcmd
, NULL
, SVCCMD_NOISY
))
3388 discrim
= nickserv_discrim_create(user
, argc
-2, argv
+2);
3392 if (action
== search_print_func
)
3393 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3394 else if (action
== search_count_func
)
3395 discrim
->limit
= INT_MAX
;
3397 matches
= nickserv_discrim_search(discrim
, action
, user
);
3400 reply("MSG_MATCH_COUNT", matches
);
3402 reply("MSG_NO_MATCHES");
3408 static MODCMD_FUNC(cmd_checkpass
)
3410 struct handle_info
*hi
;
3412 NICKSERV_MIN_PARMS(3);
3413 if (!(hi
= get_handle_info(argv
[1]))) {
3414 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
3417 if (checkpass(argv
[2], hi
->passwd
))
3418 reply("CHECKPASS_YES");
3420 reply("CHECKPASS_NO");
3426 nickserv_db_read_handle(const char *handle
, dict_t obj
)
3429 struct string_list
*masks
, *slist
;
3430 struct handle_info
*hi
;
3431 struct userNode
*authed_users
;
3432 unsigned long int id
;
3436 str
= database_get_data(obj
, KEY_ID
, RECDB_QSTRING
);
3437 id
= str
? strtoul(str
, NULL
, 0) : 0;
3438 str
= database_get_data(obj
, KEY_PASSWD
, RECDB_QSTRING
);
3440 log_module(NS_LOG
, LOG_WARNING
, "did not find a password for %s -- skipping user.", handle
);
3443 if ((hi
= get_handle_info(handle
))) {
3444 authed_users
= hi
->users
;
3446 dict_remove(nickserv_handle_dict
, hi
->handle
);
3448 authed_users
= NULL
;
3450 hi
= register_handle(handle
, str
, id
);
3452 hi
->users
= authed_users
;
3453 while (authed_users
) {
3454 authed_users
->handle_info
= hi
;
3455 authed_users
= authed_users
->next_authed
;
3458 masks
= database_get_data(obj
, KEY_MASKS
, RECDB_STRING_LIST
);
3459 hi
->masks
= masks
? string_list_copy(masks
) : alloc_string_list(1);
3460 str
= database_get_data(obj
, KEY_MAXLOGINS
, RECDB_QSTRING
);
3461 hi
->maxlogins
= str
? strtoul(str
, NULL
, 0) : 0;
3462 str
= database_get_data(obj
, KEY_LANGUAGE
, RECDB_QSTRING
);
3463 hi
->language
= language_find(str
? str
: "C");
3464 str
= database_get_data(obj
, KEY_OPSERV_LEVEL
, RECDB_QSTRING
);
3465 hi
->opserv_level
= str
? strtoul(str
, NULL
, 0) : 0;
3466 str
= database_get_data(obj
, KEY_INFO
, RECDB_QSTRING
);
3468 hi
->infoline
= strdup(str
);
3469 str
= database_get_data(obj
, KEY_REGISTER_ON
, RECDB_QSTRING
);
3470 hi
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
3471 str
= database_get_data(obj
, KEY_LAST_SEEN
, RECDB_QSTRING
);
3472 hi
->lastseen
= str
? (time_t)strtoul(str
, NULL
, 0) : hi
->registered
;
3473 /* We want to read the nicks even if disable_nicks is set. This is so
3474 * that we don't lose the nick data entirely. */
3475 slist
= database_get_data(obj
, KEY_NICKS
, RECDB_STRING_LIST
);
3477 for (ii
=0; ii
<slist
->used
; ii
++)
3478 register_nick(slist
->list
[ii
], hi
);
3480 str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
);
3482 for (ii
=0; str
[ii
]; ii
++)
3483 hi
->flags
|= 1 << (handle_inverse_flags
[(unsigned char)str
[ii
]] - 1);
3485 str
= database_get_data(obj
, KEY_USERLIST_STYLE
, RECDB_QSTRING
);
3486 hi
->userlist_style
= str
? str
[0] : HI_STYLE_ZOOT
;
3487 str
= database_get_data(obj
, KEY_ANNOUNCEMENTS
, RECDB_QSTRING
);
3488 hi
->announcements
= str
? str
[0] : '?';
3489 str
= database_get_data(obj
, KEY_SCREEN_WIDTH
, RECDB_QSTRING
);
3490 hi
->screen_width
= str
? strtoul(str
, NULL
, 0) : 0;
3491 str
= database_get_data(obj
, KEY_TABLE_WIDTH
, RECDB_QSTRING
);
3492 hi
->table_width
= str
? strtoul(str
, NULL
, 0) : 0;
3493 str
= database_get_data(obj
, KEY_LAST_QUIT_HOST
, RECDB_QSTRING
);
3495 str
= database_get_data(obj
, KEY_LAST_AUTHED_HOST
, RECDB_QSTRING
);
3497 safestrncpy(hi
->last_quit_host
, str
, sizeof(hi
->last_quit_host
));
3498 str
= database_get_data(obj
, KEY_EMAIL_ADDR
, RECDB_QSTRING
);
3500 nickserv_set_email_addr(hi
, str
);
3501 str
= database_get_data(obj
, KEY_EPITHET
, RECDB_QSTRING
);
3503 hi
->epithet
= strdup(str
);
3504 str
= database_get_data(obj
, KEY_FAKEHOST
, RECDB_QSTRING
);
3506 hi
->fakehost
= strdup(str
);
3507 subdb
= database_get_data(obj
, KEY_COOKIE
, RECDB_OBJECT
);
3509 const char *data
, *type
, *expires
, *cookie_str
;
3510 struct handle_cookie
*cookie
;
3512 cookie
= calloc(1, sizeof(*cookie
));
3513 type
= database_get_data(subdb
, KEY_COOKIE_TYPE
, RECDB_QSTRING
);
3514 data
= database_get_data(subdb
, KEY_COOKIE_DATA
, RECDB_QSTRING
);
3515 expires
= database_get_data(subdb
, KEY_COOKIE_EXPIRES
, RECDB_QSTRING
);
3516 cookie_str
= database_get_data(subdb
, KEY_COOKIE
, RECDB_QSTRING
);
3517 if (!type
|| !expires
|| !cookie_str
) {
3518 log_module(NS_LOG
, LOG_ERROR
, "Missing field(s) from cookie for account %s; dropping cookie.", hi
->handle
);
3521 if (!irccasecmp(type
, KEY_ACTIVATION
))
3522 cookie
->type
= ACTIVATION
;
3523 else if (!irccasecmp(type
, KEY_PASSWORD_CHANGE
))
3524 cookie
->type
= PASSWORD_CHANGE
;
3525 else if (!irccasecmp(type
, KEY_EMAIL_CHANGE
))
3526 cookie
->type
= EMAIL_CHANGE
;
3527 else if (!irccasecmp(type
, KEY_ALLOWAUTH
))
3528 cookie
->type
= ALLOWAUTH
;
3530 log_module(NS_LOG
, LOG_ERROR
, "Invalid cookie type %s for account %s; dropping cookie.", type
, handle
);
3533 cookie
->expires
= strtoul(expires
, NULL
, 0);
3534 if (cookie
->expires
< now
)
3537 cookie
->data
= strdup(data
);
3538 safestrncpy(cookie
->cookie
, cookie_str
, sizeof(cookie
->cookie
));
3542 nickserv_bake_cookie(cookie
);
3544 nickserv_free_cookie(cookie
);
3549 nickserv_saxdb_read(dict_t db
) {
3551 struct record_data
*rd
;
3553 for (it
=dict_first(db
); it
; it
=iter_next(it
)) {
3555 nickserv_db_read_handle(iter_key(it
), rd
->d
.object
);
3560 static NICKSERV_FUNC(cmd_mergedb
)
3562 struct timeval start
, stop
;
3565 NICKSERV_MIN_PARMS(2);
3566 gettimeofday(&start
, NULL
);
3567 if (!(db
= parse_database(argv
[1]))) {
3568 reply("NSMSG_DB_UNREADABLE", argv
[1]);
3571 nickserv_saxdb_read(db
);
3573 gettimeofday(&stop
, NULL
);
3574 stop
.tv_sec
-= start
.tv_sec
;
3575 stop
.tv_usec
-= start
.tv_usec
;
3576 if (stop
.tv_usec
< 0) {
3578 stop
.tv_usec
+= 1000000;
3580 reply("NSMSG_DB_MERGED", argv
[1], stop
.tv_sec
, stop
.tv_usec
/1000);
3585 expire_handles(UNUSED_ARG(void *data
))
3587 dict_iterator_t it
, next
;
3589 struct handle_info
*hi
;
3591 for (it
=dict_first(nickserv_handle_dict
); it
; it
=next
) {
3592 next
= iter_next(it
);
3594 if ((hi
->opserv_level
> 0)
3596 || HANDLE_FLAGGED(hi
, FROZEN
)
3597 || HANDLE_FLAGGED(hi
, NODELETE
)) {
3600 expiry
= hi
->channels
? nickserv_conf
.handle_expire_delay
: nickserv_conf
.nochan_handle_expire_delay
;
3601 if ((now
- hi
->lastseen
) > expiry
) {
3602 log_module(NS_LOG
, LOG_INFO
, "Expiring account %s for inactivity.", hi
->handle
);
3603 nickserv_unregister_handle(hi
, NULL
);
3607 if (nickserv_conf
.handle_expire_frequency
)
3608 timeq_add(now
+ nickserv_conf
.handle_expire_frequency
, expire_handles
, NULL
);
3612 nickserv_load_dict(const char *fname
)
3616 if (!(file
= fopen(fname
, "r"))) {
3617 log_module(NS_LOG
, LOG_ERROR
, "Unable to open dictionary file %s: %s", fname
, strerror(errno
));
3620 while (!feof(file
)) {
3621 fgets(line
, sizeof(line
), file
);
3624 if (line
[strlen(line
)-1] == '\n')
3625 line
[strlen(line
)-1] = 0;
3626 dict_insert(nickserv_conf
.weak_password_dict
, strdup(line
), NULL
);
3629 log_module(NS_LOG
, LOG_INFO
, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf
.weak_password_dict
));
3632 static enum reclaim_action
3633 reclaim_action_from_string(const char *str
) {
3635 return RECLAIM_NONE
;
3636 else if (!irccasecmp(str
, "warn"))
3637 return RECLAIM_WARN
;
3638 else if (!irccasecmp(str
, "svsnick"))
3639 return RECLAIM_SVSNICK
;
3640 else if (!irccasecmp(str
, "kill"))
3641 return RECLAIM_KILL
;
3643 return RECLAIM_NONE
;
3647 nickserv_conf_read(void)
3649 dict_t conf_node
, child
;
3653 if (!(conf_node
= conf_get_data(NICKSERV_CONF_NAME
, RECDB_OBJECT
))) {
3654 log_module(NS_LOG
, LOG_ERROR
, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME
);
3657 str
= database_get_data(conf_node
, KEY_VALID_HANDLE_REGEX
, RECDB_QSTRING
);
3659 str
= database_get_data(conf_node
, KEY_VALID_ACCOUNT_REGEX
, RECDB_QSTRING
);
3660 if (nickserv_conf
.valid_handle_regex_set
)
3661 regfree(&nickserv_conf
.valid_handle_regex
);
3663 int err
= regcomp(&nickserv_conf
.valid_handle_regex
, str
, REG_EXTENDED
|REG_ICASE
|REG_NOSUB
);
3664 nickserv_conf
.valid_handle_regex_set
= !err
;
3665 if (err
) log_module(NS_LOG
, LOG_ERROR
, "Bad valid_account_regex (error %d)", err
);
3667 nickserv_conf
.valid_handle_regex_set
= 0;
3669 str
= database_get_data(conf_node
, KEY_VALID_NICK_REGEX
, RECDB_QSTRING
);
3670 if (nickserv_conf
.valid_nick_regex_set
)
3671 regfree(&nickserv_conf
.valid_nick_regex
);
3673 int err
= regcomp(&nickserv_conf
.valid_nick_regex
, str
, REG_EXTENDED
|REG_ICASE
|REG_NOSUB
);
3674 nickserv_conf
.valid_nick_regex_set
= !err
;
3675 if (err
) log_module(NS_LOG
, LOG_ERROR
, "Bad valid_nick_regex (error %d)", err
);
3677 nickserv_conf
.valid_nick_regex_set
= 0;
3679 str
= database_get_data(conf_node
, KEY_NICKS_PER_HANDLE
, RECDB_QSTRING
);
3681 str
= database_get_data(conf_node
, KEY_NICKS_PER_ACCOUNT
, RECDB_QSTRING
);
3682 nickserv_conf
.nicks_per_handle
= str
? strtoul(str
, NULL
, 0) : 4;
3683 str
= database_get_data(conf_node
, KEY_DISABLE_NICKS
, RECDB_QSTRING
);
3684 nickserv_conf
.disable_nicks
= str
? strtoul(str
, NULL
, 0) : 0;
3685 str
= database_get_data(conf_node
, KEY_DEFAULT_HOSTMASK
, RECDB_QSTRING
);
3686 nickserv_conf
.default_hostmask
= str
? !disabled_string(str
) : 0;
3687 str
= database_get_data(conf_node
, KEY_PASSWORD_MIN_LENGTH
, RECDB_QSTRING
);
3688 nickserv_conf
.password_min_length
= str
? strtoul(str
, NULL
, 0) : 0;
3689 str
= database_get_data(conf_node
, KEY_PASSWORD_MIN_DIGITS
, RECDB_QSTRING
);
3690 nickserv_conf
.password_min_digits
= str
? strtoul(str
, NULL
, 0) : 0;
3691 str
= database_get_data(conf_node
, KEY_PASSWORD_MIN_UPPER
, RECDB_QSTRING
);
3692 nickserv_conf
.password_min_upper
= str
? strtoul(str
, NULL
, 0) : 0;
3693 str
= database_get_data(conf_node
, KEY_PASSWORD_MIN_LOWER
, RECDB_QSTRING
);
3694 nickserv_conf
.password_min_lower
= str
? strtoul(str
, NULL
, 0) : 0;
3695 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
3696 nickserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
3697 str
= database_get_data(conf_node
, KEY_MODOPER_LEVEL
, RECDB_QSTRING
);
3698 nickserv_conf
.modoper_level
= str
? strtoul(str
, NULL
, 0) : 900;
3699 str
= database_get_data(conf_node
, KEY_SET_EPITHET_LEVEL
, RECDB_QSTRING
);
3700 nickserv_conf
.set_epithet_level
= str
? strtoul(str
, NULL
, 0) : 1;
3701 str
= database_get_data(conf_node
, KEY_SET_TITLE_LEVEL
, RECDB_QSTRING
);
3702 nickserv_conf
.set_title_level
= str
? strtoul(str
, NULL
, 0) : 900;
3703 str
= database_get_data(conf_node
, KEY_SET_FAKEHOST_LEVEL
, RECDB_QSTRING
);
3704 nickserv_conf
.set_fakehost_level
= str
? strtoul(str
, NULL
, 0) : 1000;
3705 str
= database_get_data(conf_node
, KEY_HANDLE_EXPIRE_FREQ
, RECDB_QSTRING
);
3707 str
= database_get_data(conf_node
, KEY_ACCOUNT_EXPIRE_FREQ
, RECDB_QSTRING
);
3708 nickserv_conf
.handle_expire_frequency
= str
? ParseInterval(str
) : 86400;
3709 str
= database_get_data(conf_node
, KEY_HANDLE_EXPIRE_DELAY
, RECDB_QSTRING
);
3711 str
= database_get_data(conf_node
, KEY_ACCOUNT_EXPIRE_DELAY
, RECDB_QSTRING
);
3712 nickserv_conf
.handle_expire_delay
= str
? ParseInterval(str
) : 86400*30;
3713 str
= database_get_data(conf_node
, KEY_NOCHAN_HANDLE_EXPIRE_DELAY
, RECDB_QSTRING
);
3715 str
= database_get_data(conf_node
, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY
, RECDB_QSTRING
);
3716 nickserv_conf
.nochan_handle_expire_delay
= str
? ParseInterval(str
) : 86400*15;
3717 str
= database_get_data(conf_node
, "warn_clone_auth", RECDB_QSTRING
);
3718 nickserv_conf
.warn_clone_auth
= str
? !disabled_string(str
) : 1;
3719 str
= database_get_data(conf_node
, "default_maxlogins", RECDB_QSTRING
);
3720 nickserv_conf
.default_maxlogins
= str
? strtoul(str
, NULL
, 0) : 2;
3721 str
= database_get_data(conf_node
, "hard_maxlogins", RECDB_QSTRING
);
3722 nickserv_conf
.hard_maxlogins
= str
? strtoul(str
, NULL
, 0) : 10;
3723 if (!nickserv_conf
.disable_nicks
) {
3724 str
= database_get_data(conf_node
, "reclaim_action", RECDB_QSTRING
);
3725 nickserv_conf
.reclaim_action
= str
? reclaim_action_from_string(str
) : RECLAIM_NONE
;
3726 str
= database_get_data(conf_node
, "warn_nick_owned", RECDB_QSTRING
);
3727 nickserv_conf
.warn_nick_owned
= str
? enabled_string(str
) : 0;
3728 str
= database_get_data(conf_node
, "auto_reclaim_action", RECDB_QSTRING
);
3729 nickserv_conf
.auto_reclaim_action
= str
? reclaim_action_from_string(str
) : RECLAIM_NONE
;
3730 str
= database_get_data(conf_node
, "auto_reclaim_delay", RECDB_QSTRING
);
3731 nickserv_conf
.auto_reclaim_delay
= str
? ParseInterval(str
) : 0;
3733 child
= database_get_data(conf_node
, KEY_FLAG_LEVELS
, RECDB_OBJECT
);
3734 for (it
=dict_first(child
); it
; it
=iter_next(it
)) {
3735 const char *key
= iter_key(it
), *value
;
3739 if (!strncasecmp(key
, "uc_", 3))
3740 flag
= toupper(key
[3]);
3741 else if (!strncasecmp(key
, "lc_", 3))
3742 flag
= tolower(key
[3]);
3746 if ((pos
= handle_inverse_flags
[flag
])) {
3747 value
= GET_RECORD_QSTRING((struct record_data
*)iter_data(it
));
3748 flag_access_levels
[pos
- 1] = strtoul(value
, NULL
, 0);
3751 if (nickserv_conf
.weak_password_dict
)
3752 dict_delete(nickserv_conf
.weak_password_dict
);
3753 nickserv_conf
.weak_password_dict
= dict_new();
3754 dict_set_free_keys(nickserv_conf
.weak_password_dict
, free
);
3755 dict_insert(nickserv_conf
.weak_password_dict
, strdup("password"), NULL
);
3756 dict_insert(nickserv_conf
.weak_password_dict
, strdup("<password>"), NULL
);
3757 str
= database_get_data(conf_node
, KEY_DICT_FILE
, RECDB_QSTRING
);
3759 nickserv_load_dict(str
);
3760 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
3761 if (nickserv
&& str
)
3762 NickChange(nickserv
, str
, 0);
3763 str
= database_get_data(conf_node
, KEY_AUTOGAG_ENABLED
, RECDB_QSTRING
);
3764 nickserv_conf
.autogag_enabled
= str
? strtoul(str
, NULL
, 0) : 1;
3765 str
= database_get_data(conf_node
, KEY_AUTOGAG_DURATION
, RECDB_QSTRING
);
3766 nickserv_conf
.autogag_duration
= str
? ParseInterval(str
) : 1800;
3767 str
= database_get_data(conf_node
, KEY_EMAIL_VISIBLE_LEVEL
, RECDB_QSTRING
);
3768 nickserv_conf
.email_visible_level
= str
? strtoul(str
, NULL
, 0) : 800;
3769 str
= database_get_data(conf_node
, KEY_EMAIL_ENABLED
, RECDB_QSTRING
);
3770 nickserv_conf
.email_enabled
= str
? enabled_string(str
) : 0;
3771 str
= database_get_data(conf_node
, KEY_SYNC_LOG
, RECDB_QSTRING
);
3772 nickserv_conf
.sync_log
= str
? enabled_string(str
) : 0;
3773 str
= database_get_data(conf_node
, KEY_COOKIE_TIMEOUT
, RECDB_QSTRING
);
3774 nickserv_conf
.cookie_timeout
= str
? ParseInterval(str
) : 24*3600;
3775 str
= database_get_data(conf_node
, KEY_EMAIL_REQUIRED
, RECDB_QSTRING
);
3776 nickserv_conf
.email_required
= (nickserv_conf
.email_enabled
&& str
) ? enabled_string(str
) : 0;
3777 str
= database_get_data(conf_node
, KEY_ACCOUNTS_PER_EMAIL
, RECDB_QSTRING
);
3778 nickserv_conf
.handles_per_email
= str
? strtoul(str
, NULL
, 0) : 1;
3779 str
= database_get_data(conf_node
, KEY_EMAIL_SEARCH_LEVEL
, RECDB_QSTRING
);
3780 nickserv_conf
.email_search_level
= str
? strtoul(str
, NULL
, 0) : 600;
3781 str
= database_get_data(conf_node
, KEY_TITLEHOST_SUFFIX
, RECDB_QSTRING
);
3782 nickserv_conf
.titlehost_suffix
= str
? str
: "example.net";
3783 str
= conf_get_data("server/network", RECDB_QSTRING
);
3784 nickserv_conf
.network_name
= str
? str
: "some IRC network";
3785 if (!nickserv_conf
.auth_policer_params
) {
3786 nickserv_conf
.auth_policer_params
= policer_params_new();
3787 policer_params_set(nickserv_conf
.auth_policer_params
, "size", "5");
3788 policer_params_set(nickserv_conf
.auth_policer_params
, "drain-rate", "0.05");
3790 child
= database_get_data(conf_node
, KEY_AUTH_POLICER
, RECDB_OBJECT
);
3791 for (it
=dict_first(child
); it
; it
=iter_next(it
))
3792 set_policer_param(iter_key(it
), iter_data(it
), nickserv_conf
.auth_policer_params
);
3796 nickserv_reclaim(struct userNode
*user
, struct nick_info
*ni
, enum reclaim_action action
) {
3798 char newnick
[NICKLEN
+1];
3807 send_message(user
, nickserv
, "NSMSG_RECLAIM_WARN", ni
->nick
, ni
->owner
->handle
);
3809 case RECLAIM_SVSNICK
:
3811 snprintf(newnick
, sizeof(newnick
), "Guest%d", rand()%10000
);
3812 } while (GetUserH(newnick
));
3813 irc_svsnick(nickserv
, user
, newnick
);
3816 msg
= user_find_message(user
, "NSMSG_RECLAIM_KILL");
3817 irc_kill(nickserv
, user
, msg
);
3823 nickserv_reclaim_p(void *data
) {
3824 struct userNode
*user
= data
;
3825 struct nick_info
*ni
= get_nick_info(user
->nick
);
3827 nickserv_reclaim(user
, ni
, nickserv_conf
.auto_reclaim_action
);
3831 check_user_nick(struct userNode
*user
) {
3832 struct nick_info
*ni
;
3833 user
->modes
&= ~FLAGS_REGNICK
;
3834 if (!(ni
= get_nick_info(user
->nick
)))
3836 if (user
->handle_info
== ni
->owner
) {
3837 user
->modes
|= FLAGS_REGNICK
;
3841 if (nickserv_conf
.warn_nick_owned
)
3842 send_message(user
, nickserv
, "NSMSG_RECLAIM_WARN", ni
->nick
, ni
->owner
->handle
);
3843 if (nickserv_conf
.auto_reclaim_action
== RECLAIM_NONE
)
3845 if (nickserv_conf
.auto_reclaim_delay
)
3846 timeq_add(now
+ nickserv_conf
.auto_reclaim_delay
, nickserv_reclaim_p
, user
);
3848 nickserv_reclaim(user
, ni
, nickserv_conf
.auto_reclaim_action
);
3853 handle_new_user(struct userNode
*user
)
3855 return check_user_nick(user
);
3859 handle_account(struct userNode
*user
, const char *stamp
)
3861 struct handle_info
*hi
;
3864 #ifdef WITH_PROTOCOL_P10
3865 time_t timestamp
= 0;
3867 colon
= strchr(stamp
, ':');
3868 if(colon
&& colon
[1])
3871 timestamp
= atoi(colon
+1);
3873 hi
= dict_find(nickserv_handle_dict
, stamp
, NULL
);
3874 if(hi
&& timestamp
&& hi
->registered
!= timestamp
)
3876 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
);
3880 hi
= dict_find(nickserv_id_dict
, stamp
, NULL
);
3881 log_module(MAIN_LOG
, LOG_WARNING
, "Using non-P10 code in accounts, not tested at all!");
3885 if (HANDLE_FLAGGED(hi
, SUSPENDED
)) {
3888 set_user_handle_info(user
, hi
, 0);
3890 log_module(MAIN_LOG
, LOG_WARNING
, "%s had unknown account stamp %s.", user
->nick
, stamp
);
3895 handle_nick_change(struct userNode
*user
, const char *old_nick
)
3897 struct handle_info
*hi
;
3899 if ((hi
= dict_find(nickserv_allow_auth_dict
, old_nick
, 0))) {
3900 dict_remove(nickserv_allow_auth_dict
, old_nick
);
3901 dict_insert(nickserv_allow_auth_dict
, user
->nick
, hi
);
3903 timeq_del(0, nickserv_reclaim_p
, user
, TIMEQ_IGNORE_WHEN
);
3904 check_user_nick(user
);
3908 nickserv_remove_user(struct userNode
*user
, UNUSED_ARG(struct userNode
*killer
), UNUSED_ARG(const char *why
))
3910 dict_remove(nickserv_allow_auth_dict
, user
->nick
);
3911 timeq_del(0, nickserv_reclaim_p
, user
, TIMEQ_IGNORE_WHEN
);
3912 set_user_handle_info(user
, NULL
, 0);
3915 static struct modcmd
*
3916 nickserv_define_func(const char *name
, modcmd_func_t func
, int min_level
, int must_auth
, int must_be_qualified
)
3918 if (min_level
> 0) {
3920 sprintf(buf
, "%u", min_level
);
3921 if (must_be_qualified
) {
3922 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), "level", buf
, "flags", "+qualified,+loghostmask", NULL
);
3924 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), "level", buf
, NULL
);
3926 } else if (min_level
== 0) {
3927 if (must_be_qualified
) {
3928 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), "flags", "+helping", NULL
);
3930 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), "flags", "+helping", NULL
);
3933 if (must_be_qualified
) {
3934 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), "flags", "+qualified,+loghostmask", NULL
);
3936 return modcmd_register(nickserv_module
, name
, func
, 1, (must_auth
? MODCMD_REQUIRE_AUTHED
: 0), NULL
);
3942 nickserv_db_cleanup(void)
3944 unreg_del_user_func(nickserv_remove_user
);
3945 userList_clean(&curr_helpers
);
3946 policer_params_delete(nickserv_conf
.auth_policer_params
);
3947 dict_delete(nickserv_handle_dict
);
3948 dict_delete(nickserv_nick_dict
);
3949 dict_delete(nickserv_opt_dict
);
3950 dict_delete(nickserv_allow_auth_dict
);
3951 dict_delete(nickserv_email_dict
);
3952 dict_delete(nickserv_id_dict
);
3953 dict_delete(nickserv_conf
.weak_password_dict
);
3954 free(auth_func_list
);
3955 free(unreg_func_list
);
3957 free(allowauth_func_list
);
3958 free(handle_merge_func_list
);
3959 free(failpw_func_list
);
3960 if (nickserv_conf
.valid_handle_regex_set
)
3961 regfree(&nickserv_conf
.valid_handle_regex
);
3962 if (nickserv_conf
.valid_nick_regex_set
)
3963 regfree(&nickserv_conf
.valid_nick_regex
);
3967 init_nickserv(const char *nick
)
3970 NS_LOG
= log_register_type("NickServ", "file:nickserv.log");
3971 reg_new_user_func(handle_new_user
);
3972 reg_nick_change_func(handle_nick_change
);
3973 reg_del_user_func(nickserv_remove_user
);
3974 reg_account_func(handle_account
);
3976 /* set up handle_inverse_flags */
3977 memset(handle_inverse_flags
, 0, sizeof(handle_inverse_flags
));
3978 for (i
=0; handle_flags
[i
]; i
++) {
3979 handle_inverse_flags
[(unsigned char)handle_flags
[i
]] = i
+ 1;
3980 flag_access_levels
[i
] = 0;
3983 conf_register_reload(nickserv_conf_read
);
3984 nickserv_opt_dict
= dict_new();
3985 nickserv_email_dict
= dict_new();
3986 dict_set_free_keys(nickserv_email_dict
, free
);
3987 dict_set_free_data(nickserv_email_dict
, nickserv_free_email_addr
);
3989 nickserv_module
= module_register("NickServ", NS_LOG
, "nickserv.help", NULL
);
3990 /* Removed qualified_host as default requirement for AUTH, REGISTER, PASS, etc. nets
3991 * can enable it per command using modcmd. (its a shitty default IMO, and now in 1.3
3992 * a big pain to disable since its nolonger in the config file. ) -Rubin
3994 modcmd_register(nickserv_module
, "AUTH", cmd_auth
, 2, MODCMD_KEEP_BOUND
, "flags", "+loghostmask", NULL
);
3995 nickserv_define_func("ALLOWAUTH", cmd_allowauth
, 0, 1, 0);
3996 nickserv_define_func("REGISTER", cmd_register
, -1, 0, 0);
3997 nickserv_define_func("OREGISTER", cmd_oregister
, 0, 1, 0);
3998 nickserv_define_func("UNREGISTER", cmd_unregister
, -1, 1, 0);
3999 nickserv_define_func("OUNREGISTER", cmd_ounregister
, 0, 1, 0);
4000 nickserv_define_func("ADDMASK", cmd_addmask
, -1, 1, 0);
4001 nickserv_define_func("OADDMASK", cmd_oaddmask
, 0, 1, 0);
4002 nickserv_define_func("DELMASK", cmd_delmask
, -1, 1, 0);
4003 nickserv_define_func("ODELMASK", cmd_odelmask
, 0, 1, 0);
4004 nickserv_define_func("PASS", cmd_pass
, -1, 1, 0);
4005 nickserv_define_func("SET", cmd_set
, -1, 1, 0);
4006 nickserv_define_func("OSET", cmd_oset
, 0, 1, 0);
4007 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo
, -1, 0, 0);
4008 nickserv_define_func("USERINFO", cmd_userinfo
, -1, 1, 0);
4009 nickserv_define_func("RENAME", cmd_rename_handle
, -1, 1, 0);
4010 nickserv_define_func("VACATION", cmd_vacation
, -1, 1, 0);
4011 nickserv_define_func("MERGE", cmd_merge
, 0, 1, 0);
4012 if (!nickserv_conf
.disable_nicks
) {
4013 /* nick management commands */
4014 nickserv_define_func("REGNICK", cmd_regnick
, -1, 1, 0);
4015 nickserv_define_func("OREGNICK", cmd_oregnick
, 0, 1, 0);
4016 nickserv_define_func("UNREGNICK", cmd_unregnick
, -1, 1, 0);
4017 nickserv_define_func("OUNREGNICK", cmd_ounregnick
, 0, 1, 0);
4018 nickserv_define_func("NICKINFO", cmd_nickinfo
, -1, 1, 0);
4019 nickserv_define_func("RECLAIM", cmd_reclaim
, -1, 1, 0);
4021 if (nickserv_conf
.email_enabled
) {
4022 nickserv_define_func("AUTHCOOKIE", cmd_authcookie
, -1, 0, 0);
4023 nickserv_define_func("RESETPASS", cmd_resetpass
, -1, 0, 0);
4024 nickserv_define_func("COOKIE", cmd_cookie
, -1, 0, 0);
4025 nickserv_define_func("DELCOOKIE", cmd_delcookie
, -1, 1, 0);
4026 nickserv_define_func("ODELCOOKIE", cmd_odelcookie
, 0, 1, 0);
4027 dict_insert(nickserv_opt_dict
, "EMAIL", opt_email
);
4029 nickserv_define_func("GHOST", cmd_ghost
, -1, 1, 0);
4030 /* miscellaneous commands */
4031 nickserv_define_func("STATUS", cmd_status
, -1, 0, 0);
4032 nickserv_define_func("SEARCH", cmd_search
, 100, 1, 0);
4033 nickserv_define_func("SEARCH UNREGISTER", NULL
, 800, 1, 0);
4034 nickserv_define_func("MERGEDB", cmd_mergedb
, 999, 1, 0);
4035 nickserv_define_func("CHECKPASS", cmd_checkpass
, 601, 1, 0);
4037 dict_insert(nickserv_opt_dict
, "INFO", opt_info
);
4038 dict_insert(nickserv_opt_dict
, "WIDTH", opt_width
);
4039 dict_insert(nickserv_opt_dict
, "TABLEWIDTH", opt_tablewidth
);
4040 dict_insert(nickserv_opt_dict
, "COLOR", opt_color
);
4041 dict_insert(nickserv_opt_dict
, "PRIVMSG", opt_privmsg
);
4042 /* dict_insert(nickserv_opt_dict, "STYLE", opt_style); */
4043 dict_insert(nickserv_opt_dict
, "PASS", opt_password
);
4044 dict_insert(nickserv_opt_dict
, "PASSWORD", opt_password
);
4045 dict_insert(nickserv_opt_dict
, "FLAGS", opt_flags
);
4046 dict_insert(nickserv_opt_dict
, "ACCESS", opt_level
);
4047 dict_insert(nickserv_opt_dict
, "LEVEL", opt_level
);
4048 dict_insert(nickserv_opt_dict
, "EPITHET", opt_epithet
);
4049 if (nickserv_conf
.titlehost_suffix
) {
4050 dict_insert(nickserv_opt_dict
, "TITLE", opt_title
);
4051 dict_insert(nickserv_opt_dict
, "FAKEHOST", opt_fakehost
);
4053 dict_insert(nickserv_opt_dict
, "ANNOUNCEMENTS", opt_announcements
);
4054 dict_insert(nickserv_opt_dict
, "MAXLOGINS", opt_maxlogins
);
4055 dict_insert(nickserv_opt_dict
, "LANGUAGE", opt_language
);
4057 nickserv_handle_dict
= dict_new();
4058 dict_set_free_keys(nickserv_handle_dict
, free
);
4059 dict_set_free_data(nickserv_handle_dict
, free_handle_info
);
4061 nickserv_id_dict
= dict_new();
4062 dict_set_free_keys(nickserv_id_dict
, free
);
4064 nickserv_nick_dict
= dict_new();
4065 dict_set_free_data(nickserv_nick_dict
, free
);
4067 nickserv_allow_auth_dict
= dict_new();
4069 userList_init(&curr_helpers
);
4072 const char *modes
= conf_get_data("services/nickserv/modes", RECDB_QSTRING
);
4073 nickserv
= AddService(nick
, modes
? modes
: NULL
, "Nick Services", NULL
);
4074 nickserv_service
= service_register(nickserv
);
4076 saxdb_register("NickServ", nickserv_saxdb_read
, nickserv_saxdb_write
);
4077 reg_exit_func(nickserv_db_cleanup
);
4078 if(nickserv_conf
.handle_expire_frequency
)
4079 timeq_add(now
+ nickserv_conf
.handle_expire_frequency
, expire_handles
, NULL
);
4080 message_register_table(msgtab
);