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)
1115 struct handle_info *hi;
1116 const char *email_addr, *password;
1119 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1120 /* Require the first handle registered to belong to someone +o. */
1121 reply("NSMSG_REQUIRE_OPER");
1125 if (user->handle_info) {
1126 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1130 if (IsRegistering(user)) {
1131 reply("NSMSG_ALREADY_REGISTERING");
1135 if (IsStamped(user)) {
1136 /* Unauthenticated users might still have been stamped
1137 previously and could therefore have a hidden host;
1138 do not allow them to register a new account. */
1139 reply("NSMSG_STAMPED_REGISTER");
1143 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1145 if (!is_valid_handle(argv[1])) {
1146 reply("NSMSG_BAD_HANDLE", argv[1]);
1150 if ((argc >= 4) && nickserv_conf.email_enabled) {
1151 struct handle_info_list *hil;
1154 /* Remember email address. */
1155 email_addr = argv[3];
1157 /* Check that the email address looks valid.. */
1158 if (!is_valid_email_addr(email_addr)) {
1159 reply("NSMSG_BAD_EMAIL_ADDR");
1163 /* .. and that we are allowed to send to it. */
1164 if ((str = sendmail_prohibited_address(email_addr))) {
1165 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1169 /* If we do email verify, make sure we don't spam the address. */
1170 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1172 for (nn=0; nn<hil->used; nn++) {
1173 if (hil->list[nn]->cookie) {
1174 reply("NSMSG_EMAIL_UNACTIVATED");
1178 if (hil->used >= nickserv_conf.handles_per_email) {
1179 reply("NSMSG_EMAIL_OVERUSED");
1192 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1194 /* Add any masks they should get. */
1195 if (nickserv_conf.default_hostmask) {
1196 string_list_append(hi->masks, strdup("*@*"));
1198 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1199 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1200 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1203 /* If they're the first to register, give them level 1000. */
1204 if (dict_size(nickserv_handle_dict) == 1) {
1205 hi->opserv_level = 1000;
1206 reply("NSMSG_ROOT_HANDLE", argv[1]);
1209 /* Set their email address. */
1211 nickserv_set_email_addr(hi, email_addr);
1213 /* If they need to do email verification, tell them. */
1215 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1217 /* Set registering flag.. */
1218 user->modes |= FLAGS_REGISTERING;
1223 static NICKSERV_FUNC(cmd_oregister)
1226 struct userNode *settee;
1227 struct handle_info *hi;
1229 NICKSERV_MIN_PARMS(4);
1231 if (!is_valid_handle(argv[1])) {
1232 reply("NSMSG_BAD_HANDLE", argv[1]);
1236 if (strchr(argv[3], '@')) {
1237 mask = canonicalize_hostmask(strdup(argv[3]));
1239 settee = GetUserH(argv[4]);
1241 reply("MSG_NICK_UNKNOWN", argv[4]);
1248 } else if ((settee = GetUserH(argv[3]))) {
1249 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1251 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1254 if (settee && settee->handle_info) {
1255 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1259 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1263 string_list_append(hi->masks, mask);
1267 static NICKSERV_FUNC(cmd_handleinfo)
1270 unsigned int i, pos=0, herelen;
1271 struct userNode *target, *next_un;
1272 struct handle_info *hi;
1273 const char *nsmsg_none;
1276 if (!(hi = user->handle_info)) {
1277 reply("NSMSG_MUST_AUTH");
1280 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1284 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1285 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1286 #ifdef WITH_PROTOCOL_BAHAMUT
1287 reply("NSMSG_HANDLEINFO_ID", hi->id);
1289 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1292 intervalString(buff, now - hi->lastseen, user->handle_info);
1293 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1295 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1298 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1299 if (HANDLE_FLAGGED(hi, FROZEN))
1300 reply("NSMSG_HANDLEINFO_VACATION");
1302 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1303 struct do_not_register *dnr;
1304 if ((dnr = chanserv_is_dnr(NULL, hi)))
1305 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1306 if (!oper_outranks(user, hi))
1308 } else if (hi != user->handle_info)
1311 if (nickserv_conf.email_enabled)
1312 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1316 switch (hi->cookie->type) {
1317 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1318 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1319 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1320 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1321 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1327 unsigned long flen = 1;
1328 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1330 for (i=0, flen=1; handle_flags[i]; i++)
1331 if (hi->flags & 1 << i)
1332 flags[flen++] = handle_flags[i];
1334 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1336 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1339 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1340 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1341 || (hi->opserv_level > 0)) {
1342 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1346 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1348 if (hi->last_quit_host[0])
1349 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1351 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1353 if (nickserv_conf.disable_nicks) {
1354 /* nicks disabled; don't show anything about registered nicks */
1355 } else if (hi->nicks) {
1356 struct nick_info *ni, *next_ni;
1357 for (ni = hi->nicks; ni; ni = next_ni) {
1358 herelen = strlen(ni->nick);
1359 if (pos + herelen + 1 > ArrayLength(buff)) {
1361 goto print_nicks_buff;
1365 memcpy(buff+pos, ni->nick, herelen);
1366 pos += herelen; buff[pos++] = ' ';
1370 reply("NSMSG_HANDLEINFO_NICKS", buff);
1375 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1378 if (hi->masks->used) {
1379 for (i=0; i < hi->masks->used; i++) {
1380 herelen = strlen(hi->masks->list[i]);
1381 if (pos + herelen + 1 > ArrayLength(buff)) {
1383 goto print_mask_buff;
1385 memcpy(buff+pos, hi->masks->list[i], herelen);
1386 pos += herelen; buff[pos++] = ' ';
1387 if (i+1 == hi->masks->used) {
1390 reply("NSMSG_HANDLEINFO_MASKS", buff);
1395 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1399 struct userData *channel, *next;
1402 for (channel = hi->channels; channel; channel = next) {
1403 next = channel->u_next;
1404 name = channel->channel->channel->name;
1405 herelen = strlen(name);
1406 if (pos + herelen + 7 > ArrayLength(buff)) {
1408 goto print_chans_buff;
1410 if (IsUserSuspended(channel))
1412 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1416 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1421 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1424 for (target = hi->users; target; target = next_un) {
1425 herelen = strlen(target->nick);
1426 if (pos + herelen + 1 > ArrayLength(buff)) {
1428 goto print_cnick_buff;
1430 next_un = target->next_authed;
1432 memcpy(buff+pos, target->nick, herelen);
1433 pos += herelen; buff[pos++] = ' ';
1437 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1445 static NICKSERV_FUNC(cmd_userinfo)
1447 struct userNode *target;
1449 NICKSERV_MIN_PARMS(2);
1450 if (!(target = GetUserH(argv[1]))) {
1451 reply("MSG_NICK_UNKNOWN", argv[1]);
1454 if (target->handle_info)
1455 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1457 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1461 static NICKSERV_FUNC(cmd_nickinfo)
1463 struct nick_info *ni;
1465 NICKSERV_MIN_PARMS(2);
1466 if (!(ni = get_nick_info(argv[1]))) {
1467 reply("MSG_NICK_UNKNOWN", argv[1]);
1470 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1474 static NICKSERV_FUNC(cmd_rename_handle)
1476 struct handle_info *hi;
1477 char msgbuf[MAXLEN], *old_handle;
1480 NICKSERV_MIN_PARMS(3);
1481 if (!(hi = get_victim_oper(user, argv[1])))
1483 if (!is_valid_handle(argv[2])) {
1484 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1487 if (get_handle_info(argv[2])) {
1488 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1492 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1493 hi->handle = strdup(argv[2]);
1494 dict_insert(nickserv_handle_dict, hi->handle, hi);
1495 for (nn=0; nn<rf_list_used; nn++)
1496 rf_list[nn](hi, old_handle);
1497 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1498 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1499 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1504 static failpw_func_t *failpw_func_list;
1505 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1508 reg_failpw_func(failpw_func_t func)
1510 if (failpw_func_used == failpw_func_size) {
1511 if (failpw_func_size) {
1512 failpw_func_size <<= 1;
1513 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1515 failpw_func_size = 8;
1516 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1519 failpw_func_list[failpw_func_used++] = func;
1522 static NICKSERV_FUNC(cmd_auth)
1524 int pw_arg, used, maxlogins;
1525 struct handle_info *hi;
1527 struct userNode *other;
1529 if (user->handle_info) {
1530 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1533 if (IsStamped(user)) {
1534 /* Unauthenticated users might still have been stamped
1535 previously and could therefore have a hidden host;
1536 do not allow them to authenticate. */
1537 reply("NSMSG_STAMPED_AUTH");
1541 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1543 } else if (argc == 2) {
1544 if (nickserv_conf.disable_nicks) {
1545 if (!(hi = get_handle_info(user->nick))) {
1546 reply("NSMSG_HANDLE_NOT_FOUND");
1550 /* try to look up their handle from their nick */
1551 struct nick_info *ni;
1552 ni = get_nick_info(user->nick);
1554 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1561 reply("MSG_MISSING_PARAMS", argv[0]);
1562 svccmd_send_help(user, nickserv, cmd);
1566 reply("NSMSG_HANDLE_NOT_FOUND");
1569 /* Responses from here on look up the language used by the handle they asked about. */
1570 passwd = argv[pw_arg];
1571 if (!valid_user_for(user, hi)) {
1572 if (hi->email_addr && nickserv_conf.email_enabled)
1573 send_message_type(4, user, cmd->parent->bot,
1574 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1577 send_message_type(4, user, cmd->parent->bot,
1578 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1580 argv[pw_arg] = "BADMASK";
1583 if (!checkpass(passwd, hi->passwd)) {
1585 send_message_type(4, user, cmd->parent->bot,
1586 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1587 argv[pw_arg] = "BADPASS";
1588 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1589 if (nickserv_conf.autogag_enabled) {
1590 if (!user->auth_policer.params) {
1591 user->auth_policer.last_req = now;
1592 user->auth_policer.params = nickserv_conf.auth_policer_params;
1594 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1596 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1597 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1598 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1600 argv[pw_arg] = "GAGGED";
1605 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1606 send_message_type(4, user, cmd->parent->bot,
1607 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1608 argv[pw_arg] = "SUSPENDED";
1611 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1612 for (used = 0, other = hi->users; other; other = other->next_authed) {
1613 if (++used >= maxlogins) {
1614 send_message_type(4, user, cmd->parent->bot,
1615 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1617 argv[pw_arg] = "MAXLOGINS";
1622 set_user_handle_info(user, hi, 1);
1623 if (nickserv_conf.email_required && !hi->email_addr)
1624 reply("NSMSG_PLEASE_SET_EMAIL");
1625 if (!is_secure_password(hi->handle, passwd, NULL))
1626 reply("NSMSG_WEAK_PASSWORD");
1627 if (hi->passwd[0] != '$')
1628 cryptpass(passwd, hi->passwd);
1629 reply("NSMSG_AUTH_SUCCESS");
1630 argv[pw_arg] = "****";
1634 static allowauth_func_t *allowauth_func_list;
1635 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1638 reg_allowauth_func(allowauth_func_t func)
1640 if (allowauth_func_used == allowauth_func_size) {
1641 if (allowauth_func_size) {
1642 allowauth_func_size <<= 1;
1643 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1645 allowauth_func_size = 8;
1646 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1649 allowauth_func_list[allowauth_func_used++] = func;
1652 static NICKSERV_FUNC(cmd_allowauth)
1654 struct userNode *target;
1655 struct handle_info *hi;
1658 NICKSERV_MIN_PARMS(2);
1659 if (!(target = GetUserH(argv[1]))) {
1660 reply("MSG_NICK_UNKNOWN", argv[1]);
1663 if (target->handle_info) {
1664 reply("NSMSG_USER_PREV_AUTH", target->nick);
1667 if (IsStamped(target)) {
1668 /* Unauthenticated users might still have been stamped
1669 previously and could therefore have a hidden host;
1670 do not allow them to authenticate to an account. */
1671 reply("NSMSG_USER_PREV_STAMP", target->nick);
1676 else if (!(hi = get_handle_info(argv[2]))) {
1677 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1681 if (hi->opserv_level > user->handle_info->opserv_level) {
1682 reply("MSG_USER_OUTRANKED", hi->handle);
1685 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1686 || (hi->opserv_level > 0))
1687 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1688 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1691 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1692 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1693 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1694 if (nickserv_conf.email_enabled)
1695 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1697 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1698 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1700 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1702 for (n=0; n<allowauth_func_used; n++)
1703 allowauth_func_list[n](user, target, hi);
1707 static NICKSERV_FUNC(cmd_authcookie)
1709 struct handle_info *hi;
1711 NICKSERV_MIN_PARMS(2);
1712 if (user->handle_info) {
1713 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1716 if (IsStamped(user)) {
1717 /* Unauthenticated users might still have been stamped
1718 previously and could therefore have a hidden host;
1719 do not allow them to authenticate to an account. */
1720 reply("NSMSG_STAMPED_AUTHCOOKIE");
1723 if (!(hi = get_handle_info(argv[1]))) {
1724 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1727 if (!hi->email_addr) {
1728 reply("MSG_SET_EMAIL_ADDR");
1731 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1735 static NICKSERV_FUNC(cmd_delcookie)
1737 struct handle_info *hi;
1739 hi = user->handle_info;
1741 reply("NSMSG_NO_COOKIE");
1744 switch (hi->cookie->type) {
1747 reply("NSMSG_MUST_TIME_OUT");
1750 nickserv_eat_cookie(hi->cookie);
1751 reply("NSMSG_ATE_COOKIE");
1757 static NICKSERV_FUNC(cmd_resetpass)
1759 struct handle_info *hi;
1760 char crypted[MD5_CRYPT_LENGTH];
1762 NICKSERV_MIN_PARMS(3);
1763 if (user->handle_info) {
1764 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1767 if (IsStamped(user)) {
1768 /* Unauthenticated users might still have been stamped
1769 previously and could therefore have a hidden host;
1770 do not allow them to activate an account. */
1771 reply("NSMSG_STAMPED_RESETPASS");
1774 if (!(hi = get_handle_info(argv[1]))) {
1775 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1778 if (!hi->email_addr) {
1779 reply("MSG_SET_EMAIL_ADDR");
1782 cryptpass(argv[2], crypted);
1784 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1788 static NICKSERV_FUNC(cmd_cookie)
1790 struct handle_info *hi;
1793 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1796 NICKSERV_MIN_PARMS(3);
1797 if (!(hi = get_handle_info(argv[1]))) {
1798 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1804 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1805 reply("NSMSG_HANDLE_SUSPENDED");
1810 reply("NSMSG_NO_COOKIE");
1814 /* Check validity of operation before comparing cookie to
1815 * prohibit guessing by authed users. */
1816 if (user->handle_info
1817 && (hi->cookie->type != EMAIL_CHANGE)
1818 && (hi->cookie->type != PASSWORD_CHANGE)) {
1819 reply("NSMSG_CANNOT_COOKIE");
1823 if (strcmp(cookie, hi->cookie->cookie)) {
1824 reply("NSMSG_BAD_COOKIE");
1828 switch (hi->cookie->type) {
1830 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1831 set_user_handle_info(user, hi, 1);
1832 reply("NSMSG_HANDLE_ACTIVATED");
1834 case PASSWORD_CHANGE:
1835 set_user_handle_info(user, hi, 1);
1836 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1837 reply("NSMSG_PASSWORD_CHANGED");
1840 nickserv_set_email_addr(hi, hi->cookie->data);
1841 reply("NSMSG_EMAIL_CHANGED");
1844 set_user_handle_info(user, hi, 1);
1845 reply("NSMSG_AUTH_SUCCESS");
1848 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1849 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1853 nickserv_eat_cookie(hi->cookie);
1858 static NICKSERV_FUNC(cmd_oregnick) {
1860 struct handle_info *target;
1861 struct nick_info *ni;
1863 NICKSERV_MIN_PARMS(3);
1864 if (!(target = modcmd_get_handle_info(user, argv[1])))
1867 if (!is_registerable_nick(nick)) {
1868 reply("NSMSG_BAD_NICK", nick);
1871 ni = dict_find(nickserv_nick_dict, nick, NULL);
1873 reply("NSMSG_NICK_EXISTS", nick);
1876 register_nick(nick, target);
1877 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1881 static NICKSERV_FUNC(cmd_regnick) {
1883 struct nick_info *ni;
1885 if (!is_registerable_nick(user->nick)) {
1886 reply("NSMSG_BAD_NICK", user->nick);
1889 /* count their nicks, see if it's too many */
1890 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1891 if (n >= nickserv_conf.nicks_per_handle) {
1892 reply("NSMSG_TOO_MANY_NICKS");
1895 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1897 reply("NSMSG_NICK_EXISTS", user->nick);
1900 register_nick(user->nick, user->handle_info);
1901 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1905 static NICKSERV_FUNC(cmd_pass)
1907 struct handle_info *hi;
1908 const char *old_pass, *new_pass;
1910 NICKSERV_MIN_PARMS(3);
1911 hi = user->handle_info;
1915 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1916 if (!checkpass(old_pass, hi->passwd)) {
1917 argv[1] = "BADPASS";
1918 reply("NSMSG_PASSWORD_INVALID");
1921 cryptpass(new_pass, hi->passwd);
1923 reply("NSMSG_PASS_SUCCESS");
1928 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1931 char *new_mask = canonicalize_hostmask(strdup(mask));
1932 for (i=0; i<hi->masks->used; i++) {
1933 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1934 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1939 string_list_append(hi->masks, new_mask);
1940 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
1944 static NICKSERV_FUNC(cmd_addmask)
1947 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1948 int res = nickserv_addmask(user, user->handle_info, mask);
1952 if (!is_gline(argv[1])) {
1953 reply("NSMSG_MASK_INVALID", argv[1]);
1956 return nickserv_addmask(user, user->handle_info, argv[1]);
1960 static NICKSERV_FUNC(cmd_oaddmask)
1962 struct handle_info *hi;
1964 NICKSERV_MIN_PARMS(3);
1965 if (!(hi = get_victim_oper(user, argv[1])))
1967 return nickserv_addmask(user, hi, argv[2]);
1971 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
1974 for (i=0; i<hi->masks->used; i++) {
1975 if (!strcmp(del_mask, hi->masks->list[i])) {
1976 char *old_mask = hi->masks->list[i];
1977 if (hi->masks->used == 1) {
1978 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
1981 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
1982 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
1987 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
1991 static NICKSERV_FUNC(cmd_delmask)
1993 NICKSERV_MIN_PARMS(2);
1994 return nickserv_delmask(user, user->handle_info, argv[1]);
1997 static NICKSERV_FUNC(cmd_odelmask)
1999 struct handle_info *hi;
2000 NICKSERV_MIN_PARMS(3);
2001 if (!(hi = get_victim_oper(user, argv[1])))
2003 return nickserv_delmask(user, hi, argv[2]);
2007 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2008 unsigned int nn, add = 1, pos;
2009 unsigned long added, removed, flag;
2011 for (added=removed=nn=0; str[nn]; nn++) {
2013 case '+': add = 1; break;
2014 case '-': add = 0; break;
2016 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2017 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2020 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2021 /* cheesy avoidance of looking up the flag name.. */
2022 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2025 flag = 1 << (pos - 1);
2027 added |= flag, removed &= ~flag;
2029 removed |= flag, added &= ~flag;
2034 *premoved = removed;
2039 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2041 unsigned long before, after, added, removed;
2042 struct userNode *uNode;
2044 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2045 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2047 hi->flags = (hi->flags | added) & ~removed;
2048 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2050 /* Strip helping flag if they're only a support helper and not
2051 * currently in #support. */
2052 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2053 struct channelList *schannels;
2055 schannels = chanserv_support_channels();
2056 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2057 for (ii = 0; ii < schannels->used; ++ii)
2058 if (GetUserMode(schannels->list[ii], uNode))
2060 if (ii < schannels->used)
2064 HANDLE_CLEAR_FLAG(hi, HELPING);
2067 if (after && !before) {
2068 /* Add user to current helper list. */
2069 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2070 userList_append(&curr_helpers, uNode);
2071 } else if (!after && before) {
2072 /* Remove user from current helper list. */
2073 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2074 userList_remove(&curr_helpers, uNode);
2081 set_list(struct userNode *user, struct handle_info *hi, int override)
2085 char *set_display[] = {
2086 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2087 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2090 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2092 /* Do this so options are presented in a consistent order. */
2093 for (i = 0; i < ArrayLength(set_display); ++i)
2094 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2095 opt(user, hi, override, 0, NULL);
2098 static NICKSERV_FUNC(cmd_set)
2100 struct handle_info *hi;
2103 hi = user->handle_info;
2105 set_list(user, hi, 0);
2108 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2109 reply("NSMSG_INVALID_OPTION", argv[1]);
2112 return opt(user, hi, 0, argc-1, argv+1);
2115 static NICKSERV_FUNC(cmd_oset)
2117 struct handle_info *hi;
2120 NICKSERV_MIN_PARMS(2);
2122 if (!(hi = get_victim_oper(user, argv[1])))
2126 set_list(user, hi, 0);
2130 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2131 reply("NSMSG_INVALID_OPTION", argv[2]);
2135 return opt(user, hi, 1, argc-2, argv+2);
2138 static OPTION_FUNC(opt_info)
2142 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2144 hi->infoline = NULL;
2146 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2150 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2151 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2155 static OPTION_FUNC(opt_width)
2158 hi->screen_width = strtoul(argv[1], NULL, 0);
2160 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2161 hi->screen_width = MIN_LINE_SIZE;
2162 else if (hi->screen_width > MAX_LINE_SIZE)
2163 hi->screen_width = MAX_LINE_SIZE;
2165 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2169 static OPTION_FUNC(opt_tablewidth)
2172 hi->table_width = strtoul(argv[1], NULL, 0);
2174 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2175 hi->table_width = MIN_LINE_SIZE;
2176 else if (hi->screen_width > MAX_LINE_SIZE)
2177 hi->table_width = MAX_LINE_SIZE;
2179 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2183 static OPTION_FUNC(opt_color)
2186 if (enabled_string(argv[1]))
2187 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2188 else if (disabled_string(argv[1]))
2189 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2191 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2196 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2200 static OPTION_FUNC(opt_privmsg)
2203 if (enabled_string(argv[1]))
2204 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2205 else if (disabled_string(argv[1]))
2206 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2208 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2213 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2217 static OPTION_FUNC(opt_style)
2222 if (!irccasecmp(argv[1], "Zoot"))
2223 hi->userlist_style = HI_STYLE_ZOOT;
2224 else if (!irccasecmp(argv[1], "def"))
2225 hi->userlist_style = HI_STYLE_DEF;
2228 switch (hi->userlist_style) {
2237 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2241 static OPTION_FUNC(opt_announcements)
2246 if (enabled_string(argv[1]))
2247 hi->announcements = 'y';
2248 else if (disabled_string(argv[1]))
2249 hi->announcements = 'n';
2250 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2251 hi->announcements = '?';
2253 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2258 switch (hi->announcements) {
2259 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2260 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2261 case '?': choice = "default"; break;
2262 default: choice = "unknown"; break;
2264 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2268 static OPTION_FUNC(opt_password)
2271 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2276 cryptpass(argv[1], hi->passwd);
2278 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2282 static OPTION_FUNC(opt_flags)
2285 unsigned int ii, flen;
2288 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2293 nickserv_apply_flags(user, hi, argv[1]);
2295 for (ii = flen = 0; handle_flags[ii]; ii++)
2296 if (hi->flags & (1 << ii))
2297 flags[flen++] = handle_flags[ii];
2300 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2302 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2306 static OPTION_FUNC(opt_email)
2310 if (!is_valid_email_addr(argv[1])) {
2311 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2314 if ((str = sendmail_prohibited_address(argv[1]))) {
2315 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2318 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2319 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2321 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2323 nickserv_set_email_addr(hi, argv[1]);
2325 nickserv_eat_cookie(hi->cookie);
2326 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2329 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2333 static OPTION_FUNC(opt_maxlogins)
2335 unsigned char maxlogins;
2337 maxlogins = strtoul(argv[1], NULL, 0);
2338 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2339 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2342 hi->maxlogins = maxlogins;
2344 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2345 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2349 static OPTION_FUNC(opt_language)
2351 struct language *lang;
2353 lang = language_find(argv[1]);
2354 if (irccasecmp(lang->name, argv[1]))
2355 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2356 hi->language = lang;
2358 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2363 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2364 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2366 if ((user->handle_info->opserv_level < target->opserv_level)
2367 || ((user->handle_info->opserv_level == target->opserv_level)
2368 && (user->handle_info->opserv_level < 1000))) {
2369 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2372 if ((user->handle_info->opserv_level < new_level)
2373 || ((user->handle_info->opserv_level == new_level)
2374 && (user->handle_info->opserv_level < 1000))) {
2375 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2378 if (user->handle_info == target) {
2379 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2382 if (target->opserv_level == new_level)
2384 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2385 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2386 target->opserv_level = new_level;
2390 static OPTION_FUNC(opt_level)
2395 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2399 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2400 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2404 static OPTION_FUNC(opt_epithet)
2407 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2411 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2412 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2415 if ((epithet[0] == '*') && !epithet[1])
2418 hi->epithet = strdup(epithet);
2422 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2424 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2428 static OPTION_FUNC(opt_title)
2433 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2437 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2439 if (strchr(title, '.')) {
2440 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2443 if ((strlen(user->handle_info->handle) + strlen(title) +
2444 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2445 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2450 if (!strcmp(title, "*")) {
2451 hi->fakehost = NULL;
2453 hi->fakehost = malloc(strlen(title)+2);
2454 hi->fakehost[0] = '.';
2455 strcpy(hi->fakehost+1, title);
2458 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2459 title = hi->fakehost + 1;
2463 title = user_find_message(user, "MSG_NONE");
2464 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2468 static OPTION_FUNC(opt_fakehost)
2473 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2477 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2479 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2480 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2484 if (!strcmp(fake, "*"))
2485 hi->fakehost = NULL;
2487 hi->fakehost = strdup(fake);
2488 fake = hi->fakehost;
2491 fake = generate_fakehost(hi);
2493 fake = user_find_message(user, "MSG_NONE");
2494 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2498 static NICKSERV_FUNC(cmd_reclaim)
2500 struct handle_info *hi;
2501 struct nick_info *ni;
2502 struct userNode *victim;
2504 NICKSERV_MIN_PARMS(2);
2505 hi = user->handle_info;
2506 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2508 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2511 if (ni->owner != user->handle_info) {
2512 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2515 victim = GetUserH(ni->nick);
2517 reply("MSG_NICK_UNKNOWN", ni->nick);
2520 if (victim == user) {
2521 reply("NSMSG_NICK_USER_YOU");
2524 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2525 switch (nickserv_conf.reclaim_action) {
2526 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2527 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2528 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2529 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2534 static NICKSERV_FUNC(cmd_unregnick)
2537 struct handle_info *hi;
2538 struct nick_info *ni;
2540 hi = user->handle_info;
2541 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2542 ni = dict_find(nickserv_nick_dict, nick, NULL);
2544 reply("NSMSG_UNKNOWN_NICK", nick);
2547 if (hi != ni->owner) {
2548 reply("NSMSG_NOT_YOUR_NICK", nick);
2551 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2556 static NICKSERV_FUNC(cmd_ounregnick)
2558 struct nick_info *ni;
2560 NICKSERV_MIN_PARMS(2);
2561 if (!(ni = get_nick_info(argv[1]))) {
2562 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2565 if (!oper_outranks(user, ni->owner))
2567 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2572 static NICKSERV_FUNC(cmd_unregister)
2574 struct handle_info *hi;
2577 NICKSERV_MIN_PARMS(2);
2578 hi = user->handle_info;
2581 if (checkpass(passwd, hi->passwd)) {
2582 nickserv_unregister_handle(hi, user);
2585 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2586 reply("NSMSG_PASSWORD_INVALID");
2591 static NICKSERV_FUNC(cmd_ounregister)
2593 struct handle_info *hi;
2595 NICKSERV_MIN_PARMS(2);
2596 if (!(hi = get_victim_oper(user, argv[1])))
2598 nickserv_unregister_handle(hi, user);
2602 static NICKSERV_FUNC(cmd_status)
2604 if (nickserv_conf.disable_nicks) {
2605 reply("NSMSG_GLOBAL_STATS_NONICK",
2606 dict_size(nickserv_handle_dict));
2608 if (user->handle_info) {
2610 struct nick_info *ni;
2611 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2612 reply("NSMSG_HANDLE_STATS", cnt);
2614 reply("NSMSG_HANDLE_NONE");
2616 reply("NSMSG_GLOBAL_STATS",
2617 dict_size(nickserv_handle_dict),
2618 dict_size(nickserv_nick_dict));
2623 static NICKSERV_FUNC(cmd_ghost)
2625 struct userNode *target;
2626 char reason[MAXLEN];
2628 NICKSERV_MIN_PARMS(2);
2629 if (!(target = GetUserH(argv[1]))) {
2630 reply("MSG_NICK_UNKNOWN", argv[1]);
2633 if (target == user) {
2634 reply("NSMSG_CANNOT_GHOST_SELF");
2637 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2638 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2641 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2642 DelUser(target, nickserv, 1, reason);
2643 reply("NSMSG_GHOST_KILLED", argv[1]);
2647 static NICKSERV_FUNC(cmd_vacation)
2649 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2650 reply("NSMSG_ON_VACATION");
2655 nickserv_saxdb_write(struct saxdb_context *ctx) {
2657 struct handle_info *hi;
2660 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2662 #ifdef WITH_PROTOCOL_BAHAMUT
2665 saxdb_start_record(ctx, iter_key(it), 0);
2666 if (hi->announcements != '?') {
2667 flags[0] = hi->announcements;
2669 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2672 struct handle_cookie *cookie = hi->cookie;
2675 switch (cookie->type) {
2676 case ACTIVATION: type = KEY_ACTIVATION; break;
2677 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2678 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2679 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2680 default: type = NULL; break;
2683 saxdb_start_record(ctx, KEY_COOKIE, 0);
2684 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2685 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2687 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2688 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2689 saxdb_end_record(ctx);
2693 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2695 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2697 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2701 for (ii=flen=0; handle_flags[ii]; ++ii)
2702 if (hi->flags & (1 << ii))
2703 flags[flen++] = handle_flags[ii];
2705 saxdb_write_string(ctx, KEY_FLAGS, flags);
2707 #ifdef WITH_PROTOCOL_BAHAMUT
2708 saxdb_write_int(ctx, KEY_ID, hi->id);
2711 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2712 if (hi->last_quit_host[0])
2713 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2714 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2715 if (hi->masks->used)
2716 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2718 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2720 struct string_list *slist;
2721 struct nick_info *ni;
2723 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2724 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2725 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2729 if (hi->opserv_level)
2730 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2731 if (hi->language != lang_C)
2732 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2733 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2734 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2735 if (hi->screen_width)
2736 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2737 if (hi->table_width)
2738 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2739 flags[0] = hi->userlist_style;
2741 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2742 saxdb_end_record(ctx);
2747 static handle_merge_func_t *handle_merge_func_list;
2748 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2751 reg_handle_merge_func(handle_merge_func_t func)
2753 if (handle_merge_func_used == handle_merge_func_size) {
2754 if (handle_merge_func_size) {
2755 handle_merge_func_size <<= 1;
2756 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2758 handle_merge_func_size = 8;
2759 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2762 handle_merge_func_list[handle_merge_func_used++] = func;
2765 static NICKSERV_FUNC(cmd_merge)
2767 struct handle_info *hi_from, *hi_to;
2768 struct userNode *last_user;
2769 struct userData *cList, *cListNext;
2770 unsigned int ii, jj, n;
2771 char buffer[MAXLEN];
2773 NICKSERV_MIN_PARMS(3);
2775 if (!(hi_from = get_victim_oper(user, argv[1])))
2777 if (!(hi_to = get_victim_oper(user, argv[2])))
2779 if (hi_to == hi_from) {
2780 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2784 for (n=0; n<handle_merge_func_used; n++)
2785 handle_merge_func_list[n](user, hi_to, hi_from);
2787 /* Append "from" handle's nicks to "to" handle's nick list. */
2789 struct nick_info *last_ni;
2790 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2791 last_ni->next = hi_from->nicks;
2793 while (hi_from->nicks) {
2794 hi_from->nicks->owner = hi_to;
2795 hi_from->nicks = hi_from->nicks->next;
2798 /* Merge the hostmasks. */
2799 for (ii=0; ii<hi_from->masks->used; ii++) {
2800 char *mask = hi_from->masks->list[ii];
2801 for (jj=0; jj<hi_to->masks->used; jj++)
2802 if (match_ircglobs(hi_to->masks->list[jj], mask))
2804 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2805 string_list_append(hi_to->masks, strdup(mask));
2808 /* Merge the lists of authed users. */
2810 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2811 last_user->next_authed = hi_from->users;
2813 hi_to->users = hi_from->users;
2815 /* Repoint the old "from" handle's users. */
2816 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2817 last_user->handle_info = hi_to;
2819 hi_from->users = NULL;
2821 /* Merge channel userlists. */
2822 for (cList=hi_from->channels; cList; cList=cListNext) {
2823 struct userData *cList2;
2824 cListNext = cList->u_next;
2825 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2826 if (cList->channel == cList2->channel)
2828 if (cList2 && (cList2->access >= cList->access)) {
2829 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);
2830 /* keep cList2 in hi_to; remove cList from hi_from */
2831 del_channel_user(cList, 1);
2834 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);
2835 /* remove the lower-ranking cList2 from hi_to */
2836 del_channel_user(cList2, 1);
2838 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
2840 /* cList needs to be moved from hi_from to hi_to */
2841 cList->handle = hi_to;
2842 /* Remove from linked list for hi_from */
2843 assert(!cList->u_prev);
2844 hi_from->channels = cList->u_next;
2846 cList->u_next->u_prev = cList->u_prev;
2847 /* Add to linked list for hi_to */
2848 cList->u_prev = NULL;
2849 cList->u_next = hi_to->channels;
2850 if (hi_to->channels)
2851 hi_to->channels->u_prev = cList;
2852 hi_to->channels = cList;
2856 /* Do they get an OpServ level promotion? */
2857 if (hi_from->opserv_level > hi_to->opserv_level)
2858 hi_to->opserv_level = hi_from->opserv_level;
2860 /* What about last seen time? */
2861 if (hi_from->lastseen > hi_to->lastseen)
2862 hi_to->lastseen = hi_from->lastseen;
2864 /* Does a fakehost carry over? (This intentionally doesn't set it
2865 * for users previously attached to hi_to. They'll just have to
2868 if (hi_from->fakehost && !hi_to->fakehost)
2869 hi_to->fakehost = strdup(hi_from->fakehost);
2871 /* Notify of success. */
2872 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2873 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2874 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2876 /* Unregister the "from" handle. */
2877 nickserv_unregister_handle(hi_from, NULL);
2882 struct nickserv_discrim {
2883 unsigned int limit, min_level, max_level;
2884 unsigned long flags_on, flags_off;
2885 time_t min_registered, max_registered;
2887 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2888 const char *nickmask;
2889 const char *hostmask;
2890 const char *handlemask;
2891 const char *emailmask;
2894 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2896 struct discrim_apply_info {
2897 struct nickserv_discrim *discrim;
2898 discrim_search_func func;
2899 struct userNode *source;
2900 unsigned int matched;
2903 static struct nickserv_discrim *
2904 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2907 struct nickserv_discrim *discrim;
2909 discrim = malloc(sizeof(*discrim));
2910 memset(discrim, 0, sizeof(*discrim));
2911 discrim->min_level = 0;
2912 discrim->max_level = ~0;
2913 discrim->limit = 50;
2914 discrim->min_registered = 0;
2915 discrim->max_registered = INT_MAX;
2916 discrim->lastseen = now;
2918 for (i=0; i<argc; i++) {
2919 if (i == argc - 1) {
2920 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2923 if (!irccasecmp(argv[i], "limit")) {
2924 discrim->limit = strtoul(argv[++i], NULL, 0);
2925 } else if (!irccasecmp(argv[i], "flags")) {
2926 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2927 } else if (!irccasecmp(argv[i], "registered")) {
2928 const char *cmp = argv[++i];
2929 if (cmp[0] == '<') {
2930 if (cmp[1] == '=') {
2931 discrim->min_registered = now - ParseInterval(cmp+2);
2933 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2935 } else if (cmp[0] == '=') {
2936 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2937 } else if (cmp[0] == '>') {
2938 if (cmp[1] == '=') {
2939 discrim->max_registered = now - ParseInterval(cmp+2);
2941 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2944 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2946 } else if (!irccasecmp(argv[i], "seen")) {
2947 discrim->lastseen = now - ParseInterval(argv[++i]);
2948 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2949 discrim->nickmask = argv[++i];
2950 } else if (!irccasecmp(argv[i], "hostmask")) {
2952 if (!irccasecmp(argv[i], "exact")) {
2953 if (i == argc - 1) {
2954 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2957 discrim->hostmask_type = EXACT;
2958 } else if (!irccasecmp(argv[i], "subset")) {
2959 if (i == argc - 1) {
2960 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2963 discrim->hostmask_type = SUBSET;
2964 } else if (!irccasecmp(argv[i], "superset")) {
2965 if (i == argc - 1) {
2966 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2969 discrim->hostmask_type = SUPERSET;
2970 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2971 if (i == argc - 1) {
2972 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2975 discrim->hostmask_type = LASTQUIT;
2978 discrim->hostmask_type = SUPERSET;
2980 discrim->hostmask = argv[++i];
2981 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2982 if (!irccasecmp(argv[++i], "*")) {
2983 discrim->handlemask = 0;
2985 discrim->handlemask = argv[i];
2987 } else if (!irccasecmp(argv[i], "email")) {
2988 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2989 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2991 } else if (!irccasecmp(argv[++i], "*")) {
2992 discrim->emailmask = 0;
2994 discrim->emailmask = argv[i];
2996 } else if (!irccasecmp(argv[i], "access")) {
2997 const char *cmp = argv[++i];
2998 if (cmp[0] == '<') {
2999 if (discrim->min_level == 0) discrim->min_level = 1;
3000 if (cmp[1] == '=') {
3001 discrim->max_level = strtoul(cmp+2, NULL, 0);
3003 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3005 } else if (cmp[0] == '=') {
3006 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3007 } else if (cmp[0] == '>') {
3008 if (cmp[1] == '=') {
3009 discrim->min_level = strtoul(cmp+2, NULL, 0);
3011 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3014 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3017 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3028 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3030 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3031 || (discrim->flags_off & hi->flags)
3032 || (discrim->min_registered > hi->registered)
3033 || (discrim->max_registered < hi->registered)
3034 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3035 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3036 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3037 || (discrim->min_level > hi->opserv_level)
3038 || (discrim->max_level < hi->opserv_level)) {
3041 if (discrim->hostmask) {
3043 for (i=0; i<hi->masks->used; i++) {
3044 const char *mask = hi->masks->list[i];
3045 if ((discrim->hostmask_type == SUBSET)
3046 && (match_ircglobs(discrim->hostmask, mask))) break;
3047 else if ((discrim->hostmask_type == EXACT)
3048 && !irccasecmp(discrim->hostmask, mask)) break;
3049 else if ((discrim->hostmask_type == SUPERSET)
3050 && (match_ircglobs(mask, discrim->hostmask))) break;
3051 else if ((discrim->hostmask_type == LASTQUIT)
3052 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3054 if (i==hi->masks->used) return 0;
3056 if (discrim->nickmask) {
3057 struct nick_info *nick = hi->nicks;
3059 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3062 if (!nick) return 0;
3068 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3070 dict_iterator_t it, next;
3071 unsigned int matched;
3073 for (it = dict_first(nickserv_handle_dict), matched = 0;
3074 it && (matched < discrim->limit);
3076 next = iter_next(it);
3077 if (nickserv_discrim_match(discrim, iter_data(it))) {
3078 dsf(source, iter_data(it));
3086 search_print_func(struct userNode *source, struct handle_info *match)
3088 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3092 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3097 search_unregister_func (struct userNode *source, struct handle_info *match)
3099 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3100 nickserv_unregister_handle(match, source);
3104 nickserv_sort_accounts_by_access(const void *a, const void *b)
3106 const struct handle_info *hi_a = *(const struct handle_info**)a;
3107 const struct handle_info *hi_b = *(const struct handle_info**)b;
3108 if (hi_a->opserv_level != hi_b->opserv_level)
3109 return hi_b->opserv_level - hi_a->opserv_level;
3110 return irccasecmp(hi_a->handle, hi_b->handle);
3114 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3116 struct handle_info_list hil;
3117 struct helpfile_table tbl;
3122 memset(&hil, 0, sizeof(hil));
3123 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3124 struct handle_info *hi = iter_data(it);
3125 if (hi->opserv_level)
3126 handle_info_list_append(&hil, hi);
3128 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3129 tbl.length = hil.used + 1;
3131 tbl.flags = TABLE_NO_FREE;
3132 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3133 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3136 for (ii = 0; ii < hil.used; ) {
3137 ary = malloc(tbl.width * sizeof(ary[0]));
3138 ary[0] = hil.list[ii]->handle;
3139 ary[1] = strtab(hil.list[ii]->opserv_level);
3140 tbl.contents[++ii] = ary;
3142 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3143 reply("MSG_MATCH_COUNT", hil.used);
3144 for (ii = 0; ii < hil.used; ii++)
3145 free(tbl.contents[ii]);
3150 static NICKSERV_FUNC(cmd_search)
3152 struct nickserv_discrim *discrim;
3153 discrim_search_func action;
3154 struct svccmd *subcmd;
3155 unsigned int matches;
3158 NICKSERV_MIN_PARMS(3);
3159 sprintf(buf, "search %s", argv[1]);
3160 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3161 if (!irccasecmp(argv[1], "print"))
3162 action = search_print_func;
3163 else if (!irccasecmp(argv[1], "count"))
3164 action = search_count_func;
3165 else if (!irccasecmp(argv[1], "unregister"))
3166 action = search_unregister_func;
3168 reply("NSMSG_INVALID_ACTION", argv[1]);
3172 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3175 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3179 if (action == search_print_func)
3180 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3181 else if (action == search_count_func)
3182 discrim->limit = INT_MAX;
3184 matches = nickserv_discrim_search(discrim, action, user);
3187 reply("MSG_MATCH_COUNT", matches);
3189 reply("MSG_NO_MATCHES");
3195 static MODCMD_FUNC(cmd_checkpass)
3197 struct handle_info *hi;
3199 NICKSERV_MIN_PARMS(3);
3200 if (!(hi = get_handle_info(argv[1]))) {
3201 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3204 if (checkpass(argv[2], hi->passwd))
3205 reply("CHECKPASS_YES");
3207 reply("CHECKPASS_NO");
3213 nickserv_db_read_handle(const char *handle, dict_t obj)
3216 struct string_list *masks, *slist;
3217 struct handle_info *hi;
3218 struct userNode *authed_users;
3219 struct userData *channels;
3220 unsigned long int id;
3224 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3225 id = str ? strtoul(str, NULL, 0) : 0;
3226 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3228 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3231 if ((hi = get_handle_info(handle))) {
3232 authed_users = hi->users;
3233 channels = hi->channels;
3235 hi->channels = NULL;
3236 dict_remove(nickserv_handle_dict, hi->handle);
3238 authed_users = NULL;
3241 hi = register_handle(handle, str, id);
3243 hi->users = authed_users;
3244 while (authed_users) {
3245 authed_users->handle_info = hi;
3246 authed_users = authed_users->next_authed;
3249 hi->channels = channels;
3250 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3251 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3252 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3253 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3254 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3255 hi->language = language_find(str ? str : "C");
3256 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3257 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3258 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3260 hi->infoline = strdup(str);
3261 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3262 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3263 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3264 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3265 /* We want to read the nicks even if disable_nicks is set. This is so
3266 * that we don't lose the nick data entirely. */
3267 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3269 for (ii=0; ii<slist->used; ii++)
3270 register_nick(slist->list[ii], hi);
3272 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3274 for (ii=0; str[ii]; ii++)
3275 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3277 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3278 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3279 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3280 hi->announcements = str ? str[0] : '?';
3281 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3282 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3283 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3284 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3285 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3287 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3289 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3290 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3292 nickserv_set_email_addr(hi, str);
3293 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3295 hi->epithet = strdup(str);
3296 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3298 hi->fakehost = strdup(str);
3299 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3301 const char *data, *type, *expires, *cookie_str;
3302 struct handle_cookie *cookie;
3304 cookie = calloc(1, sizeof(*cookie));
3305 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3306 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3307 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3308 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3309 if (!type || !expires || !cookie_str) {
3310 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3313 if (!irccasecmp(type, KEY_ACTIVATION))
3314 cookie->type = ACTIVATION;
3315 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3316 cookie->type = PASSWORD_CHANGE;
3317 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3318 cookie->type = EMAIL_CHANGE;
3319 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3320 cookie->type = ALLOWAUTH;
3322 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3325 cookie->expires = strtoul(expires, NULL, 0);
3326 if (cookie->expires < now)
3329 cookie->data = strdup(data);
3330 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3334 nickserv_bake_cookie(cookie);
3336 nickserv_free_cookie(cookie);
3341 nickserv_saxdb_read(dict_t db) {
3343 struct record_data *rd;
3345 for (it=dict_first(db); it; it=iter_next(it)) {
3347 nickserv_db_read_handle(iter_key(it), rd->d.object);
3352 static NICKSERV_FUNC(cmd_mergedb)
3354 struct timeval start, stop;
3357 NICKSERV_MIN_PARMS(2);
3358 gettimeofday(&start, NULL);
3359 if (!(db = parse_database(argv[1]))) {
3360 reply("NSMSG_DB_UNREADABLE", argv[1]);
3363 nickserv_saxdb_read(db);
3365 gettimeofday(&stop, NULL);
3366 stop.tv_sec -= start.tv_sec;
3367 stop.tv_usec -= start.tv_usec;
3368 if (stop.tv_usec < 0) {
3370 stop.tv_usec += 1000000;
3372 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3377 expire_handles(UNUSED_ARG(void *data))
3379 dict_iterator_t it, next;
3381 struct handle_info *hi;
3383 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3384 next = iter_next(it);
3386 if ((hi->opserv_level > 0)
3388 || HANDLE_FLAGGED(hi, FROZEN)
3389 || HANDLE_FLAGGED(hi, NODELETE)) {
3392 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3393 if ((now - hi->lastseen) > expiry) {
3394 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3395 nickserv_unregister_handle(hi, NULL);
3399 if (nickserv_conf.handle_expire_frequency)
3400 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3404 nickserv_load_dict(const char *fname)
3408 if (!(file = fopen(fname, "r"))) {
3409 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3412 while (!feof(file)) {
3413 fgets(line, sizeof(line), file);
3416 if (line[strlen(line)-1] == '\n')
3417 line[strlen(line)-1] = 0;
3418 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3421 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3424 static enum reclaim_action
3425 reclaim_action_from_string(const char *str) {
3427 return RECLAIM_NONE;
3428 else if (!irccasecmp(str, "warn"))
3429 return RECLAIM_WARN;
3430 else if (!irccasecmp(str, "svsnick"))
3431 return RECLAIM_SVSNICK;
3432 else if (!irccasecmp(str, "kill"))
3433 return RECLAIM_KILL;
3435 return RECLAIM_NONE;
3439 nickserv_conf_read(void)
3441 dict_t conf_node, child;
3445 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3446 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3449 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3451 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3452 if (nickserv_conf.valid_handle_regex_set)
3453 regfree(&nickserv_conf.valid_handle_regex);
3455 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3456 nickserv_conf.valid_handle_regex_set = !err;
3457 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3459 nickserv_conf.valid_handle_regex_set = 0;
3461 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3462 if (nickserv_conf.valid_nick_regex_set)
3463 regfree(&nickserv_conf.valid_nick_regex);
3465 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3466 nickserv_conf.valid_nick_regex_set = !err;
3467 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3469 nickserv_conf.valid_nick_regex_set = 0;
3471 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3473 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3474 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3475 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3476 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3477 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3478 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3479 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3480 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3481 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3482 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3483 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3484 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3485 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3486 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3487 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3488 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3489 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3490 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3491 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3492 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3493 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3494 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3495 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3496 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3497 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3499 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3500 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3501 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3503 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3504 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3505 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3507 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3508 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3509 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3510 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3511 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3512 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3513 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3514 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3515 if (!nickserv_conf.disable_nicks) {
3516 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3517 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3518 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3519 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3520 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3521 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3522 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3523 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3525 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3526 for (it=dict_first(child); it; it=iter_next(it)) {
3527 const char *key = iter_key(it), *value;
3531 if (!strncasecmp(key, "uc_", 3))
3532 flag = toupper(key[3]);
3533 else if (!strncasecmp(key, "lc_", 3))
3534 flag = tolower(key[3]);
3538 if ((pos = handle_inverse_flags[flag])) {
3539 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3540 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3543 if (nickserv_conf.weak_password_dict)
3544 dict_delete(nickserv_conf.weak_password_dict);
3545 nickserv_conf.weak_password_dict = dict_new();
3546 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3547 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3548 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3549 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3551 nickserv_load_dict(str);
3552 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3553 if (nickserv && str)
3554 NickChange(nickserv, str, 0);
3555 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3556 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3557 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3558 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3559 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3560 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3561 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3562 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3563 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3564 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3565 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3566 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3567 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3568 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3569 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3570 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3571 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3572 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3573 str = conf_get_data("server/network", RECDB_QSTRING);
3574 nickserv_conf.network_name = str ? str : "some IRC network";
3575 if (!nickserv_conf.auth_policer_params) {
3576 nickserv_conf.auth_policer_params = policer_params_new();
3577 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3578 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3580 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3581 for (it=dict_first(child); it; it=iter_next(it))
3582 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3586 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3588 char newnick[NICKLEN+1];
3597 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3599 case RECLAIM_SVSNICK:
3601 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3602 } while (GetUserH(newnick));
3603 irc_svsnick(nickserv, user, newnick);
3606 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3607 DelUser(user, nickserv, 1, msg);
3613 nickserv_reclaim_p(void *data) {
3614 struct userNode *user = data;
3615 struct nick_info *ni = get_nick_info(user->nick);
3617 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3621 check_user_nick(struct userNode *user) {
3622 struct nick_info *ni;
3623 user->modes &= ~FLAGS_REGNICK;
3624 if (!(ni = get_nick_info(user->nick)))
3626 if (user->handle_info == ni->owner) {
3627 user->modes |= FLAGS_REGNICK;
3631 if (nickserv_conf.warn_nick_owned)
3632 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3633 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3635 if (nickserv_conf.auto_reclaim_delay)
3636 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3638 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3643 handle_new_user(struct userNode *user)
3645 return check_user_nick(user);
3649 handle_account(struct userNode *user, const char *stamp)
3651 struct handle_info *hi;
3653 #ifdef WITH_PROTOCOL_P10
3654 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3656 hi = dict_find(nickserv_id_dict, stamp, NULL);
3660 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3663 set_user_handle_info(user, hi, 0);
3665 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3670 handle_nick_change(struct userNode *user, const char *old_nick)
3672 struct handle_info *hi;
3674 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3675 dict_remove(nickserv_allow_auth_dict, old_nick);
3676 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3678 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3679 check_user_nick(user);
3683 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3685 dict_remove(nickserv_allow_auth_dict, user->nick);
3686 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3687 set_user_handle_info(user, NULL, 0);
3690 static struct modcmd *
3691 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3693 if (min_level > 0) {
3695 sprintf(buf, "%u", min_level);
3696 if (must_be_qualified) {
3697 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3699 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3701 } else if (min_level == 0) {
3702 if (must_be_qualified) {
3703 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3705 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3708 if (must_be_qualified) {
3709 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3711 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3717 nickserv_db_cleanup(void)
3719 unreg_del_user_func(nickserv_remove_user);
3720 userList_clean(&curr_helpers);
3721 policer_params_delete(nickserv_conf.auth_policer_params);
3722 dict_delete(nickserv_handle_dict);
3723 dict_delete(nickserv_nick_dict);
3724 dict_delete(nickserv_opt_dict);
3725 dict_delete(nickserv_allow_auth_dict);
3726 dict_delete(nickserv_email_dict);
3727 dict_delete(nickserv_id_dict);
3728 dict_delete(nickserv_conf.weak_password_dict);
3729 free(auth_func_list);
3730 free(unreg_func_list);
3732 free(allowauth_func_list);
3733 free(handle_merge_func_list);
3734 free(failpw_func_list);
3735 if (nickserv_conf.valid_handle_regex_set)
3736 regfree(&nickserv_conf.valid_handle_regex);
3737 if (nickserv_conf.valid_nick_regex_set)
3738 regfree(&nickserv_conf.valid_nick_regex);
3742 init_nickserv(const char *nick)
3745 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3746 reg_new_user_func(handle_new_user);
3747 reg_nick_change_func(handle_nick_change);
3748 reg_del_user_func(nickserv_remove_user);
3749 reg_account_func(handle_account);
3751 /* set up handle_inverse_flags */
3752 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3753 for (i=0; handle_flags[i]; i++) {
3754 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3755 flag_access_levels[i] = 0;
3758 conf_register_reload(nickserv_conf_read);
3759 nickserv_opt_dict = dict_new();
3760 nickserv_email_dict = dict_new();
3761 dict_set_free_keys(nickserv_email_dict, free);
3762 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3764 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3765 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3766 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3767 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3768 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3769 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3770 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3771 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3772 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3773 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3774 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3775 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3776 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3777 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3778 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3779 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3780 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3781 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3782 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3783 if (!nickserv_conf.disable_nicks) {
3784 /* nick management commands */
3785 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3786 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3787 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3788 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3789 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3790 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3792 if (nickserv_conf.email_enabled) {
3793 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3794 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3795 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3796 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3797 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3799 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3800 /* miscellaneous commands */
3801 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3802 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3803 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3804 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3805 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3807 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3808 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3809 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3810 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3811 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3812 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3813 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3814 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3815 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3816 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3817 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3818 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3819 if (nickserv_conf.titlehost_suffix) {
3820 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
3821 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
3823 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3824 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3825 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3827 nickserv_handle_dict = dict_new();
3828 dict_set_free_keys(nickserv_handle_dict, free);
3829 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3831 nickserv_id_dict = dict_new();
3832 dict_set_free_keys(nickserv_id_dict, free);
3834 nickserv_nick_dict = dict_new();
3835 dict_set_free_data(nickserv_nick_dict, free);
3837 nickserv_allow_auth_dict = dict_new();
3839 userList_init(&curr_helpers);
3842 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
3843 nickserv = AddService(nick, modes ? modes : NULL, "Nick Services", NULL);
3844 nickserv_service = service_register(nickserv);
3846 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3847 reg_exit_func(nickserv_db_cleanup);
3848 if(nickserv_conf.handle_expire_frequency)
3849 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3850 message_register_table(msgtab);