1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for gag_create(), opserv_bad_channel() */
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_COOKIE_TIMEOUT "cookie_timeout"
70 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
71 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
74 #define KEY_PASSWD "passwd"
75 #define KEY_NICKS "nicks"
76 #define KEY_MASKS "masks"
77 #define KEY_OPSERV_LEVEL "opserv_level"
78 #define KEY_FLAGS "flags"
79 #define KEY_REGISTER_ON "register"
80 #define KEY_LAST_SEEN "lastseen"
81 #define KEY_INFO "info"
82 #define KEY_USERLIST_STYLE "user_style"
83 #define KEY_SCREEN_WIDTH "screen_width"
84 #define KEY_LAST_AUTHED_HOST "last_authed_host"
85 #define KEY_LAST_QUIT_HOST "last_quit_host"
86 #define KEY_EMAIL_ADDR "email_addr"
87 #define KEY_COOKIE "cookie"
88 #define KEY_COOKIE_DATA "data"
89 #define KEY_COOKIE_TYPE "type"
90 #define KEY_COOKIE_EXPIRES "expires"
91 #define KEY_ACTIVATION "activation"
92 #define KEY_PASSWORD_CHANGE "password change"
93 #define KEY_EMAIL_CHANGE "email change"
94 #define KEY_ALLOWAUTH "allowauth"
95 #define KEY_EPITHET "epithet"
96 #define KEY_TABLE_WIDTH "table_width"
97 #define KEY_ANNOUNCEMENTS "announcements"
98 #define KEY_MAXLOGINS "maxlogins"
99 #define KEY_FAKEHOST "fakehost"
101 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
103 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
104 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
105 typedef OPTION_FUNC(option_func_t);
107 DEFINE_LIST(handle_info_list, struct handle_info*);
109 #define NICKSERV_MIN_PARMS(N) do { \
111 reply("MSG_MISSING_PARAMS", argv[0]); \
112 svccmd_send_help(user, nickserv, cmd); \
116 struct userNode *nickserv;
117 struct userList curr_helpers;
118 const char *handle_flags = HANDLE_FLAGS;
120 static struct module *nickserv_module;
121 static struct service *nickserv_service;
122 static struct log_type *NS_LOG;
123 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
124 static dict_t nickserv_id_dict; /* contains struct handle_info* */
125 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
126 static dict_t nickserv_opt_dict; /* contains option_func_t* */
127 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
128 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
129 static char handle_inverse_flags[256];
130 static unsigned int flag_access_levels[32];
131 static const struct message_entry msgtab[] = {
132 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
133 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
134 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
135 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
136 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
137 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
138 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
139 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
140 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
141 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
142 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
143 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
144 { "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." },
145 { "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." },
146 { "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." },
147 { "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." },
148 { "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." },
149 { "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." },
150 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
151 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
152 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
153 { "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." },
154 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
155 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
156 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
157 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
158 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
159 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
160 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
161 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
162 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
163 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
164 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
165 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
166 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
167 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
168 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
169 { "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)" },
170 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
171 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
172 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
173 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
174 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
175 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
176 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
177 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
178 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
179 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
180 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
181 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
182 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
183 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
184 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
185 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
186 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
187 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
188 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
189 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
190 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
191 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
192 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
193 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
194 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
195 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
196 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
197 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
198 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
199 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
200 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
201 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
202 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
203 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
204 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
205 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
206 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
207 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
208 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
209 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
210 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
211 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
212 { "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)." },
213 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
214 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
215 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
216 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
217 { "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." },
218 { "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." },
219 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
220 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
221 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
222 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
223 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
224 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
225 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
226 { "NSMSG_PASS_SUCCESS", "Password changed." },
227 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
228 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
229 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
230 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
231 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
232 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
233 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
234 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
235 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
236 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
237 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
238 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
239 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
240 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
241 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
242 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
243 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
244 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
245 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
246 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
247 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
248 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
249 { "NSMSG_NO_ACCESS", "Access denied." },
250 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
251 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
252 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
253 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
254 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
255 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
256 { "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." },
257 { "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." },
258 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
259 { "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." },
260 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
261 { "NSMSG_SEARCH_MATCH", "Match: %s" },
262 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
263 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
264 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
265 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
266 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
267 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
268 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
269 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
270 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
271 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
272 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
273 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
274 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
275 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
276 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
277 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
278 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
279 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
280 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
281 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
282 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
283 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
284 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
285 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
286 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
287 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
288 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
289 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
290 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
291 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
292 { "NSEMAIL_ACTIVATION_BODY", "This email has been sent to verify that this email address belongs to the person who tried to register an account on %1$s. Your cookie is:\n %2$s\nTo verify your email address and complete the account registration, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nThis command is only used once to complete your account registration, and never again. Once you have run this command, you will need to authenticate everytime you reconnect to the network. To do this, you will have to type this command every time you reconnect:\n /msg %3$s@%4$s AUTH %5$s your-password\n Please remember to fill in 'your-password' with the actual password you gave to us when you registered.\n\nIf you did NOT request this account, you do not need to do anything. Please contact the %1$s staff if you have questions, and be sure to check our website." },
293 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
294 { "NSEMAIL_PASSWORD_CHANGE_BODY", "This email has been sent to verify that you wish to change the password on your account %5$s. Your cookie is %2$s.\nTo complete the password change, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request your password to be changed, you do not need to do anything. Please contact the %1$s staff if you have questions." },
295 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
296 { "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." },
297 { "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." },
298 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
299 { "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." },
300 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
301 { "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." },
302 { "CHECKPASS_YES", "Yes." },
303 { "CHECKPASS_NO", "No." },
307 enum reclaim_action {
313 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
314 static void nickserv_reclaim_p(void *data);
317 unsigned int disable_nicks : 1;
318 unsigned int valid_handle_regex_set : 1;
319 unsigned int valid_nick_regex_set : 1;
320 unsigned int autogag_enabled : 1;
321 unsigned int email_enabled : 1;
322 unsigned int email_required : 1;
323 unsigned int default_hostmask : 1;
324 unsigned int warn_nick_owned : 1;
325 unsigned int warn_clone_auth : 1;
326 unsigned long nicks_per_handle;
327 unsigned long password_min_length;
328 unsigned long password_min_digits;
329 unsigned long password_min_upper;
330 unsigned long password_min_lower;
331 unsigned long db_backup_frequency;
332 unsigned long handle_expire_frequency;
333 unsigned long autogag_duration;
334 unsigned long email_visible_level;
335 unsigned long cookie_timeout;
336 unsigned long handle_expire_delay;
337 unsigned long nochan_handle_expire_delay;
338 unsigned long modoper_level;
339 unsigned long set_epithet_level;
340 unsigned long set_title_level;
341 unsigned long set_fakehost_level;
342 unsigned long handles_per_email;
343 unsigned long email_search_level;
344 const char *network_name;
345 const char *titlehost_suffix;
346 regex_t valid_handle_regex;
347 regex_t valid_nick_regex;
348 dict_t weak_password_dict;
349 struct policer_params *auth_policer_params;
350 enum reclaim_action reclaim_action;
351 enum reclaim_action auto_reclaim_action;
352 unsigned long auto_reclaim_delay;
353 unsigned char default_maxlogins;
354 unsigned char hard_maxlogins;
357 /* We have 2^32 unique account IDs to use. */
358 unsigned long int highest_id = 0;
361 canonicalize_hostmask(char *mask)
363 char *out = mask, *temp;
364 if ((temp = strchr(mask, '!'))) {
366 while (*temp) *out++ = *temp++;
372 static struct handle_info *
373 register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
375 struct handle_info *hi;
377 #ifdef WITH_PROTOCOL_BAHAMUT
378 char id_base64[IDLEN + 1];
381 /* Assign a unique account ID to the account; note that 0 is
382 an invalid account ID. 1 is therefore the first account ID. */
384 id = 1 + highest_id++;
386 /* Note: highest_id is and must always be the highest ID. */
387 if(id > highest_id) {
391 inttobase64(id_base64, id, IDLEN);
393 /* Make sure an account with the same ID doesn't exist. If a
394 duplicate is found, log some details and assign a new one.
395 This should be impossible, but it never hurts to expect it. */
396 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
397 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
403 hi = calloc(1, sizeof(*hi));
404 hi->userlist_style = HI_DEFAULT_STYLE;
405 hi->announcements = '?';
406 hi->handle = strdup(handle);
407 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
409 dict_insert(nickserv_handle_dict, hi->handle, hi);
411 #ifdef WITH_PROTOCOL_BAHAMUT
413 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
420 register_nick(const char *nick, struct handle_info *owner)
422 struct nick_info *ni;
423 ni = malloc(sizeof(struct nick_info));
424 safestrncpy(ni->nick, nick, sizeof(ni->nick));
426 ni->next = owner->nicks;
428 dict_insert(nickserv_nick_dict, ni->nick, ni);
432 delete_nick(struct nick_info *ni)
434 struct nick_info *last, *next;
435 struct userNode *user;
436 /* Check to see if we should mark a user as unregistered. */
437 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
438 user->modes &= ~FLAGS_REGNICK;
441 /* Remove ni from the nick_info linked list. */
442 if (ni == ni->owner->nicks) {
443 ni->owner->nicks = ni->next;
445 last = ni->owner->nicks;
451 last->next = next->next;
453 dict_remove(nickserv_nick_dict, ni->nick);
456 static unreg_func_t *unreg_func_list;
457 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
460 reg_unreg_func(unreg_func_t func)
462 if (unreg_func_used == unreg_func_size) {
463 if (unreg_func_size) {
464 unreg_func_size <<= 1;
465 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
468 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
471 unreg_func_list[unreg_func_used++] = func;
475 nickserv_free_cookie(void *data)
477 struct handle_cookie *cookie = data;
478 if (cookie->hi) cookie->hi->cookie = NULL;
479 if (cookie->data) free(cookie->data);
484 free_handle_info(void *vhi)
486 struct handle_info *hi = vhi;
488 #ifdef WITH_PROTOCOL_BAHAMUT
491 inttobase64(id, hi->id, IDLEN);
492 dict_remove(nickserv_id_dict, id);
495 free_string_list(hi->masks);
499 delete_nick(hi->nicks);
504 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
505 nickserv_free_cookie(hi->cookie);
507 if (hi->email_addr) {
508 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
509 handle_info_list_remove(hil, hi);
511 dict_remove(nickserv_email_dict, hi->email_addr);
516 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
519 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
523 for (n=0; n<unreg_func_used; n++)
524 unreg_func_list[n](notify, hi);
526 set_user_handle_info(hi->users, NULL, 0);
528 if (nickserv_conf.disable_nicks)
529 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
531 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
533 dict_remove(nickserv_handle_dict, hi->handle);
537 get_handle_info(const char *handle)
539 return dict_find(nickserv_handle_dict, handle, 0);
543 get_nick_info(const char *nick)
545 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
549 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
554 for (nn=0; nn<channel->members.used; ++nn) {
555 mn = channel->members.list[nn];
556 if ((mn->user != except) && (mn->user->handle_info == handle))
563 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
564 if (!user->handle_info) {
566 send_message(user, bot, "MSG_AUTHENTICATE");
570 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
572 send_message(user, bot, "NSMSG_NO_ACCESS");
576 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
578 send_message(user, bot, "MSG_OPER_SUSPENDED");
582 if (user->handle_info->opserv_level < min_level) {
584 send_message(user, bot, "NSMSG_NO_ACCESS");
592 is_valid_handle(const char *handle)
594 struct userNode *user;
595 /* cant register a juped nick/service nick as handle, to prevent confusion */
596 user = GetUserH(handle);
597 if (user && IsLocal(user))
599 /* check against maximum length */
600 if (strlen(handle) > NICKSERV_HANDLE_LEN)
602 /* for consistency, only allow account names that could be nicks */
603 if (!is_valid_nick(handle))
605 /* disallow account names that look like bad words */
606 if (opserv_bad_channel(handle))
608 /* test either regex or containing all valid chars */
609 if (nickserv_conf.valid_handle_regex_set) {
610 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
613 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
614 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
618 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
623 is_registerable_nick(const char *nick)
625 /* make sure it could be used as an account name */
626 if (!is_valid_handle(nick))
629 if (strlen(nick) > NICKLEN)
631 /* test either regex or as valid handle */
632 if (nickserv_conf.valid_nick_regex_set) {
633 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
636 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
637 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
645 is_valid_email_addr(const char *email)
647 return strchr(email, '@') != NULL;
651 visible_email_addr(struct userNode *user, struct handle_info *hi)
653 if (hi->email_addr) {
654 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
655 return hi->email_addr;
665 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
667 struct handle_info *hi;
668 struct userNode *target;
672 if (!(hi = get_handle_info(++name))) {
673 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
678 if (!(target = GetUserH(name))) {
679 send_message(user, service, "MSG_NICK_UNKNOWN", name);
682 if (IsLocal(target)) {
683 if (IsService(target))
684 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
686 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
689 if (!(hi = target->handle_info)) {
690 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
698 oper_outranks(struct userNode *user, struct handle_info *hi) {
699 if (user->handle_info->opserv_level > hi->opserv_level)
701 if (user->handle_info->opserv_level == hi->opserv_level) {
702 if ((user->handle_info->opserv_level == 1000)
703 || (user->handle_info == hi)
704 || ((user->handle_info->opserv_level == 0)
705 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
706 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
710 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
714 static struct handle_info *
715 get_victim_oper(struct userNode *user, const char *target)
717 struct handle_info *hi;
718 if (!(hi = smart_get_handle_info(nickserv, user, target)))
720 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
721 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
724 return oper_outranks(user, hi) ? hi : NULL;
728 valid_user_for(struct userNode *user, struct handle_info *hi)
732 /* If no hostmasks on the account, allow it. */
733 if (!hi->masks->used)
735 /* If any hostmask matches, allow it. */
736 for (ii=0; ii<hi->masks->used; ii++)
737 if (user_matches_glob(user, hi->masks->list[ii], 0))
739 /* If they are allowauthed to this account, allow it (removing the aa). */
740 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
741 dict_remove(nickserv_allow_auth_dict, user->nick);
744 /* The user is not allowed to use this account. */
749 is_secure_password(const char *handle, const char *pass, struct userNode *user)
752 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
754 if (len < nickserv_conf.password_min_length) {
756 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
759 if (!irccasecmp(pass, handle)) {
761 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
764 dict_find(nickserv_conf.weak_password_dict, pass, &i);
767 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
770 for (i=0; i<len; i++) {
771 if (isdigit(pass[i]))
773 if (isupper(pass[i]))
775 if (islower(pass[i]))
778 if ((cnt_lower < nickserv_conf.password_min_lower)
779 || (cnt_upper < nickserv_conf.password_min_upper)
780 || (cnt_digits < nickserv_conf.password_min_digits)) {
782 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
788 static auth_func_t *auth_func_list;
789 static unsigned int auth_func_size = 0, auth_func_used = 0;
792 reg_auth_func(auth_func_t func)
794 if (auth_func_used == auth_func_size) {
795 if (auth_func_size) {
796 auth_func_size <<= 1;
797 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
800 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
803 auth_func_list[auth_func_used++] = func;
806 static handle_rename_func_t *rf_list;
807 static unsigned int rf_list_size, rf_list_used;
810 reg_handle_rename_func(handle_rename_func_t func)
812 if (rf_list_used == rf_list_size) {
815 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
818 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
821 rf_list[rf_list_used++] = func;
825 generate_fakehost(struct handle_info *handle)
827 extern const char *hidden_host_suffix;
828 static char buffer[HOSTLEN+1];
830 if (!handle->fakehost) {
831 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
833 } else if (handle->fakehost[0] == '.') {
834 /* A leading dot indicates the stored value is actually a title. */
835 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
838 return handle->fakehost;
842 apply_fakehost(struct handle_info *handle)
844 struct userNode *target;
849 fake = generate_fakehost(handle);
850 for (target = handle->users; target; target = target->next_authed)
851 assign_fakehost(target, fake, 1);
855 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
858 struct handle_info *old_info;
860 /* This can happen if somebody uses COOKIE while authed, or if
861 * they re-auth to their current handle (which is silly, but users
863 if (user->handle_info == hi)
866 if (user->handle_info) {
867 struct userNode *other;
870 userList_remove(&curr_helpers, user);
872 /* remove from next_authed linked list */
873 if (user->handle_info->users == user) {
874 user->handle_info->users = user->next_authed;
876 for (other = user->handle_info->users;
877 other->next_authed != user;
878 other = other->next_authed) ;
879 other->next_authed = user->next_authed;
881 /* if nobody left on old handle, and they're not an oper, remove !god */
882 if (!user->handle_info->users && !user->handle_info->opserv_level)
883 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
884 /* record them as being last seen at this time */
885 user->handle_info->lastseen = now;
886 /* and record their hostmask */
887 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
889 old_info = user->handle_info;
890 user->handle_info = hi;
891 if (hi && !hi->users && !hi->opserv_level)
892 HANDLE_CLEAR_FLAG(hi, HELPING);
893 for (n=0; n<auth_func_used; n++)
894 auth_func_list[n](user, old_info);
896 struct nick_info *ni;
898 HANDLE_CLEAR_FLAG(hi, FROZEN);
899 if (nickserv_conf.warn_clone_auth) {
900 struct userNode *other;
901 for (other = hi->users; other; other = other->next_authed)
902 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
904 user->next_authed = hi->users;
908 userList_append(&curr_helpers, user);
910 if (hi->fakehost || old_info)
914 #ifdef WITH_PROTOCOL_BAHAMUT
915 /* Stamp users with their account ID. */
917 inttobase64(id, hi->id, IDLEN);
918 #elif WITH_PROTOCOL_P10
919 /* Stamp users with their account name. */
920 char *id = hi->handle;
922 const char *id = "???";
924 if (!nickserv_conf.disable_nicks) {
925 struct nick_info *ni;
926 for (ni = hi->nicks; ni; ni = ni->next) {
927 if (!irccasecmp(user->nick, ni->nick)) {
928 user->modes |= FLAGS_REGNICK;
936 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
937 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
939 /* We cannot clear the user's account ID, unfortunately. */
940 user->next_authed = NULL;
944 static struct handle_info*
945 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
947 struct handle_info *hi;
948 struct nick_info *ni;
949 char crypted[MD5_CRYPT_LENGTH];
951 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
952 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
956 if (!is_secure_password(handle, passwd, user))
959 cryptpass(passwd, crypted);
960 hi = register_handle(handle, crypted, 0);
961 hi->masks = alloc_string_list(1);
963 hi->language = lang_C;
964 hi->registered = now;
966 hi->flags = HI_DEFAULT_FLAGS;
967 if (settee && !no_auth)
968 set_user_handle_info(settee, hi, 1);
971 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
972 else if (nickserv_conf.disable_nicks)
973 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
974 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
975 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
977 register_nick(user->nick, hi);
978 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
980 if (settee && (user != settee))
981 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
986 nickserv_bake_cookie(struct handle_cookie *cookie)
988 cookie->hi->cookie = cookie;
989 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
993 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
995 struct handle_cookie *cookie;
996 char subject[128], body[4096], *misc;
997 const char *netname, *fmt;
1001 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1005 cookie = calloc(1, sizeof(*cookie));
1007 cookie->type = type;
1008 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1009 cookie->expires = now + nickserv_conf.cookie_timeout;
1010 inttobase64(cookie->cookie, rand(), 5);
1011 inttobase64(cookie->cookie+5, rand(), 5);
1013 netname = nickserv_conf.network_name;
1016 switch (cookie->type) {
1018 hi->passwd[0] = 0; /* invalidate password */
1019 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1020 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1021 snprintf(subject, sizeof(subject), fmt, netname);
1022 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1023 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1026 case PASSWORD_CHANGE:
1027 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1028 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1029 snprintf(subject, sizeof(subject), fmt, netname);
1030 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1031 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1034 misc = hi->email_addr;
1035 hi->email_addr = cookie->data;
1037 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1038 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1039 snprintf(subject, sizeof(subject), fmt, netname);
1040 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1041 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1042 sendmail(nickserv, hi, subject, body, 1);
1043 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1044 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1046 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1047 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1048 snprintf(subject, sizeof(subject), fmt, netname);
1049 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1050 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1051 sendmail(nickserv, hi, subject, body, 1);
1054 hi->email_addr = misc;
1057 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1058 snprintf(subject, sizeof(subject), fmt, netname);
1059 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1060 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1061 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1064 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1068 sendmail(nickserv, hi, subject, body, first_time);
1069 nickserv_bake_cookie(cookie);
1073 nickserv_eat_cookie(struct handle_cookie *cookie)
1075 cookie->hi->cookie = NULL;
1076 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1077 nickserv_free_cookie(cookie);
1081 nickserv_free_email_addr(void *data)
1083 handle_info_list_clean(data);
1088 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1090 struct handle_info_list *hil;
1091 /* Remove from old handle_info_list ... */
1092 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1093 handle_info_list_remove(hil, hi);
1094 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1095 hi->email_addr = NULL;
1097 /* Add to the new list.. */
1098 if (new_email_addr) {
1099 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1100 hil = calloc(1, sizeof(*hil));
1101 hil->tag = strdup(new_email_addr);
1102 handle_info_list_init(hil);
1103 dict_insert(nickserv_email_dict, hil->tag, hil);
1105 handle_info_list_append(hil, hi);
1106 hi->email_addr = hil->tag;
1110 static NICKSERV_FUNC(cmd_register)
1112 struct handle_info *hi;
1113 const char *email_addr, *password;
1116 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1117 /* Require the first handle registered to belong to someone +o. */
1118 reply("NSMSG_REQUIRE_OPER");
1122 if (user->handle_info) {
1123 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1127 if (IsRegistering(user)) {
1128 reply("NSMSG_ALREADY_REGISTERING");
1132 if (IsStamped(user)) {
1133 /* Unauthenticated users might still have been stamped
1134 previously and could therefore have a hidden host;
1135 do not allow them to register a new account. */
1136 reply("NSMSG_STAMPED_REGISTER");
1140 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1142 if (!is_valid_handle(argv[1])) {
1143 reply("NSMSG_BAD_HANDLE", argv[1]);
1147 if ((argc >= 4) && nickserv_conf.email_enabled) {
1148 struct handle_info_list *hil;
1151 /* Remember email address. */
1152 email_addr = argv[3];
1154 /* Check that the email address looks valid.. */
1155 if (!is_valid_email_addr(email_addr)) {
1156 reply("NSMSG_BAD_EMAIL_ADDR");
1160 /* .. and that we are allowed to send to it. */
1161 if ((str = sendmail_prohibited_address(email_addr))) {
1162 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1166 /* If we do email verify, make sure we don't spam the address. */
1167 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1169 for (nn=0; nn<hil->used; nn++) {
1170 if (hil->list[nn]->cookie) {
1171 reply("NSMSG_EMAIL_UNACTIVATED");
1175 if (hil->used >= nickserv_conf.handles_per_email) {
1176 reply("NSMSG_EMAIL_OVERUSED");
1189 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1191 /* Add any masks they should get. */
1192 if (nickserv_conf.default_hostmask) {
1193 string_list_append(hi->masks, strdup("*@*"));
1195 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1196 if (user->ip.s_addr && user->hostname[strspn(user->hostname, "0123456789.")])
1197 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1200 /* If they're the first to register, give them level 1000. */
1201 if (dict_size(nickserv_handle_dict) == 1) {
1202 hi->opserv_level = 1000;
1203 reply("NSMSG_ROOT_HANDLE", argv[1]);
1206 /* Set their email address. */
1208 nickserv_set_email_addr(hi, email_addr);
1210 /* If they need to do email verification, tell them. */
1212 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1214 /* Set registering flag.. */
1215 user->modes |= FLAGS_REGISTERING;
1220 static NICKSERV_FUNC(cmd_oregister)
1223 struct userNode *settee;
1224 struct handle_info *hi;
1226 NICKSERV_MIN_PARMS(4);
1228 if (!is_valid_handle(argv[1])) {
1229 reply("NSMSG_BAD_HANDLE", argv[1]);
1233 if (strchr(argv[3], '@')) {
1234 mask = canonicalize_hostmask(strdup(argv[3]));
1236 settee = GetUserH(argv[4]);
1238 reply("MSG_NICK_UNKNOWN", argv[4]);
1245 } else if ((settee = GetUserH(argv[3]))) {
1246 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1248 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1251 if (settee && settee->handle_info) {
1252 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1256 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1260 string_list_append(hi->masks, mask);
1264 static NICKSERV_FUNC(cmd_handleinfo)
1267 unsigned int i, pos=0, herelen;
1268 struct userNode *target, *next_un;
1269 struct handle_info *hi;
1270 const char *nsmsg_none;
1273 if (!(hi = user->handle_info)) {
1274 reply("NSMSG_MUST_AUTH");
1277 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1281 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1282 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1283 #ifdef WITH_PROTOCOL_BAHAMUT
1284 reply("NSMSG_HANDLEINFO_ID", hi->id);
1286 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1289 intervalString(buff, now - hi->lastseen, user->handle_info);
1290 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1292 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1295 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1296 if (HANDLE_FLAGGED(hi, FROZEN))
1297 reply("NSMSG_HANDLEINFO_VACATION");
1299 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1300 struct do_not_register *dnr;
1301 if ((dnr = chanserv_is_dnr(NULL, hi)))
1302 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1303 if (!oper_outranks(user, hi))
1305 } else if (hi != user->handle_info)
1308 if (nickserv_conf.email_enabled)
1309 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1313 switch (hi->cookie->type) {
1314 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1315 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1316 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1317 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1318 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1324 unsigned long flen = 1;
1325 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1327 for (i=0, flen=1; handle_flags[i]; i++)
1328 if (hi->flags & 1 << i)
1329 flags[flen++] = handle_flags[i];
1331 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1333 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1336 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1337 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1338 || (hi->opserv_level > 0)) {
1339 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1343 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1345 if (hi->last_quit_host[0])
1346 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1348 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1350 if (nickserv_conf.disable_nicks) {
1351 /* nicks disabled; don't show anything about registered nicks */
1352 } else if (hi->nicks) {
1353 struct nick_info *ni, *next_ni;
1354 for (ni = hi->nicks; ni; ni = next_ni) {
1355 herelen = strlen(ni->nick);
1356 if (pos + herelen + 1 > ArrayLength(buff)) {
1358 goto print_nicks_buff;
1362 memcpy(buff+pos, ni->nick, herelen);
1363 pos += herelen; buff[pos++] = ' ';
1367 reply("NSMSG_HANDLEINFO_NICKS", buff);
1372 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1375 if (hi->masks->used) {
1376 for (i=0; i < hi->masks->used; i++) {
1377 herelen = strlen(hi->masks->list[i]);
1378 if (pos + herelen + 1 > ArrayLength(buff)) {
1380 goto print_mask_buff;
1382 memcpy(buff+pos, hi->masks->list[i], herelen);
1383 pos += herelen; buff[pos++] = ' ';
1384 if (i+1 == hi->masks->used) {
1387 reply("NSMSG_HANDLEINFO_MASKS", buff);
1392 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1396 struct userData *channel, *next;
1399 for (channel = hi->channels; channel; channel = next) {
1400 next = channel->u_next;
1401 name = channel->channel->channel->name;
1402 herelen = strlen(name);
1403 if (pos + herelen + 7 > ArrayLength(buff)) {
1405 goto print_chans_buff;
1407 if (IsUserSuspended(channel))
1409 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1413 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1418 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1421 for (target = hi->users; target; target = next_un) {
1422 herelen = strlen(target->nick);
1423 if (pos + herelen + 1 > ArrayLength(buff)) {
1425 goto print_cnick_buff;
1427 next_un = target->next_authed;
1429 memcpy(buff+pos, target->nick, herelen);
1430 pos += herelen; buff[pos++] = ' ';
1434 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1442 static NICKSERV_FUNC(cmd_userinfo)
1444 struct userNode *target;
1446 NICKSERV_MIN_PARMS(2);
1447 if (!(target = GetUserH(argv[1]))) {
1448 reply("MSG_NICK_UNKNOWN", argv[1]);
1451 if (target->handle_info)
1452 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1454 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1458 static NICKSERV_FUNC(cmd_nickinfo)
1460 struct nick_info *ni;
1462 NICKSERV_MIN_PARMS(2);
1463 if (!(ni = get_nick_info(argv[1]))) {
1464 reply("MSG_NICK_UNKNOWN", argv[1]);
1467 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1471 static NICKSERV_FUNC(cmd_rename_handle)
1473 struct handle_info *hi;
1474 char msgbuf[MAXLEN], *old_handle;
1477 NICKSERV_MIN_PARMS(3);
1478 if (!(hi = get_victim_oper(user, argv[1])))
1480 if (!is_valid_handle(argv[2])) {
1481 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1484 if (get_handle_info(argv[2])) {
1485 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1489 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1490 hi->handle = strdup(argv[2]);
1491 dict_insert(nickserv_handle_dict, hi->handle, hi);
1492 for (nn=0; nn<rf_list_used; nn++)
1493 rf_list[nn](hi, old_handle);
1494 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1495 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1496 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1501 static failpw_func_t *failpw_func_list;
1502 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1505 reg_failpw_func(failpw_func_t func)
1507 if (failpw_func_used == failpw_func_size) {
1508 if (failpw_func_size) {
1509 failpw_func_size <<= 1;
1510 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1512 failpw_func_size = 8;
1513 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1516 failpw_func_list[failpw_func_used++] = func;
1519 static NICKSERV_FUNC(cmd_auth)
1521 int pw_arg, used, maxlogins;
1522 struct handle_info *hi;
1524 struct userNode *other;
1526 if (user->handle_info) {
1527 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1530 if (IsStamped(user)) {
1531 /* Unauthenticated users might still have been stamped
1532 previously and could therefore have a hidden host;
1533 do not allow them to authenticate. */
1534 reply("NSMSG_STAMPED_AUTH");
1538 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1540 } else if (argc == 2) {
1541 if (nickserv_conf.disable_nicks) {
1542 if (!(hi = get_handle_info(user->nick))) {
1543 reply("NSMSG_HANDLE_NOT_FOUND");
1547 /* try to look up their handle from their nick */
1548 struct nick_info *ni;
1549 ni = get_nick_info(user->nick);
1551 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1558 reply("MSG_MISSING_PARAMS", argv[0]);
1559 svccmd_send_help(user, nickserv, cmd);
1563 reply("NSMSG_HANDLE_NOT_FOUND");
1566 /* Responses from here on look up the language used by the handle they asked about. */
1567 passwd = argv[pw_arg];
1568 if (!valid_user_for(user, hi)) {
1569 if (hi->email_addr && nickserv_conf.email_enabled)
1570 send_message_type(4, user, cmd->parent->bot,
1571 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1574 send_message_type(4, user, cmd->parent->bot,
1575 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1577 argv[pw_arg] = "BADMASK";
1580 if (!checkpass(passwd, hi->passwd)) {
1582 send_message_type(4, user, cmd->parent->bot,
1583 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1584 argv[pw_arg] = "BADPASS";
1585 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1586 if (nickserv_conf.autogag_enabled) {
1587 if (!user->auth_policer.params) {
1588 user->auth_policer.last_req = now;
1589 user->auth_policer.params = nickserv_conf.auth_policer_params;
1591 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1593 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1594 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1595 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1597 argv[pw_arg] = "GAGGED";
1602 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1603 send_message_type(4, user, cmd->parent->bot,
1604 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1605 argv[pw_arg] = "SUSPENDED";
1608 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1609 for (used = 0, other = hi->users; other; other = other->next_authed) {
1610 if (++used >= maxlogins) {
1611 send_message_type(4, user, cmd->parent->bot,
1612 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1614 argv[pw_arg] = "MAXLOGINS";
1619 set_user_handle_info(user, hi, 1);
1620 if (nickserv_conf.email_required && !hi->email_addr)
1621 reply("NSMSG_PLEASE_SET_EMAIL");
1622 if (!is_secure_password(hi->handle, passwd, NULL))
1623 reply("NSMSG_WEAK_PASSWORD");
1624 if (hi->passwd[0] != '$')
1625 cryptpass(passwd, hi->passwd);
1626 reply("NSMSG_AUTH_SUCCESS");
1627 argv[pw_arg] = "****";
1631 static allowauth_func_t *allowauth_func_list;
1632 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1635 reg_allowauth_func(allowauth_func_t func)
1637 if (allowauth_func_used == allowauth_func_size) {
1638 if (allowauth_func_size) {
1639 allowauth_func_size <<= 1;
1640 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1642 allowauth_func_size = 8;
1643 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1646 allowauth_func_list[allowauth_func_used++] = func;
1649 static NICKSERV_FUNC(cmd_allowauth)
1651 struct userNode *target;
1652 struct handle_info *hi;
1655 NICKSERV_MIN_PARMS(2);
1656 if (!(target = GetUserH(argv[1]))) {
1657 reply("MSG_NICK_UNKNOWN", argv[1]);
1660 if (target->handle_info) {
1661 reply("NSMSG_USER_PREV_AUTH", target->nick);
1664 if (IsStamped(target)) {
1665 /* Unauthenticated users might still have been stamped
1666 previously and could therefore have a hidden host;
1667 do not allow them to authenticate to an account. */
1668 reply("NSMSG_USER_PREV_STAMP", target->nick);
1673 else if (!(hi = get_handle_info(argv[2]))) {
1674 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1678 if (hi->opserv_level > user->handle_info->opserv_level) {
1679 reply("MSG_USER_OUTRANKED", hi->handle);
1682 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1683 || (hi->opserv_level > 0))
1684 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1685 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1688 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1689 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1690 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1691 if (nickserv_conf.email_enabled)
1692 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1694 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1695 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1697 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1699 for (n=0; n<allowauth_func_used; n++)
1700 allowauth_func_list[n](user, target, hi);
1704 static NICKSERV_FUNC(cmd_authcookie)
1706 struct handle_info *hi;
1708 NICKSERV_MIN_PARMS(2);
1709 if (user->handle_info) {
1710 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1713 if (IsStamped(user)) {
1714 /* Unauthenticated users might still have been stamped
1715 previously and could therefore have a hidden host;
1716 do not allow them to authenticate to an account. */
1717 reply("NSMSG_STAMPED_AUTHCOOKIE");
1720 if (!(hi = get_handle_info(argv[1]))) {
1721 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1724 if (!hi->email_addr) {
1725 reply("MSG_SET_EMAIL_ADDR");
1728 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1732 static NICKSERV_FUNC(cmd_delcookie)
1734 struct handle_info *hi;
1736 hi = user->handle_info;
1738 reply("NSMSG_NO_COOKIE");
1741 switch (hi->cookie->type) {
1744 reply("NSMSG_MUST_TIME_OUT");
1747 nickserv_eat_cookie(hi->cookie);
1748 reply("NSMSG_ATE_COOKIE");
1754 static NICKSERV_FUNC(cmd_resetpass)
1756 struct handle_info *hi;
1757 char crypted[MD5_CRYPT_LENGTH];
1759 NICKSERV_MIN_PARMS(3);
1760 if (user->handle_info) {
1761 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1764 if (IsStamped(user)) {
1765 /* Unauthenticated users might still have been stamped
1766 previously and could therefore have a hidden host;
1767 do not allow them to activate an account. */
1768 reply("NSMSG_STAMPED_RESETPASS");
1771 if (!(hi = get_handle_info(argv[1]))) {
1772 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1775 if (!hi->email_addr) {
1776 reply("MSG_SET_EMAIL_ADDR");
1779 cryptpass(argv[2], crypted);
1781 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1785 static NICKSERV_FUNC(cmd_cookie)
1787 struct handle_info *hi;
1790 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1793 NICKSERV_MIN_PARMS(3);
1794 if (!(hi = get_handle_info(argv[1]))) {
1795 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1801 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1802 reply("NSMSG_HANDLE_SUSPENDED");
1807 reply("NSMSG_NO_COOKIE");
1811 /* Check validity of operation before comparing cookie to
1812 * prohibit guessing by authed users. */
1813 if (user->handle_info
1814 && (hi->cookie->type != EMAIL_CHANGE)
1815 && (hi->cookie->type != PASSWORD_CHANGE)) {
1816 reply("NSMSG_CANNOT_COOKIE");
1820 if (strcmp(cookie, hi->cookie->cookie)) {
1821 reply("NSMSG_BAD_COOKIE");
1825 switch (hi->cookie->type) {
1827 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1828 set_user_handle_info(user, hi, 1);
1829 reply("NSMSG_HANDLE_ACTIVATED");
1831 case PASSWORD_CHANGE:
1832 set_user_handle_info(user, hi, 1);
1833 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1834 reply("NSMSG_PASSWORD_CHANGED");
1837 nickserv_set_email_addr(hi, hi->cookie->data);
1838 reply("NSMSG_EMAIL_CHANGED");
1841 set_user_handle_info(user, hi, 1);
1842 reply("NSMSG_AUTH_SUCCESS");
1845 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1846 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1850 nickserv_eat_cookie(hi->cookie);
1855 static NICKSERV_FUNC(cmd_oregnick) {
1857 struct handle_info *target;
1858 struct nick_info *ni;
1860 NICKSERV_MIN_PARMS(3);
1861 if (!(target = modcmd_get_handle_info(user, argv[1])))
1864 if (!is_registerable_nick(nick)) {
1865 reply("NSMSG_BAD_NICK", nick);
1868 ni = dict_find(nickserv_nick_dict, nick, NULL);
1870 reply("NSMSG_NICK_EXISTS", nick);
1873 register_nick(nick, target);
1874 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1878 static NICKSERV_FUNC(cmd_regnick) {
1880 struct nick_info *ni;
1882 if (!is_registerable_nick(user->nick)) {
1883 reply("NSMSG_BAD_NICK", user->nick);
1886 /* count their nicks, see if it's too many */
1887 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1888 if (n >= nickserv_conf.nicks_per_handle) {
1889 reply("NSMSG_TOO_MANY_NICKS");
1892 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1894 reply("NSMSG_NICK_EXISTS", user->nick);
1897 register_nick(user->nick, user->handle_info);
1898 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1902 static NICKSERV_FUNC(cmd_pass)
1904 struct handle_info *hi;
1905 const char *old_pass, *new_pass;
1907 NICKSERV_MIN_PARMS(3);
1908 hi = user->handle_info;
1912 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1913 if (!checkpass(old_pass, hi->passwd)) {
1914 argv[1] = "BADPASS";
1915 reply("NSMSG_PASSWORD_INVALID");
1918 cryptpass(new_pass, hi->passwd);
1920 reply("NSMSG_PASS_SUCCESS");
1925 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1928 char *new_mask = canonicalize_hostmask(strdup(mask));
1929 for (i=0; i<hi->masks->used; i++) {
1930 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1931 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1936 string_list_append(hi->masks, new_mask);
1937 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
1941 static NICKSERV_FUNC(cmd_addmask)
1944 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1945 int res = nickserv_addmask(user, user->handle_info, mask);
1949 if (!is_gline(argv[1])) {
1950 reply("NSMSG_MASK_INVALID", argv[1]);
1953 return nickserv_addmask(user, user->handle_info, argv[1]);
1957 static NICKSERV_FUNC(cmd_oaddmask)
1959 struct handle_info *hi;
1961 NICKSERV_MIN_PARMS(3);
1962 if (!(hi = get_victim_oper(user, argv[1])))
1964 return nickserv_addmask(user, hi, argv[2]);
1968 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
1971 for (i=0; i<hi->masks->used; i++) {
1972 if (!strcmp(del_mask, hi->masks->list[i])) {
1973 char *old_mask = hi->masks->list[i];
1974 if (hi->masks->used == 1) {
1975 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
1978 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
1979 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
1984 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
1988 static NICKSERV_FUNC(cmd_delmask)
1990 NICKSERV_MIN_PARMS(2);
1991 return nickserv_delmask(user, user->handle_info, argv[1]);
1994 static NICKSERV_FUNC(cmd_odelmask)
1996 struct handle_info *hi;
1997 NICKSERV_MIN_PARMS(3);
1998 if (!(hi = get_victim_oper(user, argv[1])))
2000 return nickserv_delmask(user, hi, argv[2]);
2004 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2005 unsigned int nn, add = 1, pos;
2006 unsigned long added, removed, flag;
2008 for (added=removed=nn=0; str[nn]; nn++) {
2010 case '+': add = 1; break;
2011 case '-': add = 0; break;
2013 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2014 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2017 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2018 /* cheesy avoidance of looking up the flag name.. */
2019 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2022 flag = 1 << (pos - 1);
2024 added |= flag, removed &= ~flag;
2026 removed |= flag, added &= ~flag;
2031 *premoved = removed;
2036 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2038 unsigned long before, after, added, removed;
2039 struct userNode *uNode;
2041 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2042 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2044 hi->flags = (hi->flags | added) & ~removed;
2045 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2047 /* Strip helping flag if they're only a support helper and not
2048 * currently in #support. */
2049 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2050 struct channelList *schannels;
2052 schannels = chanserv_support_channels();
2053 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2054 for (ii = 0; ii < schannels->used; ++ii)
2055 if (GetUserMode(schannels->list[ii], uNode))
2057 if (ii < schannels->used)
2061 HANDLE_CLEAR_FLAG(hi, HELPING);
2064 if (after && !before) {
2065 /* Add user to current helper list. */
2066 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2067 userList_append(&curr_helpers, uNode);
2068 } else if (!after && before) {
2069 /* Remove user from current helper list. */
2070 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2071 userList_remove(&curr_helpers, uNode);
2078 set_list(struct userNode *user, struct handle_info *hi, int override)
2082 char *set_display[] = {
2083 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2084 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2087 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2089 /* Do this so options are presented in a consistent order. */
2090 for (i = 0; i < ArrayLength(set_display); ++i)
2091 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2092 opt(user, hi, override, 0, NULL);
2095 static NICKSERV_FUNC(cmd_set)
2097 struct handle_info *hi;
2100 hi = user->handle_info;
2102 set_list(user, hi, 0);
2105 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2106 reply("NSMSG_INVALID_OPTION", argv[1]);
2109 return opt(user, hi, 0, argc-1, argv+1);
2112 static NICKSERV_FUNC(cmd_oset)
2114 struct handle_info *hi;
2117 NICKSERV_MIN_PARMS(2);
2119 if (!(hi = get_victim_oper(user, argv[1])))
2123 set_list(user, hi, 0);
2127 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2128 reply("NSMSG_INVALID_OPTION", argv[2]);
2132 return opt(user, hi, 1, argc-2, argv+2);
2135 static OPTION_FUNC(opt_info)
2139 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2141 hi->infoline = NULL;
2143 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2147 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2148 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2152 static OPTION_FUNC(opt_width)
2155 hi->screen_width = strtoul(argv[1], NULL, 0);
2157 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2158 hi->screen_width = MIN_LINE_SIZE;
2159 else if (hi->screen_width > MAX_LINE_SIZE)
2160 hi->screen_width = MAX_LINE_SIZE;
2162 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2166 static OPTION_FUNC(opt_tablewidth)
2169 hi->table_width = strtoul(argv[1], NULL, 0);
2171 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2172 hi->table_width = MIN_LINE_SIZE;
2173 else if (hi->screen_width > MAX_LINE_SIZE)
2174 hi->table_width = MAX_LINE_SIZE;
2176 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2180 static OPTION_FUNC(opt_color)
2183 if (enabled_string(argv[1]))
2184 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2185 else if (disabled_string(argv[1]))
2186 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2188 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2193 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2197 static OPTION_FUNC(opt_privmsg)
2200 if (enabled_string(argv[1]))
2201 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2202 else if (disabled_string(argv[1]))
2203 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2205 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2210 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2214 static OPTION_FUNC(opt_style)
2219 if (!irccasecmp(argv[1], "Zoot"))
2220 hi->userlist_style = HI_STYLE_ZOOT;
2221 else if (!irccasecmp(argv[1], "def"))
2222 hi->userlist_style = HI_STYLE_DEF;
2225 switch (hi->userlist_style) {
2234 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2238 static OPTION_FUNC(opt_announcements)
2243 if (enabled_string(argv[1]))
2244 hi->announcements = 'y';
2245 else if (disabled_string(argv[1]))
2246 hi->announcements = 'n';
2247 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2248 hi->announcements = '?';
2250 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2255 switch (hi->announcements) {
2256 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2257 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2258 case '?': choice = "default"; break;
2259 default: choice = "unknown"; break;
2261 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2265 static OPTION_FUNC(opt_password)
2268 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2273 cryptpass(argv[1], hi->passwd);
2275 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2279 static OPTION_FUNC(opt_flags)
2282 unsigned int ii, flen;
2285 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2290 nickserv_apply_flags(user, hi, argv[1]);
2292 for (ii = flen = 0; handle_flags[ii]; ii++)
2293 if (hi->flags & (1 << ii))
2294 flags[flen++] = handle_flags[ii];
2297 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2299 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2303 static OPTION_FUNC(opt_email)
2307 if (!is_valid_email_addr(argv[1])) {
2308 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2311 if ((str = sendmail_prohibited_address(argv[1]))) {
2312 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2315 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2316 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2318 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2320 nickserv_set_email_addr(hi, argv[1]);
2322 nickserv_eat_cookie(hi->cookie);
2323 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2326 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2330 static OPTION_FUNC(opt_maxlogins)
2332 unsigned char maxlogins;
2334 maxlogins = strtoul(argv[1], NULL, 0);
2335 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2336 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2339 hi->maxlogins = maxlogins;
2341 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2342 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2346 static OPTION_FUNC(opt_language)
2348 struct language *lang;
2350 lang = language_find(argv[1]);
2351 if (irccasecmp(lang->name, argv[1]))
2352 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2353 hi->language = lang;
2355 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2360 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2361 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2363 if ((user->handle_info->opserv_level < target->opserv_level)
2364 || ((user->handle_info->opserv_level == target->opserv_level)
2365 && (user->handle_info->opserv_level < 1000))) {
2366 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2369 if ((user->handle_info->opserv_level < new_level)
2370 || ((user->handle_info->opserv_level == new_level)
2371 && (user->handle_info->opserv_level < 1000))) {
2372 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2375 if (user->handle_info == target) {
2376 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2379 if (target->opserv_level == new_level)
2381 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2382 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2383 target->opserv_level = new_level;
2387 static OPTION_FUNC(opt_level)
2392 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2396 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2397 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2401 static OPTION_FUNC(opt_epithet)
2404 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2408 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2409 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2412 if ((epithet[0] == '*') && !epithet[1])
2415 hi->epithet = strdup(epithet);
2419 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2421 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2425 static OPTION_FUNC(opt_title)
2430 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2434 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2436 if (strchr(title, '.')) {
2437 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2440 if ((strlen(user->handle_info->handle) + strlen(title) +
2441 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2442 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2447 if (!strcmp(title, "*")) {
2448 hi->fakehost = NULL;
2450 hi->fakehost = malloc(strlen(title)+2);
2451 hi->fakehost[0] = '.';
2452 strcpy(hi->fakehost+1, title);
2455 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2456 title = hi->fakehost + 1;
2460 title = user_find_message(user, "MSG_NONE");
2461 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2465 static OPTION_FUNC(opt_fakehost)
2470 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2474 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2476 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2477 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2481 if (!strcmp(fake, "*"))
2482 hi->fakehost = NULL;
2484 hi->fakehost = strdup(fake);
2485 fake = hi->fakehost;
2488 fake = generate_fakehost(hi);
2490 fake = user_find_message(user, "MSG_NONE");
2491 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2495 static NICKSERV_FUNC(cmd_reclaim)
2497 struct handle_info *hi;
2498 struct nick_info *ni;
2499 struct userNode *victim;
2501 NICKSERV_MIN_PARMS(2);
2502 hi = user->handle_info;
2503 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2505 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2508 if (ni->owner != user->handle_info) {
2509 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2512 victim = GetUserH(ni->nick);
2514 reply("MSG_NICK_UNKNOWN", ni->nick);
2517 if (victim == user) {
2518 reply("NSMSG_NICK_USER_YOU");
2521 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2522 switch (nickserv_conf.reclaim_action) {
2523 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2524 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2525 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2526 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2531 static NICKSERV_FUNC(cmd_unregnick)
2534 struct handle_info *hi;
2535 struct nick_info *ni;
2537 hi = user->handle_info;
2538 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2539 ni = dict_find(nickserv_nick_dict, nick, NULL);
2541 reply("NSMSG_UNKNOWN_NICK", nick);
2544 if (hi != ni->owner) {
2545 reply("NSMSG_NOT_YOUR_NICK", nick);
2548 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2553 static NICKSERV_FUNC(cmd_ounregnick)
2555 struct nick_info *ni;
2557 NICKSERV_MIN_PARMS(2);
2558 if (!(ni = get_nick_info(argv[1]))) {
2559 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2562 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2563 reply("MSG_USER_OUTRANKED", ni->nick);
2566 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2571 static NICKSERV_FUNC(cmd_unregister)
2573 struct handle_info *hi;
2576 NICKSERV_MIN_PARMS(2);
2577 hi = user->handle_info;
2580 if (checkpass(passwd, hi->passwd)) {
2581 nickserv_unregister_handle(hi, user);
2584 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2585 reply("NSMSG_PASSWORD_INVALID");
2590 static NICKSERV_FUNC(cmd_ounregister)
2592 struct handle_info *hi;
2594 NICKSERV_MIN_PARMS(2);
2595 if (!(hi = get_victim_oper(user, argv[1])))
2597 nickserv_unregister_handle(hi, user);
2601 static NICKSERV_FUNC(cmd_status)
2603 if (nickserv_conf.disable_nicks) {
2604 reply("NSMSG_GLOBAL_STATS_NONICK",
2605 dict_size(nickserv_handle_dict));
2607 if (user->handle_info) {
2609 struct nick_info *ni;
2610 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2611 reply("NSMSG_HANDLE_STATS", cnt);
2613 reply("NSMSG_HANDLE_NONE");
2615 reply("NSMSG_GLOBAL_STATS",
2616 dict_size(nickserv_handle_dict),
2617 dict_size(nickserv_nick_dict));
2622 static NICKSERV_FUNC(cmd_ghost)
2624 struct userNode *target;
2625 char reason[MAXLEN];
2627 NICKSERV_MIN_PARMS(2);
2628 if (!(target = GetUserH(argv[1]))) {
2629 reply("MSG_NICK_UNKNOWN", argv[1]);
2632 if (target == user) {
2633 reply("NSMSG_CANNOT_GHOST_SELF");
2636 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2637 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2640 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2641 DelUser(target, nickserv, 1, reason);
2642 reply("NSMSG_GHOST_KILLED", argv[1]);
2646 static NICKSERV_FUNC(cmd_vacation)
2648 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2649 reply("NSMSG_ON_VACATION");
2654 nickserv_saxdb_write(struct saxdb_context *ctx) {
2656 struct handle_info *hi;
2659 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2661 #ifdef WITH_PROTOCOL_BAHAMUT
2664 saxdb_start_record(ctx, iter_key(it), 0);
2665 if (hi->announcements != '?') {
2666 flags[0] = hi->announcements;
2668 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2671 struct handle_cookie *cookie = hi->cookie;
2674 switch (cookie->type) {
2675 case ACTIVATION: type = KEY_ACTIVATION; break;
2676 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2677 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2678 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2679 default: type = NULL; break;
2682 saxdb_start_record(ctx, KEY_COOKIE, 0);
2683 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2684 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2686 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2687 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2688 saxdb_end_record(ctx);
2692 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2694 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2696 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2700 for (ii=flen=0; handle_flags[ii]; ++ii)
2701 if (hi->flags & (1 << ii))
2702 flags[flen++] = handle_flags[ii];
2704 saxdb_write_string(ctx, KEY_FLAGS, flags);
2706 #ifdef WITH_PROTOCOL_BAHAMUT
2707 saxdb_write_int(ctx, KEY_ID, hi->id);
2710 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2711 if (hi->last_quit_host[0])
2712 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2713 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2714 if (hi->masks->used)
2715 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2717 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2719 struct string_list *slist;
2720 struct nick_info *ni;
2722 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2723 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2724 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2728 if (hi->opserv_level)
2729 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2730 if (hi->language != lang_C)
2731 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2732 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2733 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2734 if (hi->screen_width)
2735 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2736 if (hi->table_width)
2737 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2738 flags[0] = hi->userlist_style;
2740 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2741 saxdb_end_record(ctx);
2746 static handle_merge_func_t *handle_merge_func_list;
2747 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2750 reg_handle_merge_func(handle_merge_func_t func)
2752 if (handle_merge_func_used == handle_merge_func_size) {
2753 if (handle_merge_func_size) {
2754 handle_merge_func_size <<= 1;
2755 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2757 handle_merge_func_size = 8;
2758 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2761 handle_merge_func_list[handle_merge_func_used++] = func;
2764 static NICKSERV_FUNC(cmd_merge)
2766 struct handle_info *hi_from, *hi_to;
2767 struct userNode *last_user;
2768 struct userData *cList, *cListNext;
2769 unsigned int ii, jj, n;
2770 char buffer[MAXLEN];
2772 NICKSERV_MIN_PARMS(3);
2774 if (!(hi_from = get_victim_oper(user, argv[1])))
2776 if (!(hi_to = get_victim_oper(user, argv[2])))
2778 if (hi_to == hi_from) {
2779 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2783 for (n=0; n<handle_merge_func_used; n++)
2784 handle_merge_func_list[n](user, hi_to, hi_from);
2786 /* Append "from" handle's nicks to "to" handle's nick list. */
2788 struct nick_info *last_ni;
2789 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2790 last_ni->next = hi_from->nicks;
2792 while (hi_from->nicks) {
2793 hi_from->nicks->owner = hi_to;
2794 hi_from->nicks = hi_from->nicks->next;
2797 /* Merge the hostmasks. */
2798 for (ii=0; ii<hi_from->masks->used; ii++) {
2799 char *mask = hi_from->masks->list[ii];
2800 for (jj=0; jj<hi_to->masks->used; jj++)
2801 if (match_ircglobs(hi_to->masks->list[jj], mask))
2803 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2804 string_list_append(hi_to->masks, strdup(mask));
2807 /* Merge the lists of authed users. */
2809 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2810 last_user->next_authed = hi_from->users;
2812 hi_to->users = hi_from->users;
2814 /* Repoint the old "from" handle's users. */
2815 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2816 last_user->handle_info = hi_to;
2818 hi_from->users = NULL;
2820 /* Merge channel userlists. */
2821 for (cList=hi_from->channels; cList; cList=cListNext) {
2822 struct userData *cList2;
2823 cListNext = cList->u_next;
2824 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2825 if (cList->channel == cList2->channel)
2827 if (cList2 && (cList2->access >= cList->access)) {
2828 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);
2829 /* keep cList2 in hi_to; remove cList from hi_from */
2830 del_channel_user(cList, 1);
2833 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);
2834 /* remove the lower-ranking cList2 from hi_to */
2835 del_channel_user(cList2, 1);
2837 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
2839 /* cList needs to be moved from hi_from to hi_to */
2840 cList->handle = hi_to;
2841 /* Remove from linked list for hi_from */
2842 assert(!cList->u_prev);
2843 hi_from->channels = cList->u_next;
2845 cList->u_next->u_prev = cList->u_prev;
2846 /* Add to linked list for hi_to */
2847 cList->u_prev = NULL;
2848 cList->u_next = hi_to->channels;
2849 if (hi_to->channels)
2850 hi_to->channels->u_prev = cList;
2851 hi_to->channels = cList;
2855 /* Do they get an OpServ level promotion? */
2856 if (hi_from->opserv_level > hi_to->opserv_level)
2857 hi_to->opserv_level = hi_from->opserv_level;
2859 /* What about last seen time? */
2860 if (hi_from->lastseen > hi_to->lastseen)
2861 hi_to->lastseen = hi_from->lastseen;
2863 /* Notify of success. */
2864 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2865 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2866 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2868 /* Unregister the "from" handle. */
2869 nickserv_unregister_handle(hi_from, NULL);
2874 struct nickserv_discrim {
2875 unsigned int limit, min_level, max_level;
2876 unsigned long flags_on, flags_off;
2877 time_t min_registered, max_registered;
2879 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2880 const char *nickmask;
2881 const char *hostmask;
2882 const char *handlemask;
2883 const char *emailmask;
2886 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2888 struct discrim_apply_info {
2889 struct nickserv_discrim *discrim;
2890 discrim_search_func func;
2891 struct userNode *source;
2892 unsigned int matched;
2895 static struct nickserv_discrim *
2896 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2899 struct nickserv_discrim *discrim;
2901 discrim = malloc(sizeof(*discrim));
2902 memset(discrim, 0, sizeof(*discrim));
2903 discrim->min_level = 0;
2904 discrim->max_level = ~0;
2905 discrim->limit = 50;
2906 discrim->min_registered = 0;
2907 discrim->max_registered = INT_MAX;
2908 discrim->lastseen = now;
2910 for (i=0; i<argc; i++) {
2911 if (i == argc - 1) {
2912 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2915 if (!irccasecmp(argv[i], "limit")) {
2916 discrim->limit = strtoul(argv[++i], NULL, 0);
2917 } else if (!irccasecmp(argv[i], "flags")) {
2918 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2919 } else if (!irccasecmp(argv[i], "registered")) {
2920 const char *cmp = argv[++i];
2921 if (cmp[0] == '<') {
2922 if (cmp[1] == '=') {
2923 discrim->min_registered = now - ParseInterval(cmp+2);
2925 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2927 } else if (cmp[0] == '=') {
2928 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2929 } else if (cmp[0] == '>') {
2930 if (cmp[1] == '=') {
2931 discrim->max_registered = now - ParseInterval(cmp+2);
2933 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2936 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2938 } else if (!irccasecmp(argv[i], "seen")) {
2939 discrim->lastseen = now - ParseInterval(argv[++i]);
2940 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2941 discrim->nickmask = argv[++i];
2942 } else if (!irccasecmp(argv[i], "hostmask")) {
2944 if (!irccasecmp(argv[i], "exact")) {
2945 if (i == argc - 1) {
2946 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2949 discrim->hostmask_type = EXACT;
2950 } else if (!irccasecmp(argv[i], "subset")) {
2951 if (i == argc - 1) {
2952 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2955 discrim->hostmask_type = SUBSET;
2956 } else if (!irccasecmp(argv[i], "superset")) {
2957 if (i == argc - 1) {
2958 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2961 discrim->hostmask_type = SUPERSET;
2962 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2963 if (i == argc - 1) {
2964 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2967 discrim->hostmask_type = LASTQUIT;
2970 discrim->hostmask_type = SUPERSET;
2972 discrim->hostmask = argv[++i];
2973 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2974 if (!irccasecmp(argv[++i], "*")) {
2975 discrim->handlemask = 0;
2977 discrim->handlemask = argv[i];
2979 } else if (!irccasecmp(argv[i], "email")) {
2980 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2981 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2983 } else if (!irccasecmp(argv[++i], "*")) {
2984 discrim->emailmask = 0;
2986 discrim->emailmask = argv[i];
2988 } else if (!irccasecmp(argv[i], "access")) {
2989 const char *cmp = argv[++i];
2990 if (cmp[0] == '<') {
2991 if (discrim->min_level == 0) discrim->min_level = 1;
2992 if (cmp[1] == '=') {
2993 discrim->max_level = strtoul(cmp+2, NULL, 0);
2995 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
2997 } else if (cmp[0] == '=') {
2998 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
2999 } else if (cmp[0] == '>') {
3000 if (cmp[1] == '=') {
3001 discrim->min_level = strtoul(cmp+2, NULL, 0);
3003 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3006 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3009 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3020 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3022 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3023 || (discrim->flags_off & hi->flags)
3024 || (discrim->min_registered > hi->registered)
3025 || (discrim->max_registered < hi->registered)
3026 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3027 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3028 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3029 || (discrim->min_level > hi->opserv_level)
3030 || (discrim->max_level < hi->opserv_level)) {
3033 if (discrim->hostmask) {
3035 for (i=0; i<hi->masks->used; i++) {
3036 const char *mask = hi->masks->list[i];
3037 if ((discrim->hostmask_type == SUBSET)
3038 && (match_ircglobs(discrim->hostmask, mask))) break;
3039 else if ((discrim->hostmask_type == EXACT)
3040 && !irccasecmp(discrim->hostmask, mask)) break;
3041 else if ((discrim->hostmask_type == SUPERSET)
3042 && (match_ircglobs(mask, discrim->hostmask))) break;
3043 else if ((discrim->hostmask_type == LASTQUIT)
3044 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3046 if (i==hi->masks->used) return 0;
3048 if (discrim->nickmask) {
3049 struct nick_info *nick = hi->nicks;
3051 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3054 if (!nick) return 0;
3060 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3062 dict_iterator_t it, next;
3063 unsigned int matched;
3065 for (it = dict_first(nickserv_handle_dict), matched = 0;
3066 it && (matched < discrim->limit);
3068 next = iter_next(it);
3069 if (nickserv_discrim_match(discrim, iter_data(it))) {
3070 dsf(source, iter_data(it));
3078 search_print_func(struct userNode *source, struct handle_info *match)
3080 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3084 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3089 search_unregister_func (struct userNode *source, struct handle_info *match)
3091 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3092 nickserv_unregister_handle(match, source);
3096 nickserv_sort_accounts_by_access(const void *a, const void *b)
3098 const struct handle_info *hi_a = *(const struct handle_info**)a;
3099 const struct handle_info *hi_b = *(const struct handle_info**)b;
3100 if (hi_a->opserv_level != hi_b->opserv_level)
3101 return hi_b->opserv_level - hi_a->opserv_level;
3102 return irccasecmp(hi_a->handle, hi_b->handle);
3106 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3108 struct handle_info_list hil;
3109 struct helpfile_table tbl;
3114 memset(&hil, 0, sizeof(hil));
3115 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3116 struct handle_info *hi = iter_data(it);
3117 if (hi->opserv_level)
3118 handle_info_list_append(&hil, hi);
3120 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3121 tbl.length = hil.used + 1;
3123 tbl.flags = TABLE_NO_FREE;
3124 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3125 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3128 for (ii = 0; ii < hil.used; ) {
3129 ary = malloc(tbl.width * sizeof(ary[0]));
3130 ary[0] = hil.list[ii]->handle;
3131 ary[1] = strtab(hil.list[ii]->opserv_level);
3132 tbl.contents[++ii] = ary;
3134 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3135 reply("MSG_MATCH_COUNT", hil.used);
3136 for (ii = 0; ii < hil.used; ii++)
3137 free(tbl.contents[ii]);
3142 static NICKSERV_FUNC(cmd_search)
3144 struct nickserv_discrim *discrim;
3145 discrim_search_func action;
3146 struct svccmd *subcmd;
3147 unsigned int matches;
3150 NICKSERV_MIN_PARMS(3);
3151 sprintf(buf, "search %s", argv[1]);
3152 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3153 if (!irccasecmp(argv[1], "print"))
3154 action = search_print_func;
3155 else if (!irccasecmp(argv[1], "count"))
3156 action = search_count_func;
3157 else if (!irccasecmp(argv[1], "unregister"))
3158 action = search_unregister_func;
3160 reply("NSMSG_INVALID_ACTION", argv[1]);
3164 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3167 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3171 if (action == search_print_func)
3172 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3173 else if (action == search_count_func)
3174 discrim->limit = INT_MAX;
3176 matches = nickserv_discrim_search(discrim, action, user);
3179 reply("MSG_MATCH_COUNT", matches);
3181 reply("MSG_NO_MATCHES");
3187 static MODCMD_FUNC(cmd_checkpass)
3189 struct handle_info *hi;
3191 NICKSERV_MIN_PARMS(3);
3192 if (!(hi = get_handle_info(argv[1]))) {
3193 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3196 if (checkpass(argv[2], hi->passwd))
3197 reply("CHECKPASS_YES");
3199 reply("CHECKPASS_NO");
3205 nickserv_db_read_handle(const char *handle, dict_t obj)
3208 struct string_list *masks, *slist;
3209 struct handle_info *hi;
3210 struct userNode *authed_users;
3211 unsigned long int id;
3215 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3216 id = str ? strtoul(str, NULL, 0) : 0;
3217 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3219 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3222 if ((hi = get_handle_info(handle))) {
3223 authed_users = hi->users;
3225 dict_remove(nickserv_handle_dict, hi->handle);
3227 authed_users = NULL;
3229 hi = register_handle(handle, str, id);
3231 hi->users = authed_users;
3232 while (authed_users) {
3233 authed_users->handle_info = hi;
3234 authed_users = authed_users->next_authed;
3237 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3238 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3239 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3240 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3241 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3242 hi->language = language_find(str ? str : "C");
3243 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3244 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3245 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3247 hi->infoline = strdup(str);
3248 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3249 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3250 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3251 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3252 /* We want to read the nicks even if disable_nicks is set. This is so
3253 * that we don't lose the nick data entirely. */
3254 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3256 for (ii=0; ii<slist->used; ii++)
3257 register_nick(slist->list[ii], hi);
3259 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3261 for (ii=0; str[ii]; ii++)
3262 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3264 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3265 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3266 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3267 hi->announcements = str ? str[0] : '?';
3268 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3269 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3270 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3271 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3272 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3274 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3276 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3277 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3279 nickserv_set_email_addr(hi, str);
3280 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3282 hi->epithet = strdup(str);
3283 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3285 hi->fakehost = strdup(str);
3286 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3288 const char *data, *type, *expires, *cookie_str;
3289 struct handle_cookie *cookie;
3291 cookie = calloc(1, sizeof(*cookie));
3292 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3293 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3294 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3295 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3296 if (!type || !expires || !cookie_str) {
3297 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3300 if (!irccasecmp(type, KEY_ACTIVATION))
3301 cookie->type = ACTIVATION;
3302 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3303 cookie->type = PASSWORD_CHANGE;
3304 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3305 cookie->type = EMAIL_CHANGE;
3306 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3307 cookie->type = ALLOWAUTH;
3309 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3312 cookie->expires = strtoul(expires, NULL, 0);
3313 if (cookie->expires < now)
3316 cookie->data = strdup(data);
3317 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3321 nickserv_bake_cookie(cookie);
3323 nickserv_free_cookie(cookie);
3328 nickserv_saxdb_read(dict_t db) {
3330 struct record_data *rd;
3332 for (it=dict_first(db); it; it=iter_next(it)) {
3334 nickserv_db_read_handle(iter_key(it), rd->d.object);
3339 static NICKSERV_FUNC(cmd_mergedb)
3341 struct timeval start, stop;
3344 NICKSERV_MIN_PARMS(2);
3345 gettimeofday(&start, NULL);
3346 if (!(db = parse_database(argv[1]))) {
3347 reply("NSMSG_DB_UNREADABLE", argv[1]);
3350 nickserv_saxdb_read(db);
3352 gettimeofday(&stop, NULL);
3353 stop.tv_sec -= start.tv_sec;
3354 stop.tv_usec -= start.tv_usec;
3355 if (stop.tv_usec < 0) {
3357 stop.tv_usec += 1000000;
3359 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3364 expire_handles(UNUSED_ARG(void *data))
3366 dict_iterator_t it, next;
3368 struct handle_info *hi;
3370 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3371 next = iter_next(it);
3373 if ((hi->opserv_level > 0)
3375 || HANDLE_FLAGGED(hi, FROZEN)
3376 || HANDLE_FLAGGED(hi, NODELETE)) {
3379 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3380 if ((now - hi->lastseen) > expiry) {
3381 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3382 nickserv_unregister_handle(hi, NULL);
3386 if (nickserv_conf.handle_expire_frequency)
3387 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3391 nickserv_load_dict(const char *fname)
3395 if (!(file = fopen(fname, "r"))) {
3396 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3399 while (!feof(file)) {
3400 fgets(line, sizeof(line), file);
3403 if (line[strlen(line)-1] == '\n')
3404 line[strlen(line)-1] = 0;
3405 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3408 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3411 static enum reclaim_action
3412 reclaim_action_from_string(const char *str) {
3414 return RECLAIM_NONE;
3415 else if (!irccasecmp(str, "warn"))
3416 return RECLAIM_WARN;
3417 else if (!irccasecmp(str, "svsnick"))
3418 return RECLAIM_SVSNICK;
3419 else if (!irccasecmp(str, "kill"))
3420 return RECLAIM_KILL;
3422 return RECLAIM_NONE;
3426 nickserv_conf_read(void)
3428 dict_t conf_node, child;
3432 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3433 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3436 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3438 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3439 if (nickserv_conf.valid_handle_regex_set)
3440 regfree(&nickserv_conf.valid_handle_regex);
3442 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3443 nickserv_conf.valid_handle_regex_set = !err;
3444 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3446 nickserv_conf.valid_handle_regex_set = 0;
3448 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3449 if (nickserv_conf.valid_nick_regex_set)
3450 regfree(&nickserv_conf.valid_nick_regex);
3452 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3453 nickserv_conf.valid_nick_regex_set = !err;
3454 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3456 nickserv_conf.valid_nick_regex_set = 0;
3458 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3460 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3461 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3462 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3463 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3464 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3465 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3466 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3467 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3468 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3469 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3470 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3471 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3472 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3473 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3474 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3475 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3476 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3477 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3478 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3479 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3480 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3481 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3482 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3483 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3484 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3486 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3487 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3488 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3490 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3491 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3492 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3494 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3495 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3496 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3497 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3498 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3499 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3500 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3501 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3502 if (!nickserv_conf.disable_nicks) {
3503 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3504 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3505 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3506 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3507 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3508 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3509 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3510 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3512 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3513 for (it=dict_first(child); it; it=iter_next(it)) {
3514 const char *key = iter_key(it), *value;
3518 if (!strncasecmp(key, "uc_", 3))
3519 flag = toupper(key[3]);
3520 else if (!strncasecmp(key, "lc_", 3))
3521 flag = tolower(key[3]);
3525 if ((pos = handle_inverse_flags[flag])) {
3526 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3527 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3530 if (nickserv_conf.weak_password_dict)
3531 dict_delete(nickserv_conf.weak_password_dict);
3532 nickserv_conf.weak_password_dict = dict_new();
3533 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3534 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3535 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3536 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3538 nickserv_load_dict(str);
3539 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3540 if (nickserv && str)
3541 NickChange(nickserv, str, 0);
3542 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3543 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3544 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3545 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3546 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3547 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3548 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3549 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3550 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3551 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3552 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3553 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3554 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3555 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3556 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3557 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3558 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3559 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3560 str = conf_get_data("server/network", RECDB_QSTRING);
3561 nickserv_conf.network_name = str ? str : "some IRC network";
3562 if (!nickserv_conf.auth_policer_params) {
3563 nickserv_conf.auth_policer_params = policer_params_new();
3564 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3565 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3567 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3568 for (it=dict_first(child); it; it=iter_next(it))
3569 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3573 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3575 char newnick[NICKLEN+1];
3584 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3586 case RECLAIM_SVSNICK:
3588 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3589 } while (GetUserH(newnick));
3590 irc_svsnick(nickserv, user, newnick);
3593 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3594 irc_kill(nickserv, user, msg);
3600 nickserv_reclaim_p(void *data) {
3601 struct userNode *user = data;
3602 struct nick_info *ni = get_nick_info(user->nick);
3604 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3608 check_user_nick(struct userNode *user) {
3609 struct nick_info *ni;
3610 user->modes &= ~FLAGS_REGNICK;
3611 if (!(ni = get_nick_info(user->nick)))
3613 if (user->handle_info == ni->owner) {
3614 user->modes |= FLAGS_REGNICK;
3618 if (nickserv_conf.warn_nick_owned)
3619 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3620 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3622 if (nickserv_conf.auto_reclaim_delay)
3623 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3625 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3630 handle_new_user(struct userNode *user)
3632 return check_user_nick(user);
3636 handle_account(struct userNode *user, const char *stamp)
3638 struct handle_info *hi;
3640 #ifdef WITH_PROTOCOL_P10
3641 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3643 hi = dict_find(nickserv_id_dict, stamp, NULL);
3647 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3650 set_user_handle_info(user, hi, 0);
3652 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3657 handle_nick_change(struct userNode *user, const char *old_nick)
3659 struct handle_info *hi;
3661 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3662 dict_remove(nickserv_allow_auth_dict, old_nick);
3663 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3665 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3666 check_user_nick(user);
3670 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3672 dict_remove(nickserv_allow_auth_dict, user->nick);
3673 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3674 set_user_handle_info(user, NULL, 0);
3677 static struct modcmd *
3678 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3680 if (min_level > 0) {
3682 sprintf(buf, "%u", min_level);
3683 if (must_be_qualified) {
3684 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3686 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3688 } else if (min_level == 0) {
3689 if (must_be_qualified) {
3690 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3692 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3695 if (must_be_qualified) {
3696 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3698 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3704 nickserv_db_cleanup(void)
3706 unreg_del_user_func(nickserv_remove_user);
3707 userList_clean(&curr_helpers);
3708 policer_params_delete(nickserv_conf.auth_policer_params);
3709 dict_delete(nickserv_handle_dict);
3710 dict_delete(nickserv_nick_dict);
3711 dict_delete(nickserv_opt_dict);
3712 dict_delete(nickserv_allow_auth_dict);
3713 dict_delete(nickserv_email_dict);
3714 dict_delete(nickserv_id_dict);
3715 dict_delete(nickserv_conf.weak_password_dict);
3716 free(auth_func_list);
3717 free(unreg_func_list);
3719 free(allowauth_func_list);
3720 free(handle_merge_func_list);
3721 free(failpw_func_list);
3722 if (nickserv_conf.valid_handle_regex_set)
3723 regfree(&nickserv_conf.valid_handle_regex);
3724 if (nickserv_conf.valid_nick_regex_set)
3725 regfree(&nickserv_conf.valid_nick_regex);
3729 init_nickserv(const char *nick)
3732 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3733 reg_new_user_func(handle_new_user);
3734 reg_nick_change_func(handle_nick_change);
3735 reg_del_user_func(nickserv_remove_user);
3736 reg_account_func(handle_account);
3738 /* set up handle_inverse_flags */
3739 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3740 for (i=0; handle_flags[i]; i++) {
3741 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3742 flag_access_levels[i] = 0;
3745 conf_register_reload(nickserv_conf_read);
3746 nickserv_opt_dict = dict_new();
3747 nickserv_email_dict = dict_new();
3748 dict_set_free_keys(nickserv_email_dict, free);
3749 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3751 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3752 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3753 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3754 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3755 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3756 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3757 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3758 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3759 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3760 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3761 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3762 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3763 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3764 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3765 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3766 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3767 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3768 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3769 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3770 if (!nickserv_conf.disable_nicks) {
3771 /* nick management commands */
3772 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3773 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3774 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3775 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3776 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3777 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3779 if (nickserv_conf.email_enabled) {
3780 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3781 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3782 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3783 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3784 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3786 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3787 /* miscellaneous commands */
3788 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3789 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3790 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3791 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3792 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3794 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3795 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3796 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3797 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3798 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3799 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3800 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3801 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3802 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3803 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3804 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3805 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3806 if (nickserv_conf.titlehost_suffix) {
3807 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
3808 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
3810 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3811 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3812 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3814 nickserv_handle_dict = dict_new();
3815 dict_set_free_keys(nickserv_handle_dict, free);
3816 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3818 nickserv_id_dict = dict_new();
3819 dict_set_free_keys(nickserv_id_dict, free);
3821 nickserv_nick_dict = dict_new();
3822 dict_set_free_data(nickserv_nick_dict, free);
3824 nickserv_allow_auth_dict = dict_new();
3826 userList_init(&curr_helpers);
3829 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
3830 nickserv = AddService(nick, modes ? modes : NULL, "Nick Services", NULL);
3831 nickserv_service = service_register(nickserv);
3833 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3834 reg_exit_func(nickserv_db_cleanup);
3835 if(nickserv_conf.handle_expire_frequency)
3836 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3837 message_register_table(msgtab);