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;
756 if (len < nickserv_conf.password_min_length) {
758 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
761 if (!irccasecmp(pass, handle)) {
763 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
766 dict_find(nickserv_conf.weak_password_dict, pass, &p);
769 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
772 for (i=0; i<len; i++) {
773 if (isdigit(pass[i]))
775 if (isupper(pass[i]))
777 if (islower(pass[i]))
780 if ((cnt_lower < nickserv_conf.password_min_lower)
781 || (cnt_upper < nickserv_conf.password_min_upper)
782 || (cnt_digits < nickserv_conf.password_min_digits)) {
784 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
790 static auth_func_t *auth_func_list;
791 static unsigned int auth_func_size = 0, auth_func_used = 0;
794 reg_auth_func(auth_func_t func)
796 if (auth_func_used == auth_func_size) {
797 if (auth_func_size) {
798 auth_func_size <<= 1;
799 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
802 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
805 auth_func_list[auth_func_used++] = func;
808 static handle_rename_func_t *rf_list;
809 static unsigned int rf_list_size, rf_list_used;
812 reg_handle_rename_func(handle_rename_func_t func)
814 if (rf_list_used == rf_list_size) {
817 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
820 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
823 rf_list[rf_list_used++] = func;
827 generate_fakehost(struct handle_info *handle)
829 extern const char *hidden_host_suffix;
830 static char buffer[HOSTLEN+1];
832 if (!handle->fakehost) {
833 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
835 } else if (handle->fakehost[0] == '.') {
836 /* A leading dot indicates the stored value is actually a title. */
837 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
840 return handle->fakehost;
844 apply_fakehost(struct handle_info *handle)
846 struct userNode *target;
851 fake = generate_fakehost(handle);
852 for (target = handle->users; target; target = target->next_authed)
853 assign_fakehost(target, fake, 1);
857 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
860 struct handle_info *old_info;
862 /* This can happen if somebody uses COOKIE while authed, or if
863 * they re-auth to their current handle (which is silly, but users
865 if (user->handle_info == hi)
868 if (user->handle_info) {
869 struct userNode *other;
872 userList_remove(&curr_helpers, user);
874 /* remove from next_authed linked list */
875 if (user->handle_info->users == user) {
876 user->handle_info->users = user->next_authed;
878 for (other = user->handle_info->users;
879 other->next_authed != user;
880 other = other->next_authed) ;
881 other->next_authed = user->next_authed;
883 /* if nobody left on old handle, and they're not an oper, remove !god */
884 if (!user->handle_info->users && !user->handle_info->opserv_level)
885 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
886 /* record them as being last seen at this time */
887 user->handle_info->lastseen = now;
888 /* and record their hostmask */
889 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
891 old_info = user->handle_info;
892 user->handle_info = hi;
893 if (hi && !hi->users && !hi->opserv_level)
894 HANDLE_CLEAR_FLAG(hi, HELPING);
895 for (n=0; n<auth_func_used; n++)
896 auth_func_list[n](user, old_info);
898 struct nick_info *ni;
900 HANDLE_CLEAR_FLAG(hi, FROZEN);
901 if (nickserv_conf.warn_clone_auth) {
902 struct userNode *other;
903 for (other = hi->users; other; other = other->next_authed)
904 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
906 user->next_authed = hi->users;
910 userList_append(&curr_helpers, user);
912 if (hi->fakehost || old_info)
916 #ifdef WITH_PROTOCOL_BAHAMUT
917 /* Stamp users with their account ID. */
919 inttobase64(id, hi->id, IDLEN);
920 #elif WITH_PROTOCOL_P10
921 /* Stamp users with their account name. */
922 char *id = hi->handle;
924 const char *id = "???";
926 if (!nickserv_conf.disable_nicks) {
927 struct nick_info *ni;
928 for (ni = hi->nicks; ni; ni = ni->next) {
929 if (!irccasecmp(user->nick, ni->nick)) {
930 user->modes |= FLAGS_REGNICK;
938 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
939 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
941 /* We cannot clear the user's account ID, unfortunately. */
942 user->next_authed = NULL;
946 static struct handle_info*
947 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
949 struct handle_info *hi;
950 struct nick_info *ni;
951 char crypted[MD5_CRYPT_LENGTH];
953 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
954 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
958 if (!is_secure_password(handle, passwd, user))
961 cryptpass(passwd, crypted);
962 hi = register_handle(handle, crypted, 0);
963 hi->masks = alloc_string_list(1);
965 hi->language = lang_C;
966 hi->registered = now;
968 hi->flags = HI_DEFAULT_FLAGS;
969 if (settee && !no_auth)
970 set_user_handle_info(settee, hi, 1);
973 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
974 else if (nickserv_conf.disable_nicks)
975 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
976 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
977 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
979 register_nick(user->nick, hi);
980 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
982 if (settee && (user != settee))
983 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
988 nickserv_bake_cookie(struct handle_cookie *cookie)
990 cookie->hi->cookie = cookie;
991 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
995 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
997 struct handle_cookie *cookie;
998 char subject[128], body[4096], *misc;
999 const char *netname, *fmt;
1003 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1007 cookie = calloc(1, sizeof(*cookie));
1009 cookie->type = type;
1010 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1011 cookie->expires = now + nickserv_conf.cookie_timeout;
1012 inttobase64(cookie->cookie, rand(), 5);
1013 inttobase64(cookie->cookie+5, rand(), 5);
1015 netname = nickserv_conf.network_name;
1018 switch (cookie->type) {
1020 hi->passwd[0] = 0; /* invalidate password */
1021 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1022 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1023 snprintf(subject, sizeof(subject), fmt, netname);
1024 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1025 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1028 case PASSWORD_CHANGE:
1029 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1030 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1031 snprintf(subject, sizeof(subject), fmt, netname);
1032 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1033 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1036 misc = hi->email_addr;
1037 hi->email_addr = cookie->data;
1039 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1040 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1041 snprintf(subject, sizeof(subject), fmt, netname);
1042 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1043 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1044 sendmail(nickserv, hi, subject, body, 1);
1045 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1046 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1048 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1049 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1050 snprintf(subject, sizeof(subject), fmt, netname);
1051 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1052 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1053 sendmail(nickserv, hi, subject, body, 1);
1056 hi->email_addr = misc;
1059 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1060 snprintf(subject, sizeof(subject), fmt, netname);
1061 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1062 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1063 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1066 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1070 sendmail(nickserv, hi, subject, body, first_time);
1071 nickserv_bake_cookie(cookie);
1075 nickserv_eat_cookie(struct handle_cookie *cookie)
1077 cookie->hi->cookie = NULL;
1078 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1079 nickserv_free_cookie(cookie);
1083 nickserv_free_email_addr(void *data)
1085 handle_info_list_clean(data);
1090 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1092 struct handle_info_list *hil;
1093 /* Remove from old handle_info_list ... */
1094 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1095 handle_info_list_remove(hil, hi);
1096 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1097 hi->email_addr = NULL;
1099 /* Add to the new list.. */
1100 if (new_email_addr) {
1101 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1102 hil = calloc(1, sizeof(*hil));
1103 hil->tag = strdup(new_email_addr);
1104 handle_info_list_init(hil);
1105 dict_insert(nickserv_email_dict, hil->tag, hil);
1107 handle_info_list_append(hil, hi);
1108 hi->email_addr = hil->tag;
1112 static NICKSERV_FUNC(cmd_register)
1114 struct handle_info *hi;
1115 const char *email_addr, *password;
1118 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1119 /* Require the first handle registered to belong to someone +o. */
1120 reply("NSMSG_REQUIRE_OPER");
1124 if (user->handle_info) {
1125 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1129 if (IsRegistering(user)) {
1130 reply("NSMSG_ALREADY_REGISTERING");
1134 if (IsStamped(user)) {
1135 /* Unauthenticated users might still have been stamped
1136 previously and could therefore have a hidden host;
1137 do not allow them to register a new account. */
1138 reply("NSMSG_STAMPED_REGISTER");
1142 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1144 if (!is_valid_handle(argv[1])) {
1145 reply("NSMSG_BAD_HANDLE", argv[1]);
1149 if ((argc >= 4) && nickserv_conf.email_enabled) {
1150 struct handle_info_list *hil;
1153 /* Remember email address. */
1154 email_addr = argv[3];
1156 /* Check that the email address looks valid.. */
1157 if (!is_valid_email_addr(email_addr)) {
1158 reply("NSMSG_BAD_EMAIL_ADDR");
1162 /* .. and that we are allowed to send to it. */
1163 if ((str = sendmail_prohibited_address(email_addr))) {
1164 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1168 /* If we do email verify, make sure we don't spam the address. */
1169 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1171 for (nn=0; nn<hil->used; nn++) {
1172 if (hil->list[nn]->cookie) {
1173 reply("NSMSG_EMAIL_UNACTIVATED");
1177 if (hil->used >= nickserv_conf.handles_per_email) {
1178 reply("NSMSG_EMAIL_OVERUSED");
1191 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1193 /* Add any masks they should get. */
1194 if (nickserv_conf.default_hostmask) {
1195 string_list_append(hi->masks, strdup("*@*"));
1197 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1198 if (user->ip.s_addr && user->hostname[strspn(user->hostname, "0123456789.")])
1199 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1202 /* If they're the first to register, give them level 1000. */
1203 if (dict_size(nickserv_handle_dict) == 1) {
1204 hi->opserv_level = 1000;
1205 reply("NSMSG_ROOT_HANDLE", argv[1]);
1208 /* Set their email address. */
1210 nickserv_set_email_addr(hi, email_addr);
1212 /* If they need to do email verification, tell them. */
1214 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1216 /* Set registering flag.. */
1217 user->modes |= FLAGS_REGISTERING;
1222 static NICKSERV_FUNC(cmd_oregister)
1225 struct userNode *settee;
1226 struct handle_info *hi;
1228 NICKSERV_MIN_PARMS(4);
1230 if (!is_valid_handle(argv[1])) {
1231 reply("NSMSG_BAD_HANDLE", argv[1]);
1235 if (strchr(argv[3], '@')) {
1236 mask = canonicalize_hostmask(strdup(argv[3]));
1238 settee = GetUserH(argv[4]);
1240 reply("MSG_NICK_UNKNOWN", argv[4]);
1247 } else if ((settee = GetUserH(argv[3]))) {
1248 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1250 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1253 if (settee && settee->handle_info) {
1254 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1258 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1262 string_list_append(hi->masks, mask);
1266 static NICKSERV_FUNC(cmd_handleinfo)
1269 unsigned int i, pos=0, herelen;
1270 struct userNode *target, *next_un;
1271 struct handle_info *hi;
1272 const char *nsmsg_none;
1275 if (!(hi = user->handle_info)) {
1276 reply("NSMSG_MUST_AUTH");
1279 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1283 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1284 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1285 #ifdef WITH_PROTOCOL_BAHAMUT
1286 reply("NSMSG_HANDLEINFO_ID", hi->id);
1288 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1291 intervalString(buff, now - hi->lastseen, user->handle_info);
1292 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1294 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1297 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1298 if (HANDLE_FLAGGED(hi, FROZEN))
1299 reply("NSMSG_HANDLEINFO_VACATION");
1301 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1302 struct do_not_register *dnr;
1303 if ((dnr = chanserv_is_dnr(NULL, hi)))
1304 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1305 if (!oper_outranks(user, hi))
1307 } else if (hi != user->handle_info)
1310 if (nickserv_conf.email_enabled)
1311 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1315 switch (hi->cookie->type) {
1316 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1317 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1318 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1319 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1320 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1326 unsigned long flen = 1;
1327 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1329 for (i=0, flen=1; handle_flags[i]; i++)
1330 if (hi->flags & 1 << i)
1331 flags[flen++] = handle_flags[i];
1333 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1335 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1338 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1339 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1340 || (hi->opserv_level > 0)) {
1341 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1345 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1347 if (hi->last_quit_host[0])
1348 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1350 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1352 if (nickserv_conf.disable_nicks) {
1353 /* nicks disabled; don't show anything about registered nicks */
1354 } else if (hi->nicks) {
1355 struct nick_info *ni, *next_ni;
1356 for (ni = hi->nicks; ni; ni = next_ni) {
1357 herelen = strlen(ni->nick);
1358 if (pos + herelen + 1 > ArrayLength(buff)) {
1360 goto print_nicks_buff;
1364 memcpy(buff+pos, ni->nick, herelen);
1365 pos += herelen; buff[pos++] = ' ';
1369 reply("NSMSG_HANDLEINFO_NICKS", buff);
1374 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1377 if (hi->masks->used) {
1378 for (i=0; i < hi->masks->used; i++) {
1379 herelen = strlen(hi->masks->list[i]);
1380 if (pos + herelen + 1 > ArrayLength(buff)) {
1382 goto print_mask_buff;
1384 memcpy(buff+pos, hi->masks->list[i], herelen);
1385 pos += herelen; buff[pos++] = ' ';
1386 if (i+1 == hi->masks->used) {
1389 reply("NSMSG_HANDLEINFO_MASKS", buff);
1394 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1398 struct userData *channel, *next;
1401 for (channel = hi->channels; channel; channel = next) {
1402 next = channel->u_next;
1403 name = channel->channel->channel->name;
1404 herelen = strlen(name);
1405 if (pos + herelen + 7 > ArrayLength(buff)) {
1407 goto print_chans_buff;
1409 if (IsUserSuspended(channel))
1411 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1415 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1420 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1423 for (target = hi->users; target; target = next_un) {
1424 herelen = strlen(target->nick);
1425 if (pos + herelen + 1 > ArrayLength(buff)) {
1427 goto print_cnick_buff;
1429 next_un = target->next_authed;
1431 memcpy(buff+pos, target->nick, herelen);
1432 pos += herelen; buff[pos++] = ' ';
1436 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1444 static NICKSERV_FUNC(cmd_userinfo)
1446 struct userNode *target;
1448 NICKSERV_MIN_PARMS(2);
1449 if (!(target = GetUserH(argv[1]))) {
1450 reply("MSG_NICK_UNKNOWN", argv[1]);
1453 if (target->handle_info)
1454 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1456 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1460 static NICKSERV_FUNC(cmd_nickinfo)
1462 struct nick_info *ni;
1464 NICKSERV_MIN_PARMS(2);
1465 if (!(ni = get_nick_info(argv[1]))) {
1466 reply("MSG_NICK_UNKNOWN", argv[1]);
1469 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1473 static NICKSERV_FUNC(cmd_rename_handle)
1475 struct handle_info *hi;
1476 char msgbuf[MAXLEN], *old_handle;
1479 NICKSERV_MIN_PARMS(3);
1480 if (!(hi = get_victim_oper(user, argv[1])))
1482 if (!is_valid_handle(argv[2])) {
1483 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1486 if (get_handle_info(argv[2])) {
1487 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1491 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1492 hi->handle = strdup(argv[2]);
1493 dict_insert(nickserv_handle_dict, hi->handle, hi);
1494 for (nn=0; nn<rf_list_used; nn++)
1495 rf_list[nn](hi, old_handle);
1496 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1497 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1498 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1503 static failpw_func_t *failpw_func_list;
1504 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1507 reg_failpw_func(failpw_func_t func)
1509 if (failpw_func_used == failpw_func_size) {
1510 if (failpw_func_size) {
1511 failpw_func_size <<= 1;
1512 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1514 failpw_func_size = 8;
1515 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1518 failpw_func_list[failpw_func_used++] = func;
1521 static NICKSERV_FUNC(cmd_auth)
1523 int pw_arg, used, maxlogins;
1524 struct handle_info *hi;
1526 struct userNode *other;
1528 if (user->handle_info) {
1529 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1532 if (IsStamped(user)) {
1533 /* Unauthenticated users might still have been stamped
1534 previously and could therefore have a hidden host;
1535 do not allow them to authenticate. */
1536 reply("NSMSG_STAMPED_AUTH");
1540 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1542 } else if (argc == 2) {
1543 if (nickserv_conf.disable_nicks) {
1544 if (!(hi = get_handle_info(user->nick))) {
1545 reply("NSMSG_HANDLE_NOT_FOUND");
1549 /* try to look up their handle from their nick */
1550 struct nick_info *ni;
1551 ni = get_nick_info(user->nick);
1553 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1560 reply("MSG_MISSING_PARAMS", argv[0]);
1561 svccmd_send_help(user, nickserv, cmd);
1565 reply("NSMSG_HANDLE_NOT_FOUND");
1568 /* Responses from here on look up the language used by the handle they asked about. */
1569 passwd = argv[pw_arg];
1570 if (!valid_user_for(user, hi)) {
1571 if (hi->email_addr && nickserv_conf.email_enabled)
1572 send_message_type(4, user, cmd->parent->bot,
1573 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1576 send_message_type(4, user, cmd->parent->bot,
1577 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1579 argv[pw_arg] = "BADMASK";
1582 if (!checkpass(passwd, hi->passwd)) {
1584 send_message_type(4, user, cmd->parent->bot,
1585 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1586 argv[pw_arg] = "BADPASS";
1587 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1588 if (nickserv_conf.autogag_enabled) {
1589 if (!user->auth_policer.params) {
1590 user->auth_policer.last_req = now;
1591 user->auth_policer.params = nickserv_conf.auth_policer_params;
1593 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1595 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1596 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1597 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1599 argv[pw_arg] = "GAGGED";
1604 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1605 send_message_type(4, user, cmd->parent->bot,
1606 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1607 argv[pw_arg] = "SUSPENDED";
1610 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1611 for (used = 0, other = hi->users; other; other = other->next_authed) {
1612 if (++used >= maxlogins) {
1613 send_message_type(4, user, cmd->parent->bot,
1614 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1616 argv[pw_arg] = "MAXLOGINS";
1621 set_user_handle_info(user, hi, 1);
1622 if (nickserv_conf.email_required && !hi->email_addr)
1623 reply("NSMSG_PLEASE_SET_EMAIL");
1624 if (!is_secure_password(hi->handle, passwd, NULL))
1625 reply("NSMSG_WEAK_PASSWORD");
1626 if (hi->passwd[0] != '$')
1627 cryptpass(passwd, hi->passwd);
1628 reply("NSMSG_AUTH_SUCCESS");
1629 argv[pw_arg] = "****";
1633 static allowauth_func_t *allowauth_func_list;
1634 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1637 reg_allowauth_func(allowauth_func_t func)
1639 if (allowauth_func_used == allowauth_func_size) {
1640 if (allowauth_func_size) {
1641 allowauth_func_size <<= 1;
1642 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1644 allowauth_func_size = 8;
1645 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1648 allowauth_func_list[allowauth_func_used++] = func;
1651 static NICKSERV_FUNC(cmd_allowauth)
1653 struct userNode *target;
1654 struct handle_info *hi;
1657 NICKSERV_MIN_PARMS(2);
1658 if (!(target = GetUserH(argv[1]))) {
1659 reply("MSG_NICK_UNKNOWN", argv[1]);
1662 if (target->handle_info) {
1663 reply("NSMSG_USER_PREV_AUTH", target->nick);
1666 if (IsStamped(target)) {
1667 /* Unauthenticated users might still have been stamped
1668 previously and could therefore have a hidden host;
1669 do not allow them to authenticate to an account. */
1670 reply("NSMSG_USER_PREV_STAMP", target->nick);
1675 else if (!(hi = get_handle_info(argv[2]))) {
1676 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1680 if (hi->opserv_level > user->handle_info->opserv_level) {
1681 reply("MSG_USER_OUTRANKED", hi->handle);
1684 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1685 || (hi->opserv_level > 0))
1686 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1687 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1690 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1691 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1692 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1693 if (nickserv_conf.email_enabled)
1694 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1696 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1697 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1699 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1701 for (n=0; n<allowauth_func_used; n++)
1702 allowauth_func_list[n](user, target, hi);
1706 static NICKSERV_FUNC(cmd_authcookie)
1708 struct handle_info *hi;
1710 NICKSERV_MIN_PARMS(2);
1711 if (user->handle_info) {
1712 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1715 if (IsStamped(user)) {
1716 /* Unauthenticated users might still have been stamped
1717 previously and could therefore have a hidden host;
1718 do not allow them to authenticate to an account. */
1719 reply("NSMSG_STAMPED_AUTHCOOKIE");
1722 if (!(hi = get_handle_info(argv[1]))) {
1723 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1726 if (!hi->email_addr) {
1727 reply("MSG_SET_EMAIL_ADDR");
1730 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1734 static NICKSERV_FUNC(cmd_delcookie)
1736 struct handle_info *hi;
1738 hi = user->handle_info;
1740 reply("NSMSG_NO_COOKIE");
1743 switch (hi->cookie->type) {
1746 reply("NSMSG_MUST_TIME_OUT");
1749 nickserv_eat_cookie(hi->cookie);
1750 reply("NSMSG_ATE_COOKIE");
1756 static NICKSERV_FUNC(cmd_resetpass)
1758 struct handle_info *hi;
1759 char crypted[MD5_CRYPT_LENGTH];
1761 NICKSERV_MIN_PARMS(3);
1762 if (user->handle_info) {
1763 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1766 if (IsStamped(user)) {
1767 /* Unauthenticated users might still have been stamped
1768 previously and could therefore have a hidden host;
1769 do not allow them to activate an account. */
1770 reply("NSMSG_STAMPED_RESETPASS");
1773 if (!(hi = get_handle_info(argv[1]))) {
1774 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1777 if (!hi->email_addr) {
1778 reply("MSG_SET_EMAIL_ADDR");
1781 cryptpass(argv[2], crypted);
1783 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1787 static NICKSERV_FUNC(cmd_cookie)
1789 struct handle_info *hi;
1792 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1795 NICKSERV_MIN_PARMS(3);
1796 if (!(hi = get_handle_info(argv[1]))) {
1797 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1803 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1804 reply("NSMSG_HANDLE_SUSPENDED");
1809 reply("NSMSG_NO_COOKIE");
1813 /* Check validity of operation before comparing cookie to
1814 * prohibit guessing by authed users. */
1815 if (user->handle_info
1816 && (hi->cookie->type != EMAIL_CHANGE)
1817 && (hi->cookie->type != PASSWORD_CHANGE)) {
1818 reply("NSMSG_CANNOT_COOKIE");
1822 if (strcmp(cookie, hi->cookie->cookie)) {
1823 reply("NSMSG_BAD_COOKIE");
1827 switch (hi->cookie->type) {
1829 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1830 set_user_handle_info(user, hi, 1);
1831 reply("NSMSG_HANDLE_ACTIVATED");
1833 case PASSWORD_CHANGE:
1834 set_user_handle_info(user, hi, 1);
1835 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1836 reply("NSMSG_PASSWORD_CHANGED");
1839 nickserv_set_email_addr(hi, hi->cookie->data);
1840 reply("NSMSG_EMAIL_CHANGED");
1843 set_user_handle_info(user, hi, 1);
1844 reply("NSMSG_AUTH_SUCCESS");
1847 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1848 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1852 nickserv_eat_cookie(hi->cookie);
1857 static NICKSERV_FUNC(cmd_oregnick) {
1859 struct handle_info *target;
1860 struct nick_info *ni;
1862 NICKSERV_MIN_PARMS(3);
1863 if (!(target = modcmd_get_handle_info(user, argv[1])))
1866 if (!is_registerable_nick(nick)) {
1867 reply("NSMSG_BAD_NICK", nick);
1870 ni = dict_find(nickserv_nick_dict, nick, NULL);
1872 reply("NSMSG_NICK_EXISTS", nick);
1875 register_nick(nick, target);
1876 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1880 static NICKSERV_FUNC(cmd_regnick) {
1882 struct nick_info *ni;
1884 if (!is_registerable_nick(user->nick)) {
1885 reply("NSMSG_BAD_NICK", user->nick);
1888 /* count their nicks, see if it's too many */
1889 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1890 if (n >= nickserv_conf.nicks_per_handle) {
1891 reply("NSMSG_TOO_MANY_NICKS");
1894 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1896 reply("NSMSG_NICK_EXISTS", user->nick);
1899 register_nick(user->nick, user->handle_info);
1900 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1904 static NICKSERV_FUNC(cmd_pass)
1906 struct handle_info *hi;
1907 const char *old_pass, *new_pass;
1909 NICKSERV_MIN_PARMS(3);
1910 hi = user->handle_info;
1914 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1915 if (!checkpass(old_pass, hi->passwd)) {
1916 argv[1] = "BADPASS";
1917 reply("NSMSG_PASSWORD_INVALID");
1920 cryptpass(new_pass, hi->passwd);
1922 reply("NSMSG_PASS_SUCCESS");
1927 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1930 char *new_mask = canonicalize_hostmask(strdup(mask));
1931 for (i=0; i<hi->masks->used; i++) {
1932 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1933 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1938 string_list_append(hi->masks, new_mask);
1939 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
1943 static NICKSERV_FUNC(cmd_addmask)
1946 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1947 int res = nickserv_addmask(user, user->handle_info, mask);
1951 if (!is_gline(argv[1])) {
1952 reply("NSMSG_MASK_INVALID", argv[1]);
1955 return nickserv_addmask(user, user->handle_info, argv[1]);
1959 static NICKSERV_FUNC(cmd_oaddmask)
1961 struct handle_info *hi;
1963 NICKSERV_MIN_PARMS(3);
1964 if (!(hi = get_victim_oper(user, argv[1])))
1966 return nickserv_addmask(user, hi, argv[2]);
1970 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
1973 for (i=0; i<hi->masks->used; i++) {
1974 if (!strcmp(del_mask, hi->masks->list[i])) {
1975 char *old_mask = hi->masks->list[i];
1976 if (hi->masks->used == 1) {
1977 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
1980 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
1981 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
1986 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
1990 static NICKSERV_FUNC(cmd_delmask)
1992 NICKSERV_MIN_PARMS(2);
1993 return nickserv_delmask(user, user->handle_info, argv[1]);
1996 static NICKSERV_FUNC(cmd_odelmask)
1998 struct handle_info *hi;
1999 NICKSERV_MIN_PARMS(3);
2000 if (!(hi = get_victim_oper(user, argv[1])))
2002 return nickserv_delmask(user, hi, argv[2]);
2006 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2007 unsigned int nn, add = 1, pos;
2008 unsigned long added, removed, flag;
2010 for (added=removed=nn=0; str[nn]; nn++) {
2012 case '+': add = 1; break;
2013 case '-': add = 0; break;
2015 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2016 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2019 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2020 /* cheesy avoidance of looking up the flag name.. */
2021 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2024 flag = 1 << (pos - 1);
2026 added |= flag, removed &= ~flag;
2028 removed |= flag, added &= ~flag;
2033 *premoved = removed;
2038 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2040 unsigned long before, after, added, removed;
2041 struct userNode *uNode;
2043 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2044 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2046 hi->flags = (hi->flags | added) & ~removed;
2047 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2049 /* Strip helping flag if they're only a support helper and not
2050 * currently in #support. */
2051 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2052 struct channelList *schannels;
2054 schannels = chanserv_support_channels();
2055 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2056 for (ii = 0; ii < schannels->used; ++ii)
2057 if (GetUserMode(schannels->list[ii], uNode))
2059 if (ii < schannels->used)
2063 HANDLE_CLEAR_FLAG(hi, HELPING);
2066 if (after && !before) {
2067 /* Add user to current helper list. */
2068 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2069 userList_append(&curr_helpers, uNode);
2070 } else if (!after && before) {
2071 /* Remove user from current helper list. */
2072 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2073 userList_remove(&curr_helpers, uNode);
2080 set_list(struct userNode *user, struct handle_info *hi, int override)
2084 char *set_display[] = {
2085 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2086 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2089 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2091 /* Do this so options are presented in a consistent order. */
2092 for (i = 0; i < ArrayLength(set_display); ++i)
2093 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2094 opt(user, hi, override, 0, NULL);
2097 static NICKSERV_FUNC(cmd_set)
2099 struct handle_info *hi;
2102 hi = user->handle_info;
2104 set_list(user, hi, 0);
2107 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2108 reply("NSMSG_INVALID_OPTION", argv[1]);
2111 return opt(user, hi, 0, argc-1, argv+1);
2114 static NICKSERV_FUNC(cmd_oset)
2116 struct handle_info *hi;
2119 NICKSERV_MIN_PARMS(2);
2121 if (!(hi = get_victim_oper(user, argv[1])))
2125 set_list(user, hi, 0);
2129 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2130 reply("NSMSG_INVALID_OPTION", argv[2]);
2134 return opt(user, hi, 1, argc-2, argv+2);
2137 static OPTION_FUNC(opt_info)
2141 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2143 hi->infoline = NULL;
2145 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2149 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2150 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2154 static OPTION_FUNC(opt_width)
2157 hi->screen_width = strtoul(argv[1], NULL, 0);
2159 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2160 hi->screen_width = MIN_LINE_SIZE;
2161 else if (hi->screen_width > MAX_LINE_SIZE)
2162 hi->screen_width = MAX_LINE_SIZE;
2164 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2168 static OPTION_FUNC(opt_tablewidth)
2171 hi->table_width = strtoul(argv[1], NULL, 0);
2173 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2174 hi->table_width = MIN_LINE_SIZE;
2175 else if (hi->screen_width > MAX_LINE_SIZE)
2176 hi->table_width = MAX_LINE_SIZE;
2178 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2182 static OPTION_FUNC(opt_color)
2185 if (enabled_string(argv[1]))
2186 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2187 else if (disabled_string(argv[1]))
2188 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2190 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2195 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2199 static OPTION_FUNC(opt_privmsg)
2202 if (enabled_string(argv[1]))
2203 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2204 else if (disabled_string(argv[1]))
2205 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2207 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2212 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2216 static OPTION_FUNC(opt_style)
2221 if (!irccasecmp(argv[1], "Zoot"))
2222 hi->userlist_style = HI_STYLE_ZOOT;
2223 else if (!irccasecmp(argv[1], "def"))
2224 hi->userlist_style = HI_STYLE_DEF;
2227 switch (hi->userlist_style) {
2236 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2240 static OPTION_FUNC(opt_announcements)
2245 if (enabled_string(argv[1]))
2246 hi->announcements = 'y';
2247 else if (disabled_string(argv[1]))
2248 hi->announcements = 'n';
2249 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2250 hi->announcements = '?';
2252 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2257 switch (hi->announcements) {
2258 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2259 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2260 case '?': choice = "default"; break;
2261 default: choice = "unknown"; break;
2263 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2267 static OPTION_FUNC(opt_password)
2270 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2275 cryptpass(argv[1], hi->passwd);
2277 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2281 static OPTION_FUNC(opt_flags)
2284 unsigned int ii, flen;
2287 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2292 nickserv_apply_flags(user, hi, argv[1]);
2294 for (ii = flen = 0; handle_flags[ii]; ii++)
2295 if (hi->flags & (1 << ii))
2296 flags[flen++] = handle_flags[ii];
2299 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2301 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2305 static OPTION_FUNC(opt_email)
2309 if (!is_valid_email_addr(argv[1])) {
2310 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2313 if ((str = sendmail_prohibited_address(argv[1]))) {
2314 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2317 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2318 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2320 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2322 nickserv_set_email_addr(hi, argv[1]);
2324 nickserv_eat_cookie(hi->cookie);
2325 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2328 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2332 static OPTION_FUNC(opt_maxlogins)
2334 unsigned char maxlogins;
2336 maxlogins = strtoul(argv[1], NULL, 0);
2337 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2338 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2341 hi->maxlogins = maxlogins;
2343 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2344 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2348 static OPTION_FUNC(opt_language)
2350 struct language *lang;
2352 lang = language_find(argv[1]);
2353 if (irccasecmp(lang->name, argv[1]))
2354 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2355 hi->language = lang;
2357 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2362 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2363 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2365 if ((user->handle_info->opserv_level < target->opserv_level)
2366 || ((user->handle_info->opserv_level == target->opserv_level)
2367 && (user->handle_info->opserv_level < 1000))) {
2368 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2371 if ((user->handle_info->opserv_level < new_level)
2372 || ((user->handle_info->opserv_level == new_level)
2373 && (user->handle_info->opserv_level < 1000))) {
2374 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2377 if (user->handle_info == target) {
2378 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2381 if (target->opserv_level == new_level)
2383 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2384 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2385 target->opserv_level = new_level;
2389 static OPTION_FUNC(opt_level)
2394 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2398 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2399 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2403 static OPTION_FUNC(opt_epithet)
2406 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2410 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2411 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2414 if ((epithet[0] == '*') && !epithet[1])
2417 hi->epithet = strdup(epithet);
2421 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2423 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2427 static OPTION_FUNC(opt_title)
2432 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2436 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2438 if (strchr(title, '.')) {
2439 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2442 if ((strlen(user->handle_info->handle) + strlen(title) +
2443 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2444 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2449 if (!strcmp(title, "*")) {
2450 hi->fakehost = NULL;
2452 hi->fakehost = malloc(strlen(title)+2);
2453 hi->fakehost[0] = '.';
2454 strcpy(hi->fakehost+1, title);
2457 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2458 title = hi->fakehost + 1;
2462 title = user_find_message(user, "MSG_NONE");
2463 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2467 static OPTION_FUNC(opt_fakehost)
2472 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2476 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2478 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2479 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2483 if (!strcmp(fake, "*"))
2484 hi->fakehost = NULL;
2486 hi->fakehost = strdup(fake);
2487 fake = hi->fakehost;
2490 fake = generate_fakehost(hi);
2492 fake = user_find_message(user, "MSG_NONE");
2493 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2497 static NICKSERV_FUNC(cmd_reclaim)
2499 struct handle_info *hi;
2500 struct nick_info *ni;
2501 struct userNode *victim;
2503 NICKSERV_MIN_PARMS(2);
2504 hi = user->handle_info;
2505 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2507 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2510 if (ni->owner != user->handle_info) {
2511 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2514 victim = GetUserH(ni->nick);
2516 reply("MSG_NICK_UNKNOWN", ni->nick);
2519 if (victim == user) {
2520 reply("NSMSG_NICK_USER_YOU");
2523 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2524 switch (nickserv_conf.reclaim_action) {
2525 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2526 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2527 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2528 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2533 static NICKSERV_FUNC(cmd_unregnick)
2536 struct handle_info *hi;
2537 struct nick_info *ni;
2539 hi = user->handle_info;
2540 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2541 ni = dict_find(nickserv_nick_dict, nick, NULL);
2543 reply("NSMSG_UNKNOWN_NICK", nick);
2546 if (hi != ni->owner) {
2547 reply("NSMSG_NOT_YOUR_NICK", nick);
2550 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2555 static NICKSERV_FUNC(cmd_ounregnick)
2557 struct nick_info *ni;
2559 NICKSERV_MIN_PARMS(2);
2560 if (!(ni = get_nick_info(argv[1]))) {
2561 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2564 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2565 reply("MSG_USER_OUTRANKED", ni->nick);
2568 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2573 static NICKSERV_FUNC(cmd_unregister)
2575 struct handle_info *hi;
2578 NICKSERV_MIN_PARMS(2);
2579 hi = user->handle_info;
2582 if (checkpass(passwd, hi->passwd)) {
2583 nickserv_unregister_handle(hi, user);
2586 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2587 reply("NSMSG_PASSWORD_INVALID");
2592 static NICKSERV_FUNC(cmd_ounregister)
2594 struct handle_info *hi;
2596 NICKSERV_MIN_PARMS(2);
2597 if (!(hi = get_victim_oper(user, argv[1])))
2599 nickserv_unregister_handle(hi, user);
2603 static NICKSERV_FUNC(cmd_status)
2605 if (nickserv_conf.disable_nicks) {
2606 reply("NSMSG_GLOBAL_STATS_NONICK",
2607 dict_size(nickserv_handle_dict));
2609 if (user->handle_info) {
2611 struct nick_info *ni;
2612 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2613 reply("NSMSG_HANDLE_STATS", cnt);
2615 reply("NSMSG_HANDLE_NONE");
2617 reply("NSMSG_GLOBAL_STATS",
2618 dict_size(nickserv_handle_dict),
2619 dict_size(nickserv_nick_dict));
2624 static NICKSERV_FUNC(cmd_ghost)
2626 struct userNode *target;
2627 char reason[MAXLEN];
2629 NICKSERV_MIN_PARMS(2);
2630 if (!(target = GetUserH(argv[1]))) {
2631 reply("MSG_NICK_UNKNOWN", argv[1]);
2634 if (target == user) {
2635 reply("NSMSG_CANNOT_GHOST_SELF");
2638 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2639 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2642 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2643 DelUser(target, nickserv, 1, reason);
2644 reply("NSMSG_GHOST_KILLED", argv[1]);
2648 static NICKSERV_FUNC(cmd_vacation)
2650 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2651 reply("NSMSG_ON_VACATION");
2656 nickserv_saxdb_write(struct saxdb_context *ctx) {
2658 struct handle_info *hi;
2661 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2663 #ifdef WITH_PROTOCOL_BAHAMUT
2666 saxdb_start_record(ctx, iter_key(it), 0);
2667 if (hi->announcements != '?') {
2668 flags[0] = hi->announcements;
2670 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2673 struct handle_cookie *cookie = hi->cookie;
2676 switch (cookie->type) {
2677 case ACTIVATION: type = KEY_ACTIVATION; break;
2678 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2679 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2680 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2681 default: type = NULL; break;
2684 saxdb_start_record(ctx, KEY_COOKIE, 0);
2685 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2686 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2688 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2689 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2690 saxdb_end_record(ctx);
2694 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2696 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2698 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2702 for (ii=flen=0; handle_flags[ii]; ++ii)
2703 if (hi->flags & (1 << ii))
2704 flags[flen++] = handle_flags[ii];
2706 saxdb_write_string(ctx, KEY_FLAGS, flags);
2708 #ifdef WITH_PROTOCOL_BAHAMUT
2709 saxdb_write_int(ctx, KEY_ID, hi->id);
2712 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2713 if (hi->last_quit_host[0])
2714 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2715 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2716 if (hi->masks->used)
2717 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2719 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2721 struct string_list *slist;
2722 struct nick_info *ni;
2724 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2725 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2726 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2730 if (hi->opserv_level)
2731 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2732 if (hi->language != lang_C)
2733 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2734 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2735 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2736 if (hi->screen_width)
2737 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2738 if (hi->table_width)
2739 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2740 flags[0] = hi->userlist_style;
2742 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2743 saxdb_end_record(ctx);
2748 static handle_merge_func_t *handle_merge_func_list;
2749 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2752 reg_handle_merge_func(handle_merge_func_t func)
2754 if (handle_merge_func_used == handle_merge_func_size) {
2755 if (handle_merge_func_size) {
2756 handle_merge_func_size <<= 1;
2757 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2759 handle_merge_func_size = 8;
2760 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2763 handle_merge_func_list[handle_merge_func_used++] = func;
2766 static NICKSERV_FUNC(cmd_merge)
2768 struct handle_info *hi_from, *hi_to;
2769 struct userNode *last_user;
2770 struct userData *cList, *cListNext;
2771 unsigned int ii, jj, n;
2772 char buffer[MAXLEN];
2774 NICKSERV_MIN_PARMS(3);
2776 if (!(hi_from = get_victim_oper(user, argv[1])))
2778 if (!(hi_to = get_victim_oper(user, argv[2])))
2780 if (hi_to == hi_from) {
2781 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2785 for (n=0; n<handle_merge_func_used; n++)
2786 handle_merge_func_list[n](user, hi_to, hi_from);
2788 /* Append "from" handle's nicks to "to" handle's nick list. */
2790 struct nick_info *last_ni;
2791 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2792 last_ni->next = hi_from->nicks;
2794 while (hi_from->nicks) {
2795 hi_from->nicks->owner = hi_to;
2796 hi_from->nicks = hi_from->nicks->next;
2799 /* Merge the hostmasks. */
2800 for (ii=0; ii<hi_from->masks->used; ii++) {
2801 char *mask = hi_from->masks->list[ii];
2802 for (jj=0; jj<hi_to->masks->used; jj++)
2803 if (match_ircglobs(hi_to->masks->list[jj], mask))
2805 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2806 string_list_append(hi_to->masks, strdup(mask));
2809 /* Merge the lists of authed users. */
2811 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2812 last_user->next_authed = hi_from->users;
2814 hi_to->users = hi_from->users;
2816 /* Repoint the old "from" handle's users. */
2817 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2818 last_user->handle_info = hi_to;
2820 hi_from->users = NULL;
2822 /* Merge channel userlists. */
2823 for (cList=hi_from->channels; cList; cList=cListNext) {
2824 struct userData *cList2;
2825 cListNext = cList->u_next;
2826 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2827 if (cList->channel == cList2->channel)
2829 if (cList2 && (cList2->access >= cList->access)) {
2830 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);
2831 /* keep cList2 in hi_to; remove cList from hi_from */
2832 del_channel_user(cList, 1);
2835 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);
2836 /* remove the lower-ranking cList2 from hi_to */
2837 del_channel_user(cList2, 1);
2839 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
2841 /* cList needs to be moved from hi_from to hi_to */
2842 cList->handle = hi_to;
2843 /* Remove from linked list for hi_from */
2844 assert(!cList->u_prev);
2845 hi_from->channels = cList->u_next;
2847 cList->u_next->u_prev = cList->u_prev;
2848 /* Add to linked list for hi_to */
2849 cList->u_prev = NULL;
2850 cList->u_next = hi_to->channels;
2851 if (hi_to->channels)
2852 hi_to->channels->u_prev = cList;
2853 hi_to->channels = cList;
2857 /* Do they get an OpServ level promotion? */
2858 if (hi_from->opserv_level > hi_to->opserv_level)
2859 hi_to->opserv_level = hi_from->opserv_level;
2861 /* What about last seen time? */
2862 if (hi_from->lastseen > hi_to->lastseen)
2863 hi_to->lastseen = hi_from->lastseen;
2865 /* Does a fakehost carry over? (This intentionally doesn't set it
2866 * for users previously attached to hi_to. They'll just have to
2869 if (hi_from->fakehost && !hi_to->fakehost)
2870 hi_to->fakehost = strdup(hi_from->fakehost);
2872 /* Notify of success. */
2873 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2874 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2875 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2877 /* Unregister the "from" handle. */
2878 nickserv_unregister_handle(hi_from, NULL);
2883 struct nickserv_discrim {
2884 unsigned int limit, min_level, max_level;
2885 unsigned long flags_on, flags_off;
2886 time_t min_registered, max_registered;
2888 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2889 const char *nickmask;
2890 const char *hostmask;
2891 const char *handlemask;
2892 const char *emailmask;
2895 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2897 struct discrim_apply_info {
2898 struct nickserv_discrim *discrim;
2899 discrim_search_func func;
2900 struct userNode *source;
2901 unsigned int matched;
2904 static struct nickserv_discrim *
2905 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2908 struct nickserv_discrim *discrim;
2910 discrim = malloc(sizeof(*discrim));
2911 memset(discrim, 0, sizeof(*discrim));
2912 discrim->min_level = 0;
2913 discrim->max_level = ~0;
2914 discrim->limit = 50;
2915 discrim->min_registered = 0;
2916 discrim->max_registered = INT_MAX;
2917 discrim->lastseen = now;
2919 for (i=0; i<argc; i++) {
2920 if (i == argc - 1) {
2921 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2924 if (!irccasecmp(argv[i], "limit")) {
2925 discrim->limit = strtoul(argv[++i], NULL, 0);
2926 } else if (!irccasecmp(argv[i], "flags")) {
2927 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2928 } else if (!irccasecmp(argv[i], "registered")) {
2929 const char *cmp = argv[++i];
2930 if (cmp[0] == '<') {
2931 if (cmp[1] == '=') {
2932 discrim->min_registered = now - ParseInterval(cmp+2);
2934 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2936 } else if (cmp[0] == '=') {
2937 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2938 } else if (cmp[0] == '>') {
2939 if (cmp[1] == '=') {
2940 discrim->max_registered = now - ParseInterval(cmp+2);
2942 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2945 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2947 } else if (!irccasecmp(argv[i], "seen")) {
2948 discrim->lastseen = now - ParseInterval(argv[++i]);
2949 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2950 discrim->nickmask = argv[++i];
2951 } else if (!irccasecmp(argv[i], "hostmask")) {
2953 if (!irccasecmp(argv[i], "exact")) {
2954 if (i == argc - 1) {
2955 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2958 discrim->hostmask_type = EXACT;
2959 } else if (!irccasecmp(argv[i], "subset")) {
2960 if (i == argc - 1) {
2961 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2964 discrim->hostmask_type = SUBSET;
2965 } else if (!irccasecmp(argv[i], "superset")) {
2966 if (i == argc - 1) {
2967 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2970 discrim->hostmask_type = SUPERSET;
2971 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2972 if (i == argc - 1) {
2973 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2976 discrim->hostmask_type = LASTQUIT;
2979 discrim->hostmask_type = SUPERSET;
2981 discrim->hostmask = argv[++i];
2982 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2983 if (!irccasecmp(argv[++i], "*")) {
2984 discrim->handlemask = 0;
2986 discrim->handlemask = argv[i];
2988 } else if (!irccasecmp(argv[i], "email")) {
2989 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2990 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2992 } else if (!irccasecmp(argv[++i], "*")) {
2993 discrim->emailmask = 0;
2995 discrim->emailmask = argv[i];
2997 } else if (!irccasecmp(argv[i], "access")) {
2998 const char *cmp = argv[++i];
2999 if (cmp[0] == '<') {
3000 if (discrim->min_level == 0) discrim->min_level = 1;
3001 if (cmp[1] == '=') {
3002 discrim->max_level = strtoul(cmp+2, NULL, 0);
3004 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3006 } else if (cmp[0] == '=') {
3007 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3008 } else if (cmp[0] == '>') {
3009 if (cmp[1] == '=') {
3010 discrim->min_level = strtoul(cmp+2, NULL, 0);
3012 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3015 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3018 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3029 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3031 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3032 || (discrim->flags_off & hi->flags)
3033 || (discrim->min_registered > hi->registered)
3034 || (discrim->max_registered < hi->registered)
3035 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3036 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3037 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3038 || (discrim->min_level > hi->opserv_level)
3039 || (discrim->max_level < hi->opserv_level)) {
3042 if (discrim->hostmask) {
3044 for (i=0; i<hi->masks->used; i++) {
3045 const char *mask = hi->masks->list[i];
3046 if ((discrim->hostmask_type == SUBSET)
3047 && (match_ircglobs(discrim->hostmask, mask))) break;
3048 else if ((discrim->hostmask_type == EXACT)
3049 && !irccasecmp(discrim->hostmask, mask)) break;
3050 else if ((discrim->hostmask_type == SUPERSET)
3051 && (match_ircglobs(mask, discrim->hostmask))) break;
3052 else if ((discrim->hostmask_type == LASTQUIT)
3053 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3055 if (i==hi->masks->used) return 0;
3057 if (discrim->nickmask) {
3058 struct nick_info *nick = hi->nicks;
3060 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3063 if (!nick) return 0;
3069 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3071 dict_iterator_t it, next;
3072 unsigned int matched;
3074 for (it = dict_first(nickserv_handle_dict), matched = 0;
3075 it && (matched < discrim->limit);
3077 next = iter_next(it);
3078 if (nickserv_discrim_match(discrim, iter_data(it))) {
3079 dsf(source, iter_data(it));
3087 search_print_func(struct userNode *source, struct handle_info *match)
3089 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3093 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3098 search_unregister_func (struct userNode *source, struct handle_info *match)
3100 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3101 nickserv_unregister_handle(match, source);
3105 nickserv_sort_accounts_by_access(const void *a, const void *b)
3107 const struct handle_info *hi_a = *(const struct handle_info**)a;
3108 const struct handle_info *hi_b = *(const struct handle_info**)b;
3109 if (hi_a->opserv_level != hi_b->opserv_level)
3110 return hi_b->opserv_level - hi_a->opserv_level;
3111 return irccasecmp(hi_a->handle, hi_b->handle);
3115 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3117 struct handle_info_list hil;
3118 struct helpfile_table tbl;
3123 memset(&hil, 0, sizeof(hil));
3124 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3125 struct handle_info *hi = iter_data(it);
3126 if (hi->opserv_level)
3127 handle_info_list_append(&hil, hi);
3129 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3130 tbl.length = hil.used + 1;
3132 tbl.flags = TABLE_NO_FREE;
3133 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3134 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3137 for (ii = 0; ii < hil.used; ) {
3138 ary = malloc(tbl.width * sizeof(ary[0]));
3139 ary[0] = hil.list[ii]->handle;
3140 ary[1] = strtab(hil.list[ii]->opserv_level);
3141 tbl.contents[++ii] = ary;
3143 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3144 reply("MSG_MATCH_COUNT", hil.used);
3145 for (ii = 0; ii < hil.used; ii++)
3146 free(tbl.contents[ii]);
3151 static NICKSERV_FUNC(cmd_search)
3153 struct nickserv_discrim *discrim;
3154 discrim_search_func action;
3155 struct svccmd *subcmd;
3156 unsigned int matches;
3159 NICKSERV_MIN_PARMS(3);
3160 sprintf(buf, "search %s", argv[1]);
3161 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3162 if (!irccasecmp(argv[1], "print"))
3163 action = search_print_func;
3164 else if (!irccasecmp(argv[1], "count"))
3165 action = search_count_func;
3166 else if (!irccasecmp(argv[1], "unregister"))
3167 action = search_unregister_func;
3169 reply("NSMSG_INVALID_ACTION", argv[1]);
3173 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3176 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3180 if (action == search_print_func)
3181 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3182 else if (action == search_count_func)
3183 discrim->limit = INT_MAX;
3185 matches = nickserv_discrim_search(discrim, action, user);
3188 reply("MSG_MATCH_COUNT", matches);
3190 reply("MSG_NO_MATCHES");
3196 static MODCMD_FUNC(cmd_checkpass)
3198 struct handle_info *hi;
3200 NICKSERV_MIN_PARMS(3);
3201 if (!(hi = get_handle_info(argv[1]))) {
3202 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3205 if (checkpass(argv[2], hi->passwd))
3206 reply("CHECKPASS_YES");
3208 reply("CHECKPASS_NO");
3214 nickserv_db_read_handle(const char *handle, dict_t obj)
3217 struct string_list *masks, *slist;
3218 struct handle_info *hi;
3219 struct userNode *authed_users;
3220 struct userData *channels;
3221 unsigned long int id;
3225 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3226 id = str ? strtoul(str, NULL, 0) : 0;
3227 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3229 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3232 if ((hi = get_handle_info(handle))) {
3233 authed_users = hi->users;
3234 channels = hi->channels;
3236 hi->channels = NULL;
3237 dict_remove(nickserv_handle_dict, hi->handle);
3239 authed_users = NULL;
3242 hi = register_handle(handle, str, id);
3244 hi->users = authed_users;
3245 while (authed_users) {
3246 authed_users->handle_info = hi;
3247 authed_users = authed_users->next_authed;
3250 hi->channels = channels;
3251 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3252 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3253 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3254 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3255 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3256 hi->language = language_find(str ? str : "C");
3257 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3258 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3259 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3261 hi->infoline = strdup(str);
3262 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3263 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3264 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3265 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3266 /* We want to read the nicks even if disable_nicks is set. This is so
3267 * that we don't lose the nick data entirely. */
3268 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3270 for (ii=0; ii<slist->used; ii++)
3271 register_nick(slist->list[ii], hi);
3273 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3275 for (ii=0; str[ii]; ii++)
3276 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3278 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3279 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3280 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3281 hi->announcements = str ? str[0] : '?';
3282 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3283 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3284 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3285 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3286 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3288 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3290 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3291 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3293 nickserv_set_email_addr(hi, str);
3294 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3296 hi->epithet = strdup(str);
3297 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3299 hi->fakehost = strdup(str);
3300 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3302 const char *data, *type, *expires, *cookie_str;
3303 struct handle_cookie *cookie;
3305 cookie = calloc(1, sizeof(*cookie));
3306 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3307 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3308 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3309 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3310 if (!type || !expires || !cookie_str) {
3311 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3314 if (!irccasecmp(type, KEY_ACTIVATION))
3315 cookie->type = ACTIVATION;
3316 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3317 cookie->type = PASSWORD_CHANGE;
3318 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3319 cookie->type = EMAIL_CHANGE;
3320 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3321 cookie->type = ALLOWAUTH;
3323 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3326 cookie->expires = strtoul(expires, NULL, 0);
3327 if (cookie->expires < now)
3330 cookie->data = strdup(data);
3331 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3335 nickserv_bake_cookie(cookie);
3337 nickserv_free_cookie(cookie);
3342 nickserv_saxdb_read(dict_t db) {
3344 struct record_data *rd;
3346 for (it=dict_first(db); it; it=iter_next(it)) {
3348 nickserv_db_read_handle(iter_key(it), rd->d.object);
3353 static NICKSERV_FUNC(cmd_mergedb)
3355 struct timeval start, stop;
3358 NICKSERV_MIN_PARMS(2);
3359 gettimeofday(&start, NULL);
3360 if (!(db = parse_database(argv[1]))) {
3361 reply("NSMSG_DB_UNREADABLE", argv[1]);
3364 nickserv_saxdb_read(db);
3366 gettimeofday(&stop, NULL);
3367 stop.tv_sec -= start.tv_sec;
3368 stop.tv_usec -= start.tv_usec;
3369 if (stop.tv_usec < 0) {
3371 stop.tv_usec += 1000000;
3373 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3378 expire_handles(UNUSED_ARG(void *data))
3380 dict_iterator_t it, next;
3382 struct handle_info *hi;
3384 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3385 next = iter_next(it);
3387 if ((hi->opserv_level > 0)
3389 || HANDLE_FLAGGED(hi, FROZEN)
3390 || HANDLE_FLAGGED(hi, NODELETE)) {
3393 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3394 if ((now - hi->lastseen) > expiry) {
3395 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3396 nickserv_unregister_handle(hi, NULL);
3400 if (nickserv_conf.handle_expire_frequency)
3401 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3405 nickserv_load_dict(const char *fname)
3409 if (!(file = fopen(fname, "r"))) {
3410 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3413 while (!feof(file)) {
3414 fgets(line, sizeof(line), file);
3417 if (line[strlen(line)-1] == '\n')
3418 line[strlen(line)-1] = 0;
3419 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3422 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3425 static enum reclaim_action
3426 reclaim_action_from_string(const char *str) {
3428 return RECLAIM_NONE;
3429 else if (!irccasecmp(str, "warn"))
3430 return RECLAIM_WARN;
3431 else if (!irccasecmp(str, "svsnick"))
3432 return RECLAIM_SVSNICK;
3433 else if (!irccasecmp(str, "kill"))
3434 return RECLAIM_KILL;
3436 return RECLAIM_NONE;
3440 nickserv_conf_read(void)
3442 dict_t conf_node, child;
3446 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3447 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3450 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3452 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3453 if (nickserv_conf.valid_handle_regex_set)
3454 regfree(&nickserv_conf.valid_handle_regex);
3456 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3457 nickserv_conf.valid_handle_regex_set = !err;
3458 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3460 nickserv_conf.valid_handle_regex_set = 0;
3462 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3463 if (nickserv_conf.valid_nick_regex_set)
3464 regfree(&nickserv_conf.valid_nick_regex);
3466 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3467 nickserv_conf.valid_nick_regex_set = !err;
3468 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3470 nickserv_conf.valid_nick_regex_set = 0;
3472 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3474 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3475 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3476 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3477 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3478 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3479 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3480 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3481 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3482 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3483 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3484 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3485 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3486 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3487 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3488 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3489 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3490 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3491 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3492 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3493 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3494 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3495 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3496 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3497 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3498 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3500 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3501 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3502 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3504 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3505 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3506 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3508 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3509 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3510 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3511 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3512 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3513 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3514 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3515 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3516 if (!nickserv_conf.disable_nicks) {
3517 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3518 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3519 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3520 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3521 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3522 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3523 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3524 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3526 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3527 for (it=dict_first(child); it; it=iter_next(it)) {
3528 const char *key = iter_key(it), *value;
3532 if (!strncasecmp(key, "uc_", 3))
3533 flag = toupper(key[3]);
3534 else if (!strncasecmp(key, "lc_", 3))
3535 flag = tolower(key[3]);
3539 if ((pos = handle_inverse_flags[flag])) {
3540 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3541 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3544 if (nickserv_conf.weak_password_dict)
3545 dict_delete(nickserv_conf.weak_password_dict);
3546 nickserv_conf.weak_password_dict = dict_new();
3547 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3548 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3549 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3550 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3552 nickserv_load_dict(str);
3553 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3554 if (nickserv && str)
3555 NickChange(nickserv, str, 0);
3556 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3557 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3558 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3559 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3560 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3561 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3562 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3563 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3564 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3565 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3566 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3567 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3568 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3569 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3570 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3571 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3572 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3573 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3574 str = conf_get_data("server/network", RECDB_QSTRING);
3575 nickserv_conf.network_name = str ? str : "some IRC network";
3576 if (!nickserv_conf.auth_policer_params) {
3577 nickserv_conf.auth_policer_params = policer_params_new();
3578 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3579 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3581 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3582 for (it=dict_first(child); it; it=iter_next(it))
3583 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3587 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3589 char newnick[NICKLEN+1];
3598 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3600 case RECLAIM_SVSNICK:
3602 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3603 } while (GetUserH(newnick));
3604 irc_svsnick(nickserv, user, newnick);
3607 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3608 irc_kill(nickserv, user, msg);
3614 nickserv_reclaim_p(void *data) {
3615 struct userNode *user = data;
3616 struct nick_info *ni = get_nick_info(user->nick);
3618 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3622 check_user_nick(struct userNode *user) {
3623 struct nick_info *ni;
3624 user->modes &= ~FLAGS_REGNICK;
3625 if (!(ni = get_nick_info(user->nick)))
3627 if (user->handle_info == ni->owner) {
3628 user->modes |= FLAGS_REGNICK;
3632 if (nickserv_conf.warn_nick_owned)
3633 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3634 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3636 if (nickserv_conf.auto_reclaim_delay)
3637 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3639 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3644 handle_new_user(struct userNode *user)
3646 return check_user_nick(user);
3650 handle_account(struct userNode *user, const char *stamp)
3652 struct handle_info *hi;
3654 #ifdef WITH_PROTOCOL_P10
3655 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3657 hi = dict_find(nickserv_id_dict, stamp, NULL);
3661 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3664 set_user_handle_info(user, hi, 0);
3666 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3671 handle_nick_change(struct userNode *user, const char *old_nick)
3673 struct handle_info *hi;
3675 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3676 dict_remove(nickserv_allow_auth_dict, old_nick);
3677 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3679 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3680 check_user_nick(user);
3684 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3686 dict_remove(nickserv_allow_auth_dict, user->nick);
3687 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3688 set_user_handle_info(user, NULL, 0);
3691 static struct modcmd *
3692 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3694 if (min_level > 0) {
3696 sprintf(buf, "%u", min_level);
3697 if (must_be_qualified) {
3698 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3700 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3702 } else if (min_level == 0) {
3703 if (must_be_qualified) {
3704 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3706 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3709 if (must_be_qualified) {
3710 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3712 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3718 nickserv_db_cleanup(void)
3720 unreg_del_user_func(nickserv_remove_user);
3721 userList_clean(&curr_helpers);
3722 policer_params_delete(nickserv_conf.auth_policer_params);
3723 dict_delete(nickserv_handle_dict);
3724 dict_delete(nickserv_nick_dict);
3725 dict_delete(nickserv_opt_dict);
3726 dict_delete(nickserv_allow_auth_dict);
3727 dict_delete(nickserv_email_dict);
3728 dict_delete(nickserv_id_dict);
3729 dict_delete(nickserv_conf.weak_password_dict);
3730 free(auth_func_list);
3731 free(unreg_func_list);
3733 free(allowauth_func_list);
3734 free(handle_merge_func_list);
3735 free(failpw_func_list);
3736 if (nickserv_conf.valid_handle_regex_set)
3737 regfree(&nickserv_conf.valid_handle_regex);
3738 if (nickserv_conf.valid_nick_regex_set)
3739 regfree(&nickserv_conf.valid_nick_regex);
3743 init_nickserv(const char *nick)
3746 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3747 reg_new_user_func(handle_new_user);
3748 reg_nick_change_func(handle_nick_change);
3749 reg_del_user_func(nickserv_remove_user);
3750 reg_account_func(handle_account);
3752 /* set up handle_inverse_flags */
3753 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3754 for (i=0; handle_flags[i]; i++) {
3755 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3756 flag_access_levels[i] = 0;
3759 conf_register_reload(nickserv_conf_read);
3760 nickserv_opt_dict = dict_new();
3761 nickserv_email_dict = dict_new();
3762 dict_set_free_keys(nickserv_email_dict, free);
3763 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3765 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3766 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3767 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3768 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3769 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3770 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3771 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3772 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3773 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3774 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3775 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3776 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3777 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3778 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3779 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3780 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3781 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3782 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3783 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3784 if (!nickserv_conf.disable_nicks) {
3785 /* nick management commands */
3786 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3787 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3788 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3789 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3790 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3791 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3793 if (nickserv_conf.email_enabled) {
3794 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3795 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3796 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3797 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3798 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3800 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3801 /* miscellaneous commands */
3802 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3803 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3804 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3805 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3806 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3808 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3809 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3810 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3811 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3812 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3813 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3814 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3815 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3816 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3817 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3818 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3819 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3820 if (nickserv_conf.titlehost_suffix) {
3821 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
3822 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
3824 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3825 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3826 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3828 nickserv_handle_dict = dict_new();
3829 dict_set_free_keys(nickserv_handle_dict, free);
3830 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3832 nickserv_id_dict = dict_new();
3833 dict_set_free_keys(nickserv_id_dict, free);
3835 nickserv_nick_dict = dict_new();
3836 dict_set_free_data(nickserv_nick_dict, free);
3838 nickserv_allow_auth_dict = dict_new();
3840 userList_init(&curr_helpers);
3843 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
3844 nickserv = AddService(nick, modes ? modes : NULL, "Nick Services", NULL);
3845 nickserv_service = service_register(nickserv);
3847 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3848 reg_exit_func(nickserv_db_cleanup);
3849 if(nickserv_conf.handle_expire_frequency)
3850 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3851 message_register_table(msgtab);