1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2006 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() */
33 # include "rx/rxposix.h"
36 #define NICKSERV_CONF_NAME "services/nickserv"
38 #define KEY_DISABLE_NICKS "disable_nicks"
39 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
40 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
41 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
42 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
43 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
44 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
45 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
46 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
47 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
48 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
49 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
50 #define KEY_MODOPER_LEVEL "modoper_level"
51 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
52 #define KEY_SET_TITLE_LEVEL "set_title_level"
53 #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
54 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
55 #define KEY_FLAG_LEVELS "flag_levels"
56 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
57 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
58 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
59 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
60 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
61 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
62 #define KEY_DICT_FILE "dict_file"
63 #define KEY_NICK "nick"
64 #define KEY_LANGUAGE "language"
65 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
66 #define KEY_AUTOGAG_DURATION "autogag_duration"
67 #define KEY_AUTH_POLICER "auth_policer"
68 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
69 #define KEY_EMAIL_ENABLED "email_enabled"
70 #define KEY_EMAIL_REQUIRED "email_required"
71 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
72 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
73 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
76 #define KEY_PASSWD "passwd"
77 #define KEY_NICKS "nicks"
78 #define KEY_MASKS "masks"
79 #define KEY_OPSERV_LEVEL "opserv_level"
80 #define KEY_FLAGS "flags"
81 #define KEY_REGISTER_ON "register"
82 #define KEY_LAST_SEEN "lastseen"
83 #define KEY_INFO "info"
84 #define KEY_USERLIST_STYLE "user_style"
85 #define KEY_SCREEN_WIDTH "screen_width"
86 #define KEY_LAST_AUTHED_HOST "last_authed_host"
87 #define KEY_LAST_QUIT_HOST "last_quit_host"
88 #define KEY_EMAIL_ADDR "email_addr"
89 #define KEY_COOKIE "cookie"
90 #define KEY_COOKIE_DATA "data"
91 #define KEY_COOKIE_TYPE "type"
92 #define KEY_COOKIE_EXPIRES "expires"
93 #define KEY_ACTIVATION "activation"
94 #define KEY_PASSWORD_CHANGE "password change"
95 #define KEY_EMAIL_CHANGE "email change"
96 #define KEY_ALLOWAUTH "allowauth"
97 #define KEY_EPITHET "epithet"
98 #define KEY_TABLE_WIDTH "table_width"
99 #define KEY_ANNOUNCEMENTS "announcements"
100 #define KEY_MAXLOGINS "maxlogins"
101 #define KEY_FAKEHOST "fakehost"
103 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
105 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
106 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
107 typedef OPTION_FUNC(option_func_t);
109 DEFINE_LIST(handle_info_list, struct handle_info*);
111 #define NICKSERV_MIN_PARMS(N) do { \
113 reply("MSG_MISSING_PARAMS", argv[0]); \
114 svccmd_send_help(user, nickserv, cmd); \
118 struct userNode *nickserv;
119 struct userList curr_helpers;
120 const char *handle_flags = HANDLE_FLAGS;
122 static struct module *nickserv_module;
123 static struct service *nickserv_service;
124 static struct log_type *NS_LOG;
125 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
126 static dict_t nickserv_id_dict; /* contains struct handle_info* */
127 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
128 static dict_t nickserv_opt_dict; /* contains option_func_t* */
129 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
130 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
131 static char handle_inverse_flags[256];
132 static unsigned int flag_access_levels[32];
133 static const struct message_entry msgtab[] = {
134 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
135 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
136 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
137 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
138 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
139 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
140 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
141 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
142 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
143 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
144 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
145 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
146 { "NSMSG_USE_COOKIE_REGISTER", "To activate your account, you must check your email for the \"cookie\" that has been mailed to it. When you have it, use the $bcookie$b command to complete registration." },
147 { "NSMSG_USE_COOKIE_RESETPASS", "A cookie has been mailed to your account's email address. You must check your email and use the $bcookie$b command to confirm the password change." },
148 { "NSMSG_USE_COOKIE_EMAIL_1", "A cookie has been mailed to the new address you requested. To finish setting your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
149 { "NSMSG_USE_COOKIE_EMAIL_2", "A cookie has been generated, and half mailed to each your old and new addresses. To finish changing your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
150 { "NSMSG_USE_COOKIE_AUTH", "A cookie has been generated and sent to your email address. Once you have checked your email and received the cookie, auth using the $bcookie$b command." },
151 { "NSMSG_COOKIE_LIVE", "Account $b%s$b already has a cookie active. Please either finish using that cookie, wait for it to expire, or auth to the account and use the $bdelcookie$b command." },
152 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
153 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
154 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
155 { "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." },
156 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
157 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
158 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
159 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
160 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
161 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
162 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
163 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
164 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
165 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
166 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
167 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
168 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
169 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
170 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
171 { "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)" },
172 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
173 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
174 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
175 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
176 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
177 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
178 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
179 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
180 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
181 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
182 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
183 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
184 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
185 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
186 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
187 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
188 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
189 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
190 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
191 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
192 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
193 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
194 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
195 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
196 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
197 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
198 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
199 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
200 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
201 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
202 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
203 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
204 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
205 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
206 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
207 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
208 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
209 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
210 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
211 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
212 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
213 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
214 { "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)." },
215 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
216 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
217 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
218 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
219 { "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." },
220 { "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." },
221 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
222 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
223 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
224 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
225 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
226 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
227 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
228 { "NSMSG_PASS_SUCCESS", "Password changed." },
229 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
230 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
231 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
232 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
233 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
234 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
235 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
236 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
237 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
238 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
239 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
240 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
241 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
242 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
243 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
244 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
245 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
246 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
247 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
248 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
249 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
250 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
251 { "NSMSG_NO_ACCESS", "Access denied." },
252 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
253 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
254 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
255 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
256 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
257 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
258 { "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." },
259 { "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." },
260 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
261 { "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." },
262 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
263 { "NSMSG_SEARCH_MATCH", "Match: %s" },
264 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
265 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
266 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
267 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
268 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
269 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
270 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
271 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
272 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
273 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
274 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
275 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
276 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
277 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
278 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
279 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
280 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
281 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
282 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
283 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
284 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
285 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
286 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
287 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
288 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
289 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
290 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
291 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
292 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
293 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
294 { "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." },
295 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
296 { "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." },
297 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
298 { "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." },
299 { "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." },
300 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
301 { "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." },
302 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
303 { "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." },
304 { "CHECKPASS_YES", "Yes." },
305 { "CHECKPASS_NO", "No." },
309 enum reclaim_action {
315 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
316 static void nickserv_reclaim_p(void *data);
319 unsigned int disable_nicks : 1;
320 unsigned int valid_handle_regex_set : 1;
321 unsigned int valid_nick_regex_set : 1;
322 unsigned int autogag_enabled : 1;
323 unsigned int email_enabled : 1;
324 unsigned int email_required : 1;
325 unsigned int default_hostmask : 1;
326 unsigned int warn_nick_owned : 1;
327 unsigned int warn_clone_auth : 1;
328 unsigned long nicks_per_handle;
329 unsigned long password_min_length;
330 unsigned long password_min_digits;
331 unsigned long password_min_upper;
332 unsigned long password_min_lower;
333 unsigned long db_backup_frequency;
334 unsigned long handle_expire_frequency;
335 unsigned long autogag_duration;
336 unsigned long email_visible_level;
337 unsigned long cookie_timeout;
338 unsigned long handle_expire_delay;
339 unsigned long nochan_handle_expire_delay;
340 unsigned long modoper_level;
341 unsigned long set_epithet_level;
342 unsigned long set_title_level;
343 unsigned long set_fakehost_level;
344 unsigned long handles_per_email;
345 unsigned long email_search_level;
346 const char *network_name;
347 const char *titlehost_suffix;
348 regex_t valid_handle_regex;
349 regex_t valid_nick_regex;
350 dict_t weak_password_dict;
351 struct policer_params *auth_policer_params;
352 enum reclaim_action reclaim_action;
353 enum reclaim_action auto_reclaim_action;
354 unsigned long auto_reclaim_delay;
355 unsigned char default_maxlogins;
356 unsigned char hard_maxlogins;
359 /* We have 2^32 unique account IDs to use. */
360 unsigned long int highest_id = 0;
363 canonicalize_hostmask(char *mask)
365 char *out = mask, *temp;
366 if ((temp = strchr(mask, '!'))) {
368 while (*temp) *out++ = *temp++;
374 static struct handle_info *
375 register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
377 struct handle_info *hi;
379 #ifdef WITH_PROTOCOL_BAHAMUT
380 char id_base64[IDLEN + 1];
383 /* Assign a unique account ID to the account; note that 0 is
384 an invalid account ID. 1 is therefore the first account ID. */
386 id = 1 + highest_id++;
388 /* Note: highest_id is and must always be the highest ID. */
389 if(id > highest_id) {
393 inttobase64(id_base64, id, IDLEN);
395 /* Make sure an account with the same ID doesn't exist. If a
396 duplicate is found, log some details and assign a new one.
397 This should be impossible, but it never hurts to expect it. */
398 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
399 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
405 hi = calloc(1, sizeof(*hi));
406 hi->userlist_style = HI_DEFAULT_STYLE;
407 hi->announcements = '?';
408 hi->handle = strdup(handle);
409 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
411 dict_insert(nickserv_handle_dict, hi->handle, hi);
413 #ifdef WITH_PROTOCOL_BAHAMUT
415 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
422 register_nick(const char *nick, struct handle_info *owner)
424 struct nick_info *ni;
425 ni = malloc(sizeof(struct nick_info));
426 safestrncpy(ni->nick, nick, sizeof(ni->nick));
428 ni->next = owner->nicks;
430 dict_insert(nickserv_nick_dict, ni->nick, ni);
434 delete_nick(struct nick_info *ni)
436 struct nick_info *last, *next;
437 struct userNode *user;
438 /* Check to see if we should mark a user as unregistered. */
439 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
440 user->modes &= ~FLAGS_REGNICK;
443 /* Remove ni from the nick_info linked list. */
444 if (ni == ni->owner->nicks) {
445 ni->owner->nicks = ni->next;
447 last = ni->owner->nicks;
453 last->next = next->next;
455 dict_remove(nickserv_nick_dict, ni->nick);
458 static unreg_func_t *unreg_func_list;
459 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
462 reg_unreg_func(unreg_func_t func)
464 if (unreg_func_used == unreg_func_size) {
465 if (unreg_func_size) {
466 unreg_func_size <<= 1;
467 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
470 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
473 unreg_func_list[unreg_func_used++] = func;
477 nickserv_free_cookie(void *data)
479 struct handle_cookie *cookie = data;
480 if (cookie->hi) cookie->hi->cookie = NULL;
481 if (cookie->data) free(cookie->data);
486 free_handle_info(void *vhi)
488 struct handle_info *hi = vhi;
490 #ifdef WITH_PROTOCOL_BAHAMUT
493 inttobase64(id, hi->id, IDLEN);
494 dict_remove(nickserv_id_dict, id);
497 free_string_list(hi->masks);
501 delete_nick(hi->nicks);
506 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
507 nickserv_free_cookie(hi->cookie);
509 if (hi->email_addr) {
510 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
511 handle_info_list_remove(hil, hi);
513 dict_remove(nickserv_email_dict, hi->email_addr);
518 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
521 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
525 for (n=0; n<unreg_func_used; n++)
526 unreg_func_list[n](notify, hi);
528 set_user_handle_info(hi->users, NULL, 0);
530 if (nickserv_conf.disable_nicks)
531 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
533 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
535 dict_remove(nickserv_handle_dict, hi->handle);
539 get_handle_info(const char *handle)
541 return dict_find(nickserv_handle_dict, handle, 0);
545 get_nick_info(const char *nick)
547 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
551 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
556 for (nn=0; nn<channel->members.used; ++nn) {
557 mn = channel->members.list[nn];
558 if ((mn->user != except) && (mn->user->handle_info == handle))
565 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
566 if (!user->handle_info) {
568 send_message(user, bot, "MSG_AUTHENTICATE");
572 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
574 send_message(user, bot, "NSMSG_NO_ACCESS");
578 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
580 send_message(user, bot, "MSG_OPER_SUSPENDED");
584 if (user->handle_info->opserv_level < min_level) {
586 send_message(user, bot, "NSMSG_NO_ACCESS");
594 is_valid_handle(const char *handle)
596 struct userNode *user;
597 /* cant register a juped nick/service nick as handle, to prevent confusion */
598 user = GetUserH(handle);
599 if (user && IsLocal(user))
601 /* check against maximum length */
602 if (strlen(handle) > NICKSERV_HANDLE_LEN)
604 /* for consistency, only allow account names that could be nicks */
605 if (!is_valid_nick(handle))
607 /* disallow account names that look like bad words */
608 if (opserv_bad_channel(handle))
610 /* test either regex or containing all valid chars */
611 if (nickserv_conf.valid_handle_regex_set) {
612 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
615 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
616 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
620 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
625 is_registerable_nick(const char *nick)
627 /* make sure it could be used as an account name */
628 if (!is_valid_handle(nick))
631 if (strlen(nick) > NICKLEN)
633 /* test either regex or as valid handle */
634 if (nickserv_conf.valid_nick_regex_set) {
635 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
638 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
639 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
647 is_valid_email_addr(const char *email)
649 return strchr(email, '@') != NULL;
653 visible_email_addr(struct userNode *user, struct handle_info *hi)
655 if (hi->email_addr) {
656 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
657 return hi->email_addr;
667 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
669 struct handle_info *hi;
670 struct userNode *target;
674 if (!(hi = get_handle_info(++name))) {
675 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
680 if (!(target = GetUserH(name))) {
681 send_message(user, service, "MSG_NICK_UNKNOWN", name);
684 if (IsLocal(target)) {
685 if (IsService(target))
686 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
688 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
691 if (!(hi = target->handle_info)) {
692 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
700 oper_outranks(struct userNode *user, struct handle_info *hi) {
701 if (user->handle_info->opserv_level > hi->opserv_level)
703 if (user->handle_info->opserv_level == hi->opserv_level) {
704 if ((user->handle_info->opserv_level == 1000)
705 || (user->handle_info == hi)
706 || ((user->handle_info->opserv_level == 0)
707 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
708 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
712 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
716 static struct handle_info *
717 get_victim_oper(struct userNode *user, const char *target)
719 struct handle_info *hi;
720 if (!(hi = smart_get_handle_info(nickserv, user, target)))
722 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
723 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
726 return oper_outranks(user, hi) ? hi : NULL;
730 valid_user_for(struct userNode *user, struct handle_info *hi)
734 /* If no hostmasks on the account, allow it. */
735 if (!hi->masks->used)
737 /* If any hostmask matches, allow it. */
738 for (ii=0; ii<hi->masks->used; ii++)
739 if (user_matches_glob(user, hi->masks->list[ii], 0))
741 /* If they are allowauthed to this account, allow it (removing the aa). */
742 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
743 dict_remove(nickserv_allow_auth_dict, user->nick);
746 /* The user is not allowed to use this account. */
751 is_secure_password(const char *handle, const char *pass, struct userNode *user)
754 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
758 if (len < nickserv_conf.password_min_length) {
760 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
763 if (!irccasecmp(pass, handle)) {
765 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
768 dict_find(nickserv_conf.weak_password_dict, pass, &p);
771 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
774 for (i=0; i<len; i++) {
775 if (isdigit(pass[i]))
777 if (isupper(pass[i]))
779 if (islower(pass[i]))
782 if ((cnt_lower < nickserv_conf.password_min_lower)
783 || (cnt_upper < nickserv_conf.password_min_upper)
784 || (cnt_digits < nickserv_conf.password_min_digits)) {
786 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
792 static auth_func_t *auth_func_list;
793 static unsigned int auth_func_size = 0, auth_func_used = 0;
796 reg_auth_func(auth_func_t func)
798 if (auth_func_used == auth_func_size) {
799 if (auth_func_size) {
800 auth_func_size <<= 1;
801 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
804 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
807 auth_func_list[auth_func_used++] = func;
810 static handle_rename_func_t *rf_list;
811 static unsigned int rf_list_size, rf_list_used;
814 reg_handle_rename_func(handle_rename_func_t func)
816 if (rf_list_used == rf_list_size) {
819 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
822 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
825 rf_list[rf_list_used++] = func;
829 generate_fakehost(struct handle_info *handle)
831 extern const char *hidden_host_suffix;
832 static char buffer[HOSTLEN+1];
834 if (!handle->fakehost) {
835 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
837 } else if (handle->fakehost[0] == '.') {
838 /* A leading dot indicates the stored value is actually a title. */
839 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
842 return handle->fakehost;
846 apply_fakehost(struct handle_info *handle)
848 struct userNode *target;
853 fake = generate_fakehost(handle);
854 for (target = handle->users; target; target = target->next_authed)
855 assign_fakehost(target, fake, 1);
859 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
862 struct handle_info *old_info;
864 /* This can happen if somebody uses COOKIE while authed, or if
865 * they re-auth to their current handle (which is silly, but users
867 if (user->handle_info == hi)
870 if (user->handle_info) {
871 struct userNode *other;
874 userList_remove(&curr_helpers, user);
876 /* remove from next_authed linked list */
877 if (user->handle_info->users == user) {
878 user->handle_info->users = user->next_authed;
880 for (other = user->handle_info->users;
881 other->next_authed != user;
882 other = other->next_authed) ;
883 other->next_authed = user->next_authed;
885 /* if nobody left on old handle, and they're not an oper, remove !god */
886 if (!user->handle_info->users && !user->handle_info->opserv_level)
887 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
888 /* record them as being last seen at this time */
889 user->handle_info->lastseen = now;
890 /* and record their hostmask */
891 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
893 old_info = user->handle_info;
894 user->handle_info = hi;
895 if (hi && !hi->users && !hi->opserv_level)
896 HANDLE_CLEAR_FLAG(hi, HELPING);
897 for (n=0; n<auth_func_used; n++)
898 auth_func_list[n](user, old_info);
900 struct nick_info *ni;
902 HANDLE_CLEAR_FLAG(hi, FROZEN);
903 if (nickserv_conf.warn_clone_auth) {
904 struct userNode *other;
905 for (other = hi->users; other; other = other->next_authed)
906 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
908 user->next_authed = hi->users;
912 userList_append(&curr_helpers, user);
914 if (hi->fakehost || old_info)
918 #ifdef WITH_PROTOCOL_BAHAMUT
919 /* Stamp users with their account ID. */
921 inttobase64(id, hi->id, IDLEN);
922 #elif WITH_PROTOCOL_P10
923 /* Stamp users with their account name. */
924 char *id = hi->handle;
926 const char *id = "???";
928 if (!nickserv_conf.disable_nicks) {
929 struct nick_info *ni;
930 for (ni = hi->nicks; ni; ni = ni->next) {
931 if (!irccasecmp(user->nick, ni->nick)) {
932 user->modes |= FLAGS_REGNICK;
940 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
941 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
943 /* We cannot clear the user's account ID, unfortunately. */
944 user->next_authed = NULL;
948 static struct handle_info*
949 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
951 struct handle_info *hi;
952 struct nick_info *ni;
953 char crypted[MD5_CRYPT_LENGTH];
955 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
956 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
960 if (!is_secure_password(handle, passwd, user))
963 cryptpass(passwd, crypted);
964 hi = register_handle(handle, crypted, 0);
965 hi->masks = alloc_string_list(1);
967 hi->language = lang_C;
968 hi->registered = now;
970 hi->flags = HI_DEFAULT_FLAGS;
971 if (settee && !no_auth)
972 set_user_handle_info(settee, hi, 1);
975 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
976 else if (nickserv_conf.disable_nicks)
977 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
978 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
979 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
981 register_nick(user->nick, hi);
982 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
984 if (settee && (user != settee))
985 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
990 nickserv_bake_cookie(struct handle_cookie *cookie)
992 cookie->hi->cookie = cookie;
993 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
997 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
999 struct handle_cookie *cookie;
1000 char subject[128], body[4096], *misc;
1001 const char *netname, *fmt;
1005 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1009 cookie = calloc(1, sizeof(*cookie));
1011 cookie->type = type;
1012 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1013 cookie->expires = now + nickserv_conf.cookie_timeout;
1014 inttobase64(cookie->cookie, rand(), 5);
1015 inttobase64(cookie->cookie+5, rand(), 5);
1017 netname = nickserv_conf.network_name;
1020 switch (cookie->type) {
1022 hi->passwd[0] = 0; /* invalidate password */
1023 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1024 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1025 snprintf(subject, sizeof(subject), fmt, netname);
1026 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1027 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1030 case PASSWORD_CHANGE:
1031 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1032 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1033 snprintf(subject, sizeof(subject), fmt, netname);
1034 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1035 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1038 misc = hi->email_addr;
1039 hi->email_addr = cookie->data;
1041 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1042 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1043 snprintf(subject, sizeof(subject), fmt, netname);
1044 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1045 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1046 sendmail(nickserv, hi, subject, body, 1);
1047 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1048 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1050 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1051 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1052 snprintf(subject, sizeof(subject), fmt, netname);
1053 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1054 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1055 sendmail(nickserv, hi, subject, body, 1);
1058 hi->email_addr = misc;
1061 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1062 snprintf(subject, sizeof(subject), fmt, netname);
1063 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1064 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1065 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1068 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1072 sendmail(nickserv, hi, subject, body, first_time);
1073 nickserv_bake_cookie(cookie);
1077 nickserv_eat_cookie(struct handle_cookie *cookie)
1079 cookie->hi->cookie = NULL;
1080 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1081 nickserv_free_cookie(cookie);
1085 nickserv_free_email_addr(void *data)
1087 handle_info_list_clean(data);
1092 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1094 struct handle_info_list *hil;
1095 /* Remove from old handle_info_list ... */
1096 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1097 handle_info_list_remove(hil, hi);
1098 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1099 hi->email_addr = NULL;
1101 /* Add to the new list.. */
1102 if (new_email_addr) {
1103 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1104 hil = calloc(1, sizeof(*hil));
1105 hil->tag = strdup(new_email_addr);
1106 handle_info_list_init(hil);
1107 dict_insert(nickserv_email_dict, hil->tag, hil);
1109 handle_info_list_append(hil, hi);
1110 hi->email_addr = hil->tag;
1114 static NICKSERV_FUNC(cmd_register)
1117 struct handle_info *hi;
1118 const char *email_addr, *password;
1121 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1122 /* Require the first handle registered to belong to someone +o. */
1123 reply("NSMSG_REQUIRE_OPER");
1127 if (user->handle_info) {
1128 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1132 if (IsRegistering(user)) {
1133 reply("NSMSG_ALREADY_REGISTERING");
1137 if (IsStamped(user)) {
1138 /* Unauthenticated users might still have been stamped
1139 previously and could therefore have a hidden host;
1140 do not allow them to register a new account. */
1141 reply("NSMSG_STAMPED_REGISTER");
1145 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1147 if (!is_valid_handle(argv[1])) {
1148 reply("NSMSG_BAD_HANDLE", argv[1]);
1152 if ((argc >= 4) && nickserv_conf.email_enabled) {
1153 struct handle_info_list *hil;
1156 /* Remember email address. */
1157 email_addr = argv[3];
1159 /* Check that the email address looks valid.. */
1160 if (!is_valid_email_addr(email_addr)) {
1161 reply("NSMSG_BAD_EMAIL_ADDR");
1165 /* .. and that we are allowed to send to it. */
1166 if ((str = sendmail_prohibited_address(email_addr))) {
1167 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1171 /* If we do email verify, make sure we don't spam the address. */
1172 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1174 for (nn=0; nn<hil->used; nn++) {
1175 if (hil->list[nn]->cookie) {
1176 reply("NSMSG_EMAIL_UNACTIVATED");
1180 if (hil->used >= nickserv_conf.handles_per_email) {
1181 reply("NSMSG_EMAIL_OVERUSED");
1194 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1196 /* Add any masks they should get. */
1197 if (nickserv_conf.default_hostmask) {
1198 string_list_append(hi->masks, strdup("*@*"));
1200 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1201 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1202 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1205 /* If they're the first to register, give them level 1000. */
1206 if (dict_size(nickserv_handle_dict) == 1) {
1207 hi->opserv_level = 1000;
1208 reply("NSMSG_ROOT_HANDLE", argv[1]);
1211 /* Set their email address. */
1213 nickserv_set_email_addr(hi, email_addr);
1215 /* If they need to do email verification, tell them. */
1217 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1219 /* Set registering flag.. */
1220 user->modes |= FLAGS_REGISTERING;
1225 static NICKSERV_FUNC(cmd_oregister)
1228 struct userNode *settee;
1229 struct handle_info *hi;
1231 NICKSERV_MIN_PARMS(4);
1233 if (!is_valid_handle(argv[1])) {
1234 reply("NSMSG_BAD_HANDLE", argv[1]);
1238 if (strchr(argv[3], '@')) {
1239 mask = canonicalize_hostmask(strdup(argv[3]));
1241 settee = GetUserH(argv[4]);
1243 reply("MSG_NICK_UNKNOWN", argv[4]);
1250 } else if ((settee = GetUserH(argv[3]))) {
1251 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1253 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1256 if (settee && settee->handle_info) {
1257 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1261 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1265 string_list_append(hi->masks, mask);
1269 static NICKSERV_FUNC(cmd_handleinfo)
1272 unsigned int i, pos=0, herelen;
1273 struct userNode *target, *next_un;
1274 struct handle_info *hi;
1275 const char *nsmsg_none;
1278 if (!(hi = user->handle_info)) {
1279 reply("NSMSG_MUST_AUTH");
1282 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1286 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1287 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1288 #ifdef WITH_PROTOCOL_BAHAMUT
1289 reply("NSMSG_HANDLEINFO_ID", hi->id);
1291 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1294 intervalString(buff, now - hi->lastseen, user->handle_info);
1295 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1297 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1300 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1301 if (HANDLE_FLAGGED(hi, FROZEN))
1302 reply("NSMSG_HANDLEINFO_VACATION");
1304 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1305 struct do_not_register *dnr;
1306 if ((dnr = chanserv_is_dnr(NULL, hi)))
1307 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1308 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1310 } else if (hi != user->handle_info)
1313 if (nickserv_conf.email_enabled)
1314 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1318 switch (hi->cookie->type) {
1319 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1320 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1321 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1322 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1323 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1329 unsigned long flen = 1;
1330 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1332 for (i=0, flen=1; handle_flags[i]; i++)
1333 if (hi->flags & 1 << i)
1334 flags[flen++] = handle_flags[i];
1336 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1338 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1341 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1342 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1343 || (hi->opserv_level > 0)) {
1344 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1348 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1350 if (hi->last_quit_host[0])
1351 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1353 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1355 if (nickserv_conf.disable_nicks) {
1356 /* nicks disabled; don't show anything about registered nicks */
1357 } else if (hi->nicks) {
1358 struct nick_info *ni, *next_ni;
1359 for (ni = hi->nicks; ni; ni = next_ni) {
1360 herelen = strlen(ni->nick);
1361 if (pos + herelen + 1 > ArrayLength(buff)) {
1363 goto print_nicks_buff;
1367 memcpy(buff+pos, ni->nick, herelen);
1368 pos += herelen; buff[pos++] = ' ';
1372 reply("NSMSG_HANDLEINFO_NICKS", buff);
1377 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1380 if (hi->masks->used) {
1381 for (i=0; i < hi->masks->used; i++) {
1382 herelen = strlen(hi->masks->list[i]);
1383 if (pos + herelen + 1 > ArrayLength(buff)) {
1385 goto print_mask_buff;
1387 memcpy(buff+pos, hi->masks->list[i], herelen);
1388 pos += herelen; buff[pos++] = ' ';
1389 if (i+1 == hi->masks->used) {
1392 reply("NSMSG_HANDLEINFO_MASKS", buff);
1397 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1401 struct userData *channel, *next;
1404 for (channel = hi->channels; channel; channel = next) {
1405 next = channel->u_next;
1406 name = channel->channel->channel->name;
1407 herelen = strlen(name);
1408 if (pos + herelen + 7 > ArrayLength(buff)) {
1410 goto print_chans_buff;
1412 if (IsUserSuspended(channel))
1414 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1418 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1423 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1426 for (target = hi->users; target; target = next_un) {
1427 herelen = strlen(target->nick);
1428 if (pos + herelen + 1 > ArrayLength(buff)) {
1430 goto print_cnick_buff;
1432 next_un = target->next_authed;
1434 memcpy(buff+pos, target->nick, herelen);
1435 pos += herelen; buff[pos++] = ' ';
1439 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1447 static NICKSERV_FUNC(cmd_userinfo)
1449 struct userNode *target;
1451 NICKSERV_MIN_PARMS(2);
1452 if (!(target = GetUserH(argv[1]))) {
1453 reply("MSG_NICK_UNKNOWN", argv[1]);
1456 if (target->handle_info)
1457 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1459 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1463 static NICKSERV_FUNC(cmd_nickinfo)
1465 struct nick_info *ni;
1467 NICKSERV_MIN_PARMS(2);
1468 if (!(ni = get_nick_info(argv[1]))) {
1469 reply("MSG_NICK_UNKNOWN", argv[1]);
1472 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1476 static NICKSERV_FUNC(cmd_rename_handle)
1478 struct handle_info *hi;
1479 char msgbuf[MAXLEN], *old_handle;
1482 NICKSERV_MIN_PARMS(3);
1483 if (!(hi = get_victim_oper(user, argv[1])))
1485 if (!is_valid_handle(argv[2])) {
1486 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1489 if (get_handle_info(argv[2])) {
1490 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1494 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1495 hi->handle = strdup(argv[2]);
1496 dict_insert(nickserv_handle_dict, hi->handle, hi);
1497 for (nn=0; nn<rf_list_used; nn++)
1498 rf_list[nn](hi, old_handle);
1499 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1500 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1501 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1506 static failpw_func_t *failpw_func_list;
1507 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1510 reg_failpw_func(failpw_func_t func)
1512 if (failpw_func_used == failpw_func_size) {
1513 if (failpw_func_size) {
1514 failpw_func_size <<= 1;
1515 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1517 failpw_func_size = 8;
1518 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1521 failpw_func_list[failpw_func_used++] = func;
1524 static NICKSERV_FUNC(cmd_auth)
1526 int pw_arg, used, maxlogins;
1527 struct handle_info *hi;
1529 struct userNode *other;
1531 if (user->handle_info) {
1532 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1535 if (IsStamped(user)) {
1536 /* Unauthenticated users might still have been stamped
1537 previously and could therefore have a hidden host;
1538 do not allow them to authenticate. */
1539 reply("NSMSG_STAMPED_AUTH");
1543 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1545 } else if (argc == 2) {
1546 if (nickserv_conf.disable_nicks) {
1547 if (!(hi = get_handle_info(user->nick))) {
1548 reply("NSMSG_HANDLE_NOT_FOUND");
1552 /* try to look up their handle from their nick */
1553 struct nick_info *ni;
1554 ni = get_nick_info(user->nick);
1556 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1563 reply("MSG_MISSING_PARAMS", argv[0]);
1564 svccmd_send_help(user, nickserv, cmd);
1568 reply("NSMSG_HANDLE_NOT_FOUND");
1571 /* Responses from here on look up the language used by the handle they asked about. */
1572 passwd = argv[pw_arg];
1573 if (!valid_user_for(user, hi)) {
1574 if (hi->email_addr && nickserv_conf.email_enabled)
1575 send_message_type(4, user, cmd->parent->bot,
1576 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1579 send_message_type(4, user, cmd->parent->bot,
1580 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1582 argv[pw_arg] = "BADMASK";
1585 if (!checkpass(passwd, hi->passwd)) {
1587 send_message_type(4, user, cmd->parent->bot,
1588 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1589 argv[pw_arg] = "BADPASS";
1590 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1591 if (nickserv_conf.autogag_enabled) {
1592 if (!user->auth_policer.params) {
1593 user->auth_policer.last_req = now;
1594 user->auth_policer.params = nickserv_conf.auth_policer_params;
1596 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1598 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1599 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1600 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1602 argv[pw_arg] = "GAGGED";
1607 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1608 send_message_type(4, user, cmd->parent->bot,
1609 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1610 argv[pw_arg] = "SUSPENDED";
1613 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1614 for (used = 0, other = hi->users; other; other = other->next_authed) {
1615 if (++used >= maxlogins) {
1616 send_message_type(4, user, cmd->parent->bot,
1617 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1619 argv[pw_arg] = "MAXLOGINS";
1624 set_user_handle_info(user, hi, 1);
1625 if (nickserv_conf.email_required && !hi->email_addr)
1626 reply("NSMSG_PLEASE_SET_EMAIL");
1627 if (!is_secure_password(hi->handle, passwd, NULL))
1628 reply("NSMSG_WEAK_PASSWORD");
1629 if (hi->passwd[0] != '$')
1630 cryptpass(passwd, hi->passwd);
1631 reply("NSMSG_AUTH_SUCCESS");
1632 argv[pw_arg] = "****";
1636 static allowauth_func_t *allowauth_func_list;
1637 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1640 reg_allowauth_func(allowauth_func_t func)
1642 if (allowauth_func_used == allowauth_func_size) {
1643 if (allowauth_func_size) {
1644 allowauth_func_size <<= 1;
1645 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1647 allowauth_func_size = 8;
1648 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1651 allowauth_func_list[allowauth_func_used++] = func;
1654 static NICKSERV_FUNC(cmd_allowauth)
1656 struct userNode *target;
1657 struct handle_info *hi;
1660 NICKSERV_MIN_PARMS(2);
1661 if (!(target = GetUserH(argv[1]))) {
1662 reply("MSG_NICK_UNKNOWN", argv[1]);
1665 if (target->handle_info) {
1666 reply("NSMSG_USER_PREV_AUTH", target->nick);
1669 if (IsStamped(target)) {
1670 /* Unauthenticated users might still have been stamped
1671 previously and could therefore have a hidden host;
1672 do not allow them to authenticate to an account. */
1673 reply("NSMSG_USER_PREV_STAMP", target->nick);
1678 else if (!(hi = get_handle_info(argv[2]))) {
1679 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1683 if (hi->opserv_level > user->handle_info->opserv_level) {
1684 reply("MSG_USER_OUTRANKED", hi->handle);
1687 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1688 || (hi->opserv_level > 0))
1689 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1690 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1693 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1694 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1695 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1696 if (nickserv_conf.email_enabled)
1697 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1699 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1700 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1702 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1704 for (n=0; n<allowauth_func_used; n++)
1705 allowauth_func_list[n](user, target, hi);
1709 static NICKSERV_FUNC(cmd_authcookie)
1711 struct handle_info *hi;
1713 NICKSERV_MIN_PARMS(2);
1714 if (user->handle_info) {
1715 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1718 if (IsStamped(user)) {
1719 /* Unauthenticated users might still have been stamped
1720 previously and could therefore have a hidden host;
1721 do not allow them to authenticate to an account. */
1722 reply("NSMSG_STAMPED_AUTHCOOKIE");
1725 if (!(hi = get_handle_info(argv[1]))) {
1726 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1729 if (!hi->email_addr) {
1730 reply("MSG_SET_EMAIL_ADDR");
1733 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1737 static NICKSERV_FUNC(cmd_delcookie)
1739 struct handle_info *hi;
1741 hi = user->handle_info;
1743 reply("NSMSG_NO_COOKIE");
1746 switch (hi->cookie->type) {
1749 reply("NSMSG_MUST_TIME_OUT");
1752 nickserv_eat_cookie(hi->cookie);
1753 reply("NSMSG_ATE_COOKIE");
1759 static NICKSERV_FUNC(cmd_resetpass)
1761 struct handle_info *hi;
1762 char crypted[MD5_CRYPT_LENGTH];
1764 NICKSERV_MIN_PARMS(3);
1765 if (user->handle_info) {
1766 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1769 if (IsStamped(user)) {
1770 /* Unauthenticated users might still have been stamped
1771 previously and could therefore have a hidden host;
1772 do not allow them to activate an account. */
1773 reply("NSMSG_STAMPED_RESETPASS");
1776 if (!(hi = get_handle_info(argv[1]))) {
1777 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1780 if (!hi->email_addr) {
1781 reply("MSG_SET_EMAIL_ADDR");
1784 cryptpass(argv[2], crypted);
1786 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1790 static NICKSERV_FUNC(cmd_cookie)
1792 struct handle_info *hi;
1795 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1798 NICKSERV_MIN_PARMS(3);
1799 if (!(hi = get_handle_info(argv[1]))) {
1800 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1806 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1807 reply("NSMSG_HANDLE_SUSPENDED");
1812 reply("NSMSG_NO_COOKIE");
1816 /* Check validity of operation before comparing cookie to
1817 * prohibit guessing by authed users. */
1818 if (user->handle_info
1819 && (hi->cookie->type != EMAIL_CHANGE)
1820 && (hi->cookie->type != PASSWORD_CHANGE)) {
1821 reply("NSMSG_CANNOT_COOKIE");
1825 if (strcmp(cookie, hi->cookie->cookie)) {
1826 reply("NSMSG_BAD_COOKIE");
1830 switch (hi->cookie->type) {
1832 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1833 set_user_handle_info(user, hi, 1);
1834 reply("NSMSG_HANDLE_ACTIVATED");
1836 case PASSWORD_CHANGE:
1837 set_user_handle_info(user, hi, 1);
1838 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1839 reply("NSMSG_PASSWORD_CHANGED");
1842 nickserv_set_email_addr(hi, hi->cookie->data);
1843 reply("NSMSG_EMAIL_CHANGED");
1846 set_user_handle_info(user, hi, 1);
1847 reply("NSMSG_AUTH_SUCCESS");
1850 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1851 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1855 nickserv_eat_cookie(hi->cookie);
1860 static NICKSERV_FUNC(cmd_oregnick) {
1862 struct handle_info *target;
1863 struct nick_info *ni;
1865 NICKSERV_MIN_PARMS(3);
1866 if (!(target = modcmd_get_handle_info(user, argv[1])))
1869 if (!is_registerable_nick(nick)) {
1870 reply("NSMSG_BAD_NICK", nick);
1873 ni = dict_find(nickserv_nick_dict, nick, NULL);
1875 reply("NSMSG_NICK_EXISTS", nick);
1878 register_nick(nick, target);
1879 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1883 static NICKSERV_FUNC(cmd_regnick) {
1885 struct nick_info *ni;
1887 if (!is_registerable_nick(user->nick)) {
1888 reply("NSMSG_BAD_NICK", user->nick);
1891 /* count their nicks, see if it's too many */
1892 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1893 if (n >= nickserv_conf.nicks_per_handle) {
1894 reply("NSMSG_TOO_MANY_NICKS");
1897 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1899 reply("NSMSG_NICK_EXISTS", user->nick);
1902 register_nick(user->nick, user->handle_info);
1903 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1907 static NICKSERV_FUNC(cmd_pass)
1909 struct handle_info *hi;
1910 const char *old_pass, *new_pass;
1912 NICKSERV_MIN_PARMS(3);
1913 hi = user->handle_info;
1917 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1918 if (!checkpass(old_pass, hi->passwd)) {
1919 argv[1] = "BADPASS";
1920 reply("NSMSG_PASSWORD_INVALID");
1923 cryptpass(new_pass, hi->passwd);
1925 reply("NSMSG_PASS_SUCCESS");
1930 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1933 char *new_mask = canonicalize_hostmask(strdup(mask));
1934 for (i=0; i<hi->masks->used; i++) {
1935 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1936 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1941 string_list_append(hi->masks, new_mask);
1942 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
1946 static NICKSERV_FUNC(cmd_addmask)
1949 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1950 int res = nickserv_addmask(user, user->handle_info, mask);
1954 if (!is_gline(argv[1])) {
1955 reply("NSMSG_MASK_INVALID", argv[1]);
1958 return nickserv_addmask(user, user->handle_info, argv[1]);
1962 static NICKSERV_FUNC(cmd_oaddmask)
1964 struct handle_info *hi;
1966 NICKSERV_MIN_PARMS(3);
1967 if (!(hi = get_victim_oper(user, argv[1])))
1969 return nickserv_addmask(user, hi, argv[2]);
1973 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
1976 for (i=0; i<hi->masks->used; i++) {
1977 if (!strcmp(del_mask, hi->masks->list[i])) {
1978 char *old_mask = hi->masks->list[i];
1979 if (hi->masks->used == 1) {
1980 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
1983 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
1984 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
1989 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
1993 static NICKSERV_FUNC(cmd_delmask)
1995 NICKSERV_MIN_PARMS(2);
1996 return nickserv_delmask(user, user->handle_info, argv[1]);
1999 static NICKSERV_FUNC(cmd_odelmask)
2001 struct handle_info *hi;
2002 NICKSERV_MIN_PARMS(3);
2003 if (!(hi = get_victim_oper(user, argv[1])))
2005 return nickserv_delmask(user, hi, argv[2]);
2009 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2010 unsigned int nn, add = 1, pos;
2011 unsigned long added, removed, flag;
2013 for (added=removed=nn=0; str[nn]; nn++) {
2015 case '+': add = 1; break;
2016 case '-': add = 0; break;
2018 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2019 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2022 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2023 /* cheesy avoidance of looking up the flag name.. */
2024 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2027 flag = 1 << (pos - 1);
2029 added |= flag, removed &= ~flag;
2031 removed |= flag, added &= ~flag;
2036 *premoved = removed;
2041 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2043 unsigned long before, after, added, removed;
2044 struct userNode *uNode;
2046 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2047 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2049 hi->flags = (hi->flags | added) & ~removed;
2050 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2052 /* Strip helping flag if they're only a support helper and not
2053 * currently in #support. */
2054 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2055 struct channelList *schannels;
2057 schannels = chanserv_support_channels();
2058 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2059 for (ii = 0; ii < schannels->used; ++ii)
2060 if (GetUserMode(schannels->list[ii], uNode))
2062 if (ii < schannels->used)
2066 HANDLE_CLEAR_FLAG(hi, HELPING);
2069 if (after && !before) {
2070 /* Add user to current helper list. */
2071 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2072 userList_append(&curr_helpers, uNode);
2073 } else if (!after && before) {
2074 /* Remove user from current helper list. */
2075 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2076 userList_remove(&curr_helpers, uNode);
2083 set_list(struct userNode *user, struct handle_info *hi, int override)
2087 char *set_display[] = {
2088 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2089 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2092 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2094 /* Do this so options are presented in a consistent order. */
2095 for (i = 0; i < ArrayLength(set_display); ++i)
2096 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2097 opt(user, hi, override, 0, NULL);
2100 static NICKSERV_FUNC(cmd_set)
2102 struct handle_info *hi;
2105 hi = user->handle_info;
2107 set_list(user, hi, 0);
2110 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2111 reply("NSMSG_INVALID_OPTION", argv[1]);
2114 return opt(user, hi, 0, argc-1, argv+1);
2117 static NICKSERV_FUNC(cmd_oset)
2119 struct handle_info *hi;
2122 NICKSERV_MIN_PARMS(2);
2124 if (!(hi = get_victim_oper(user, argv[1])))
2128 set_list(user, hi, 0);
2132 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2133 reply("NSMSG_INVALID_OPTION", argv[2]);
2137 return opt(user, hi, 1, argc-2, argv+2);
2140 static OPTION_FUNC(opt_info)
2144 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2146 hi->infoline = NULL;
2148 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2152 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2153 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2157 static OPTION_FUNC(opt_width)
2160 hi->screen_width = strtoul(argv[1], NULL, 0);
2162 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2163 hi->screen_width = MIN_LINE_SIZE;
2164 else if (hi->screen_width > MAX_LINE_SIZE)
2165 hi->screen_width = MAX_LINE_SIZE;
2167 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2171 static OPTION_FUNC(opt_tablewidth)
2174 hi->table_width = strtoul(argv[1], NULL, 0);
2176 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2177 hi->table_width = MIN_LINE_SIZE;
2178 else if (hi->screen_width > MAX_LINE_SIZE)
2179 hi->table_width = MAX_LINE_SIZE;
2181 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2185 static OPTION_FUNC(opt_color)
2188 if (enabled_string(argv[1]))
2189 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2190 else if (disabled_string(argv[1]))
2191 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2193 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2198 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2202 static OPTION_FUNC(opt_privmsg)
2205 if (enabled_string(argv[1]))
2206 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2207 else if (disabled_string(argv[1]))
2208 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2210 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2215 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2219 static OPTION_FUNC(opt_style)
2224 if (!irccasecmp(argv[1], "Zoot"))
2225 hi->userlist_style = HI_STYLE_ZOOT;
2226 else if (!irccasecmp(argv[1], "def"))
2227 hi->userlist_style = HI_STYLE_DEF;
2230 switch (hi->userlist_style) {
2239 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2243 static OPTION_FUNC(opt_announcements)
2248 if (enabled_string(argv[1]))
2249 hi->announcements = 'y';
2250 else if (disabled_string(argv[1]))
2251 hi->announcements = 'n';
2252 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2253 hi->announcements = '?';
2255 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2260 switch (hi->announcements) {
2261 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2262 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2263 case '?': choice = "default"; break;
2264 default: choice = "unknown"; break;
2266 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2270 static OPTION_FUNC(opt_password)
2273 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2278 cryptpass(argv[1], hi->passwd);
2280 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2284 static OPTION_FUNC(opt_flags)
2287 unsigned int ii, flen;
2290 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2295 nickserv_apply_flags(user, hi, argv[1]);
2297 for (ii = flen = 0; handle_flags[ii]; ii++)
2298 if (hi->flags & (1 << ii))
2299 flags[flen++] = handle_flags[ii];
2302 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2304 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2308 static OPTION_FUNC(opt_email)
2312 if (!is_valid_email_addr(argv[1])) {
2313 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2316 if ((str = sendmail_prohibited_address(argv[1]))) {
2317 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2320 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2321 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2323 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2325 nickserv_set_email_addr(hi, argv[1]);
2327 nickserv_eat_cookie(hi->cookie);
2328 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2331 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2335 static OPTION_FUNC(opt_maxlogins)
2337 unsigned char maxlogins;
2339 maxlogins = strtoul(argv[1], NULL, 0);
2340 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2341 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2344 hi->maxlogins = maxlogins;
2346 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2347 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2351 static OPTION_FUNC(opt_language)
2353 struct language *lang;
2355 lang = language_find(argv[1]);
2356 if (irccasecmp(lang->name, argv[1]))
2357 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2358 hi->language = lang;
2360 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2365 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2366 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2368 if ((user->handle_info->opserv_level < target->opserv_level)
2369 || ((user->handle_info->opserv_level == target->opserv_level)
2370 && (user->handle_info->opserv_level < 1000))) {
2371 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2374 if ((user->handle_info->opserv_level < new_level)
2375 || ((user->handle_info->opserv_level == new_level)
2376 && (user->handle_info->opserv_level < 1000))) {
2377 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2380 if (user->handle_info == target) {
2381 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2384 if (target->opserv_level == new_level)
2386 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2387 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2388 target->opserv_level = new_level;
2392 static OPTION_FUNC(opt_level)
2397 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2401 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2402 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2406 static OPTION_FUNC(opt_epithet)
2409 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2413 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2414 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2417 if ((epithet[0] == '*') && !epithet[1])
2420 hi->epithet = strdup(epithet);
2424 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2426 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2430 static OPTION_FUNC(opt_title)
2435 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2439 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2441 if (strchr(title, '.')) {
2442 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2445 if ((strlen(user->handle_info->handle) + strlen(title) +
2446 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2447 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2452 if (!strcmp(title, "*")) {
2453 hi->fakehost = NULL;
2455 hi->fakehost = malloc(strlen(title)+2);
2456 hi->fakehost[0] = '.';
2457 strcpy(hi->fakehost+1, title);
2460 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2461 title = hi->fakehost + 1;
2465 title = user_find_message(user, "MSG_NONE");
2466 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2470 static OPTION_FUNC(opt_fakehost)
2475 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2479 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2481 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2482 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2486 if (!strcmp(fake, "*"))
2487 hi->fakehost = NULL;
2489 hi->fakehost = strdup(fake);
2490 fake = hi->fakehost;
2493 fake = generate_fakehost(hi);
2495 fake = user_find_message(user, "MSG_NONE");
2496 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2500 static NICKSERV_FUNC(cmd_reclaim)
2502 struct handle_info *hi;
2503 struct nick_info *ni;
2504 struct userNode *victim;
2506 NICKSERV_MIN_PARMS(2);
2507 hi = user->handle_info;
2508 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2510 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2513 if (ni->owner != user->handle_info) {
2514 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2517 victim = GetUserH(ni->nick);
2519 reply("MSG_NICK_UNKNOWN", ni->nick);
2522 if (victim == user) {
2523 reply("NSMSG_NICK_USER_YOU");
2526 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2527 switch (nickserv_conf.reclaim_action) {
2528 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2529 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2530 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2531 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2536 static NICKSERV_FUNC(cmd_unregnick)
2539 struct handle_info *hi;
2540 struct nick_info *ni;
2542 hi = user->handle_info;
2543 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2544 ni = dict_find(nickserv_nick_dict, nick, NULL);
2546 reply("NSMSG_UNKNOWN_NICK", nick);
2549 if (hi != ni->owner) {
2550 reply("NSMSG_NOT_YOUR_NICK", nick);
2553 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2558 static NICKSERV_FUNC(cmd_ounregnick)
2560 struct nick_info *ni;
2562 NICKSERV_MIN_PARMS(2);
2563 if (!(ni = get_nick_info(argv[1]))) {
2564 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2567 if (!oper_outranks(user, ni->owner))
2569 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2574 static NICKSERV_FUNC(cmd_unregister)
2576 struct handle_info *hi;
2579 NICKSERV_MIN_PARMS(2);
2580 hi = user->handle_info;
2583 if (checkpass(passwd, hi->passwd)) {
2584 nickserv_unregister_handle(hi, user);
2587 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2588 reply("NSMSG_PASSWORD_INVALID");
2593 static NICKSERV_FUNC(cmd_ounregister)
2595 struct handle_info *hi;
2596 char reason[MAXLEN];
2598 NICKSERV_MIN_PARMS(2);
2599 if (!(hi = get_victim_oper(user, argv[1])))
2601 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2602 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2603 nickserv_unregister_handle(hi, user);
2607 static NICKSERV_FUNC(cmd_status)
2609 if (nickserv_conf.disable_nicks) {
2610 reply("NSMSG_GLOBAL_STATS_NONICK",
2611 dict_size(nickserv_handle_dict));
2613 if (user->handle_info) {
2615 struct nick_info *ni;
2616 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2617 reply("NSMSG_HANDLE_STATS", cnt);
2619 reply("NSMSG_HANDLE_NONE");
2621 reply("NSMSG_GLOBAL_STATS",
2622 dict_size(nickserv_handle_dict),
2623 dict_size(nickserv_nick_dict));
2628 static NICKSERV_FUNC(cmd_ghost)
2630 struct userNode *target;
2631 char reason[MAXLEN];
2633 NICKSERV_MIN_PARMS(2);
2634 if (!(target = GetUserH(argv[1]))) {
2635 reply("MSG_NICK_UNKNOWN", argv[1]);
2638 if (target == user) {
2639 reply("NSMSG_CANNOT_GHOST_SELF");
2642 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2643 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2646 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2647 DelUser(target, nickserv, 1, reason);
2648 reply("NSMSG_GHOST_KILLED", argv[1]);
2652 static NICKSERV_FUNC(cmd_vacation)
2654 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2655 reply("NSMSG_ON_VACATION");
2660 nickserv_saxdb_write(struct saxdb_context *ctx) {
2662 struct handle_info *hi;
2665 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2667 #ifdef WITH_PROTOCOL_BAHAMUT
2670 saxdb_start_record(ctx, iter_key(it), 0);
2671 if (hi->announcements != '?') {
2672 flags[0] = hi->announcements;
2674 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2677 struct handle_cookie *cookie = hi->cookie;
2680 switch (cookie->type) {
2681 case ACTIVATION: type = KEY_ACTIVATION; break;
2682 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2683 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2684 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2685 default: type = NULL; break;
2688 saxdb_start_record(ctx, KEY_COOKIE, 0);
2689 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2690 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2692 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2693 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2694 saxdb_end_record(ctx);
2698 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2700 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2702 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2706 for (ii=flen=0; handle_flags[ii]; ++ii)
2707 if (hi->flags & (1 << ii))
2708 flags[flen++] = handle_flags[ii];
2710 saxdb_write_string(ctx, KEY_FLAGS, flags);
2712 #ifdef WITH_PROTOCOL_BAHAMUT
2713 saxdb_write_int(ctx, KEY_ID, hi->id);
2716 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2717 if (hi->last_quit_host[0])
2718 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2719 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2720 if (hi->masks->used)
2721 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2723 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2725 struct string_list *slist;
2726 struct nick_info *ni;
2728 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2729 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2730 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2734 if (hi->opserv_level)
2735 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2736 if (hi->language != lang_C)
2737 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2738 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2739 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2740 if (hi->screen_width)
2741 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2742 if (hi->table_width)
2743 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2744 flags[0] = hi->userlist_style;
2746 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2747 saxdb_end_record(ctx);
2752 static handle_merge_func_t *handle_merge_func_list;
2753 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2756 reg_handle_merge_func(handle_merge_func_t func)
2758 if (handle_merge_func_used == handle_merge_func_size) {
2759 if (handle_merge_func_size) {
2760 handle_merge_func_size <<= 1;
2761 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2763 handle_merge_func_size = 8;
2764 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2767 handle_merge_func_list[handle_merge_func_used++] = func;
2770 static NICKSERV_FUNC(cmd_merge)
2772 struct handle_info *hi_from, *hi_to;
2773 struct userNode *last_user;
2774 struct userData *cList, *cListNext;
2775 unsigned int ii, jj, n;
2776 char buffer[MAXLEN];
2778 NICKSERV_MIN_PARMS(3);
2780 if (!(hi_from = get_victim_oper(user, argv[1])))
2782 if (!(hi_to = get_victim_oper(user, argv[2])))
2784 if (hi_to == hi_from) {
2785 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2789 for (n=0; n<handle_merge_func_used; n++)
2790 handle_merge_func_list[n](user, hi_to, hi_from);
2792 /* Append "from" handle's nicks to "to" handle's nick list. */
2794 struct nick_info *last_ni;
2795 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2796 last_ni->next = hi_from->nicks;
2798 while (hi_from->nicks) {
2799 hi_from->nicks->owner = hi_to;
2800 hi_from->nicks = hi_from->nicks->next;
2803 /* Merge the hostmasks. */
2804 for (ii=0; ii<hi_from->masks->used; ii++) {
2805 char *mask = hi_from->masks->list[ii];
2806 for (jj=0; jj<hi_to->masks->used; jj++)
2807 if (match_ircglobs(hi_to->masks->list[jj], mask))
2809 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2810 string_list_append(hi_to->masks, strdup(mask));
2813 /* Merge the lists of authed users. */
2815 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2816 last_user->next_authed = hi_from->users;
2818 hi_to->users = hi_from->users;
2820 /* Repoint the old "from" handle's users. */
2821 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2822 last_user->handle_info = hi_to;
2824 hi_from->users = NULL;
2826 /* Merge channel userlists. */
2827 for (cList=hi_from->channels; cList; cList=cListNext) {
2828 struct userData *cList2;
2829 cListNext = cList->u_next;
2830 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2831 if (cList->channel == cList2->channel)
2833 if (cList2 && (cList2->access >= cList->access)) {
2834 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);
2835 /* keep cList2 in hi_to; remove cList from hi_from */
2836 del_channel_user(cList, 1);
2839 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);
2840 /* remove the lower-ranking cList2 from hi_to */
2841 del_channel_user(cList2, 1);
2843 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
2845 /* cList needs to be moved from hi_from to hi_to */
2846 cList->handle = hi_to;
2847 /* Remove from linked list for hi_from */
2848 assert(!cList->u_prev);
2849 hi_from->channels = cList->u_next;
2851 cList->u_next->u_prev = cList->u_prev;
2852 /* Add to linked list for hi_to */
2853 cList->u_prev = NULL;
2854 cList->u_next = hi_to->channels;
2855 if (hi_to->channels)
2856 hi_to->channels->u_prev = cList;
2857 hi_to->channels = cList;
2861 /* Do they get an OpServ level promotion? */
2862 if (hi_from->opserv_level > hi_to->opserv_level)
2863 hi_to->opserv_level = hi_from->opserv_level;
2865 /* What about last seen time? */
2866 if (hi_from->lastseen > hi_to->lastseen)
2867 hi_to->lastseen = hi_from->lastseen;
2869 /* Does a fakehost carry over? (This intentionally doesn't set it
2870 * for users previously attached to hi_to. They'll just have to
2873 if (hi_from->fakehost && !hi_to->fakehost)
2874 hi_to->fakehost = strdup(hi_from->fakehost);
2876 /* Notify of success. */
2877 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2878 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2879 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2881 /* Unregister the "from" handle. */
2882 nickserv_unregister_handle(hi_from, NULL);
2887 struct nickserv_discrim {
2888 unsigned int limit, min_level, max_level;
2889 unsigned long flags_on, flags_off;
2890 time_t min_registered, max_registered;
2892 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2893 const char *nickmask;
2894 const char *hostmask;
2895 const char *handlemask;
2896 const char *emailmask;
2899 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2901 struct discrim_apply_info {
2902 struct nickserv_discrim *discrim;
2903 discrim_search_func func;
2904 struct userNode *source;
2905 unsigned int matched;
2908 static struct nickserv_discrim *
2909 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2912 struct nickserv_discrim *discrim;
2914 discrim = malloc(sizeof(*discrim));
2915 memset(discrim, 0, sizeof(*discrim));
2916 discrim->min_level = 0;
2917 discrim->max_level = ~0;
2918 discrim->limit = 50;
2919 discrim->min_registered = 0;
2920 discrim->max_registered = INT_MAX;
2921 discrim->lastseen = now;
2923 for (i=0; i<argc; i++) {
2924 if (i == argc - 1) {
2925 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2928 if (!irccasecmp(argv[i], "limit")) {
2929 discrim->limit = strtoul(argv[++i], NULL, 0);
2930 } else if (!irccasecmp(argv[i], "flags")) {
2931 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2932 } else if (!irccasecmp(argv[i], "registered")) {
2933 const char *cmp = argv[++i];
2934 if (cmp[0] == '<') {
2935 if (cmp[1] == '=') {
2936 discrim->min_registered = now - ParseInterval(cmp+2);
2938 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2940 } else if (cmp[0] == '=') {
2941 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2942 } else if (cmp[0] == '>') {
2943 if (cmp[1] == '=') {
2944 discrim->max_registered = now - ParseInterval(cmp+2);
2946 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2949 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2951 } else if (!irccasecmp(argv[i], "seen")) {
2952 discrim->lastseen = now - ParseInterval(argv[++i]);
2953 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2954 discrim->nickmask = argv[++i];
2955 } else if (!irccasecmp(argv[i], "hostmask")) {
2957 if (!irccasecmp(argv[i], "exact")) {
2958 if (i == argc - 1) {
2959 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2962 discrim->hostmask_type = EXACT;
2963 } else if (!irccasecmp(argv[i], "subset")) {
2964 if (i == argc - 1) {
2965 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2968 discrim->hostmask_type = SUBSET;
2969 } else if (!irccasecmp(argv[i], "superset")) {
2970 if (i == argc - 1) {
2971 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2974 discrim->hostmask_type = SUPERSET;
2975 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2976 if (i == argc - 1) {
2977 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2980 discrim->hostmask_type = LASTQUIT;
2983 discrim->hostmask_type = SUPERSET;
2985 discrim->hostmask = argv[++i];
2986 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2987 if (!irccasecmp(argv[++i], "*")) {
2988 discrim->handlemask = 0;
2990 discrim->handlemask = argv[i];
2992 } else if (!irccasecmp(argv[i], "email")) {
2993 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2994 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2996 } else if (!irccasecmp(argv[++i], "*")) {
2997 discrim->emailmask = 0;
2999 discrim->emailmask = argv[i];
3001 } else if (!irccasecmp(argv[i], "access")) {
3002 const char *cmp = argv[++i];
3003 if (cmp[0] == '<') {
3004 if (discrim->min_level == 0) discrim->min_level = 1;
3005 if (cmp[1] == '=') {
3006 discrim->max_level = strtoul(cmp+2, NULL, 0);
3008 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3010 } else if (cmp[0] == '=') {
3011 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3012 } else if (cmp[0] == '>') {
3013 if (cmp[1] == '=') {
3014 discrim->min_level = strtoul(cmp+2, NULL, 0);
3016 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3019 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3022 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3033 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3035 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3036 || (discrim->flags_off & hi->flags)
3037 || (discrim->min_registered > hi->registered)
3038 || (discrim->max_registered < hi->registered)
3039 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3040 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3041 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3042 || (discrim->min_level > hi->opserv_level)
3043 || (discrim->max_level < hi->opserv_level)) {
3046 if (discrim->hostmask) {
3048 for (i=0; i<hi->masks->used; i++) {
3049 const char *mask = hi->masks->list[i];
3050 if ((discrim->hostmask_type == SUBSET)
3051 && (match_ircglobs(discrim->hostmask, mask))) break;
3052 else if ((discrim->hostmask_type == EXACT)
3053 && !irccasecmp(discrim->hostmask, mask)) break;
3054 else if ((discrim->hostmask_type == SUPERSET)
3055 && (match_ircglobs(mask, discrim->hostmask))) break;
3056 else if ((discrim->hostmask_type == LASTQUIT)
3057 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3059 if (i==hi->masks->used) return 0;
3061 if (discrim->nickmask) {
3062 struct nick_info *nick = hi->nicks;
3064 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3067 if (!nick) return 0;
3073 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3075 dict_iterator_t it, next;
3076 unsigned int matched;
3078 for (it = dict_first(nickserv_handle_dict), matched = 0;
3079 it && (matched < discrim->limit);
3081 next = iter_next(it);
3082 if (nickserv_discrim_match(discrim, iter_data(it))) {
3083 dsf(source, iter_data(it));
3091 search_print_func(struct userNode *source, struct handle_info *match)
3093 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3097 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3102 search_unregister_func (struct userNode *source, struct handle_info *match)
3104 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3105 nickserv_unregister_handle(match, source);
3109 nickserv_sort_accounts_by_access(const void *a, const void *b)
3111 const struct handle_info *hi_a = *(const struct handle_info**)a;
3112 const struct handle_info *hi_b = *(const struct handle_info**)b;
3113 if (hi_a->opserv_level != hi_b->opserv_level)
3114 return hi_b->opserv_level - hi_a->opserv_level;
3115 return irccasecmp(hi_a->handle, hi_b->handle);
3119 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3121 struct handle_info_list hil;
3122 struct helpfile_table tbl;
3127 memset(&hil, 0, sizeof(hil));
3128 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3129 struct handle_info *hi = iter_data(it);
3130 if (hi->opserv_level)
3131 handle_info_list_append(&hil, hi);
3133 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3134 tbl.length = hil.used + 1;
3136 tbl.flags = TABLE_NO_FREE;
3137 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3138 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3141 for (ii = 0; ii < hil.used; ) {
3142 ary = malloc(tbl.width * sizeof(ary[0]));
3143 ary[0] = hil.list[ii]->handle;
3144 ary[1] = strtab(hil.list[ii]->opserv_level);
3145 tbl.contents[++ii] = ary;
3147 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3148 reply("MSG_MATCH_COUNT", hil.used);
3149 for (ii = 0; ii < hil.used; ii++)
3150 free(tbl.contents[ii]);
3155 static NICKSERV_FUNC(cmd_search)
3157 struct nickserv_discrim *discrim;
3158 discrim_search_func action;
3159 struct svccmd *subcmd;
3160 unsigned int matches;
3163 NICKSERV_MIN_PARMS(3);
3164 sprintf(buf, "search %s", argv[1]);
3165 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3166 if (!irccasecmp(argv[1], "print"))
3167 action = search_print_func;
3168 else if (!irccasecmp(argv[1], "count"))
3169 action = search_count_func;
3170 else if (!irccasecmp(argv[1], "unregister"))
3171 action = search_unregister_func;
3173 reply("NSMSG_INVALID_ACTION", argv[1]);
3177 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3180 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3184 if (action == search_print_func)
3185 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3186 else if (action == search_count_func)
3187 discrim->limit = INT_MAX;
3189 matches = nickserv_discrim_search(discrim, action, user);
3192 reply("MSG_MATCH_COUNT", matches);
3194 reply("MSG_NO_MATCHES");
3200 static MODCMD_FUNC(cmd_checkpass)
3202 struct handle_info *hi;
3204 NICKSERV_MIN_PARMS(3);
3205 if (!(hi = get_handle_info(argv[1]))) {
3206 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3209 if (checkpass(argv[2], hi->passwd))
3210 reply("CHECKPASS_YES");
3212 reply("CHECKPASS_NO");
3218 nickserv_db_read_handle(const char *handle, dict_t obj)
3221 struct string_list *masks, *slist;
3222 struct handle_info *hi;
3223 struct userNode *authed_users;
3224 struct userData *channels;
3225 unsigned long int id;
3229 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3230 id = str ? strtoul(str, NULL, 0) : 0;
3231 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3233 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3236 if ((hi = get_handle_info(handle))) {
3237 authed_users = hi->users;
3238 channels = hi->channels;
3240 hi->channels = NULL;
3241 dict_remove(nickserv_handle_dict, hi->handle);
3243 authed_users = NULL;
3246 hi = register_handle(handle, str, id);
3248 hi->users = authed_users;
3249 while (authed_users) {
3250 authed_users->handle_info = hi;
3251 authed_users = authed_users->next_authed;
3254 hi->channels = channels;
3255 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3256 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3257 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3258 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3259 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3260 hi->language = language_find(str ? str : "C");
3261 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3262 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3263 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3265 hi->infoline = strdup(str);
3266 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3267 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3268 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3269 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3270 /* We want to read the nicks even if disable_nicks is set. This is so
3271 * that we don't lose the nick data entirely. */
3272 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3274 for (ii=0; ii<slist->used; ii++)
3275 register_nick(slist->list[ii], hi);
3277 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3279 for (ii=0; str[ii]; ii++)
3280 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3282 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3283 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3284 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3285 hi->announcements = str ? str[0] : '?';
3286 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3287 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3288 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3289 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3290 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3292 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3294 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3295 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3297 nickserv_set_email_addr(hi, str);
3298 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3300 hi->epithet = strdup(str);
3301 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3303 hi->fakehost = strdup(str);
3304 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3306 const char *data, *type, *expires, *cookie_str;
3307 struct handle_cookie *cookie;
3309 cookie = calloc(1, sizeof(*cookie));
3310 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3311 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3312 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3313 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3314 if (!type || !expires || !cookie_str) {
3315 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3318 if (!irccasecmp(type, KEY_ACTIVATION))
3319 cookie->type = ACTIVATION;
3320 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3321 cookie->type = PASSWORD_CHANGE;
3322 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3323 cookie->type = EMAIL_CHANGE;
3324 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3325 cookie->type = ALLOWAUTH;
3327 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3330 cookie->expires = strtoul(expires, NULL, 0);
3331 if (cookie->expires < now)
3334 cookie->data = strdup(data);
3335 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3339 nickserv_bake_cookie(cookie);
3341 nickserv_free_cookie(cookie);
3346 nickserv_saxdb_read(dict_t db) {
3348 struct record_data *rd;
3350 for (it=dict_first(db); it; it=iter_next(it)) {
3352 nickserv_db_read_handle(iter_key(it), rd->d.object);
3357 static NICKSERV_FUNC(cmd_mergedb)
3359 struct timeval start, stop;
3362 NICKSERV_MIN_PARMS(2);
3363 gettimeofday(&start, NULL);
3364 if (!(db = parse_database(argv[1]))) {
3365 reply("NSMSG_DB_UNREADABLE", argv[1]);
3368 nickserv_saxdb_read(db);
3370 gettimeofday(&stop, NULL);
3371 stop.tv_sec -= start.tv_sec;
3372 stop.tv_usec -= start.tv_usec;
3373 if (stop.tv_usec < 0) {
3375 stop.tv_usec += 1000000;
3377 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3382 expire_handles(UNUSED_ARG(void *data))
3384 dict_iterator_t it, next;
3386 struct handle_info *hi;
3388 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3389 next = iter_next(it);
3391 if ((hi->opserv_level > 0)
3393 || HANDLE_FLAGGED(hi, FROZEN)
3394 || HANDLE_FLAGGED(hi, NODELETE)) {
3397 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3398 if ((now - hi->lastseen) > expiry) {
3399 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3400 nickserv_unregister_handle(hi, NULL);
3404 if (nickserv_conf.handle_expire_frequency)
3405 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3409 nickserv_load_dict(const char *fname)
3413 if (!(file = fopen(fname, "r"))) {
3414 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3417 while (!feof(file)) {
3418 fgets(line, sizeof(line), file);
3421 if (line[strlen(line)-1] == '\n')
3422 line[strlen(line)-1] = 0;
3423 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3426 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3429 static enum reclaim_action
3430 reclaim_action_from_string(const char *str) {
3432 return RECLAIM_NONE;
3433 else if (!irccasecmp(str, "warn"))
3434 return RECLAIM_WARN;
3435 else if (!irccasecmp(str, "svsnick"))
3436 return RECLAIM_SVSNICK;
3437 else if (!irccasecmp(str, "kill"))
3438 return RECLAIM_KILL;
3440 return RECLAIM_NONE;
3444 nickserv_conf_read(void)
3446 dict_t conf_node, child;
3450 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3451 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3454 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3456 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3457 if (nickserv_conf.valid_handle_regex_set)
3458 regfree(&nickserv_conf.valid_handle_regex);
3460 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3461 nickserv_conf.valid_handle_regex_set = !err;
3462 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3464 nickserv_conf.valid_handle_regex_set = 0;
3466 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3467 if (nickserv_conf.valid_nick_regex_set)
3468 regfree(&nickserv_conf.valid_nick_regex);
3470 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3471 nickserv_conf.valid_nick_regex_set = !err;
3472 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3474 nickserv_conf.valid_nick_regex_set = 0;
3476 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3478 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3479 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3480 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3481 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3482 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3483 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3484 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3485 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3486 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3487 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3488 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3489 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3490 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3491 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3492 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3493 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3494 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3495 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3496 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3497 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3498 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3499 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3500 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3501 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3502 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3504 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3505 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3506 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3508 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3509 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3510 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3512 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3513 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3514 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3515 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3516 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3517 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3518 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3519 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3520 if (!nickserv_conf.disable_nicks) {
3521 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3522 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3523 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3524 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3525 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3526 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3527 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3528 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3530 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3531 for (it=dict_first(child); it; it=iter_next(it)) {
3532 const char *key = iter_key(it), *value;
3536 if (!strncasecmp(key, "uc_", 3))
3537 flag = toupper(key[3]);
3538 else if (!strncasecmp(key, "lc_", 3))
3539 flag = tolower(key[3]);
3543 if ((pos = handle_inverse_flags[flag])) {
3544 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3545 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3548 if (nickserv_conf.weak_password_dict)
3549 dict_delete(nickserv_conf.weak_password_dict);
3550 nickserv_conf.weak_password_dict = dict_new();
3551 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3552 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3553 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3554 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3556 nickserv_load_dict(str);
3557 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3558 if (nickserv && str)
3559 NickChange(nickserv, str, 0);
3560 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3561 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3562 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3563 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3564 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3565 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3566 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3567 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3568 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3569 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3570 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3571 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3572 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3573 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3574 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3575 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3576 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3577 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3578 str = conf_get_data("server/network", RECDB_QSTRING);
3579 nickserv_conf.network_name = str ? str : "some IRC network";
3580 if (!nickserv_conf.auth_policer_params) {
3581 nickserv_conf.auth_policer_params = policer_params_new();
3582 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3583 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3585 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3586 for (it=dict_first(child); it; it=iter_next(it))
3587 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3591 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3593 char newnick[NICKLEN+1];
3602 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3604 case RECLAIM_SVSNICK:
3606 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3607 } while (GetUserH(newnick));
3608 irc_svsnick(nickserv, user, newnick);
3611 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3612 DelUser(user, nickserv, 1, msg);
3618 nickserv_reclaim_p(void *data) {
3619 struct userNode *user = data;
3620 struct nick_info *ni = get_nick_info(user->nick);
3622 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3626 check_user_nick(struct userNode *user) {
3627 struct nick_info *ni;
3628 user->modes &= ~FLAGS_REGNICK;
3629 if (!(ni = get_nick_info(user->nick)))
3631 if (user->handle_info == ni->owner) {
3632 user->modes |= FLAGS_REGNICK;
3636 if (nickserv_conf.warn_nick_owned)
3637 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3638 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3640 if (nickserv_conf.auto_reclaim_delay)
3641 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3643 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3648 handle_new_user(struct userNode *user)
3650 return check_user_nick(user);
3654 handle_account(struct userNode *user, const char *stamp)
3656 struct handle_info *hi;
3658 #ifdef WITH_PROTOCOL_P10
3659 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3661 hi = dict_find(nickserv_id_dict, stamp, NULL);
3665 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3668 set_user_handle_info(user, hi, 0);
3670 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3675 handle_nick_change(struct userNode *user, const char *old_nick)
3677 struct handle_info *hi;
3679 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3680 dict_remove(nickserv_allow_auth_dict, old_nick);
3681 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3683 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3684 check_user_nick(user);
3688 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3690 dict_remove(nickserv_allow_auth_dict, user->nick);
3691 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3692 set_user_handle_info(user, NULL, 0);
3695 static struct modcmd *
3696 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3698 if (min_level > 0) {
3700 sprintf(buf, "%u", min_level);
3701 if (must_be_qualified) {
3702 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3704 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3706 } else if (min_level == 0) {
3707 if (must_be_qualified) {
3708 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3710 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3713 if (must_be_qualified) {
3714 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3716 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3722 nickserv_db_cleanup(void)
3724 unreg_del_user_func(nickserv_remove_user);
3725 userList_clean(&curr_helpers);
3726 policer_params_delete(nickserv_conf.auth_policer_params);
3727 dict_delete(nickserv_handle_dict);
3728 dict_delete(nickserv_nick_dict);
3729 dict_delete(nickserv_opt_dict);
3730 dict_delete(nickserv_allow_auth_dict);
3731 dict_delete(nickserv_email_dict);
3732 dict_delete(nickserv_id_dict);
3733 dict_delete(nickserv_conf.weak_password_dict);
3734 free(auth_func_list);
3735 free(unreg_func_list);
3737 free(allowauth_func_list);
3738 free(handle_merge_func_list);
3739 free(failpw_func_list);
3740 if (nickserv_conf.valid_handle_regex_set)
3741 regfree(&nickserv_conf.valid_handle_regex);
3742 if (nickserv_conf.valid_nick_regex_set)
3743 regfree(&nickserv_conf.valid_nick_regex);
3747 init_nickserv(const char *nick)
3750 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3751 reg_new_user_func(handle_new_user);
3752 reg_nick_change_func(handle_nick_change);
3753 reg_del_user_func(nickserv_remove_user);
3754 reg_account_func(handle_account);
3756 /* set up handle_inverse_flags */
3757 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3758 for (i=0; handle_flags[i]; i++) {
3759 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3760 flag_access_levels[i] = 0;
3763 conf_register_reload(nickserv_conf_read);
3764 nickserv_opt_dict = dict_new();
3765 nickserv_email_dict = dict_new();
3766 dict_set_free_keys(nickserv_email_dict, free);
3767 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3769 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3770 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3771 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3772 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3773 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3774 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3775 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3776 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3777 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3778 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3779 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3780 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3781 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3782 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3783 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3784 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3785 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3786 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3787 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3788 if (!nickserv_conf.disable_nicks) {
3789 /* nick management commands */
3790 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3791 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3792 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3793 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3794 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3795 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3797 if (nickserv_conf.email_enabled) {
3798 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3799 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3800 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3801 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3802 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3804 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3805 /* miscellaneous commands */
3806 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3807 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3808 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3809 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3810 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3812 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3813 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3814 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3815 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3816 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3817 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3818 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3819 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3820 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3821 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3822 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3823 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3824 if (nickserv_conf.titlehost_suffix) {
3825 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
3826 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
3828 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3829 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3830 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3832 nickserv_handle_dict = dict_new();
3833 dict_set_free_keys(nickserv_handle_dict, free);
3834 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3836 nickserv_id_dict = dict_new();
3837 dict_set_free_keys(nickserv_id_dict, free);
3839 nickserv_nick_dict = dict_new();
3840 dict_set_free_data(nickserv_nick_dict, free);
3842 nickserv_allow_auth_dict = dict_new();
3844 userList_init(&curr_helpers);
3847 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
3848 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
3849 nickserv_service = service_register(nickserv);
3851 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3852 reg_exit_func(nickserv_db_cleanup);
3853 if(nickserv_conf.handle_expire_frequency)
3854 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3855 message_register_table(msgtab);