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_MAXLOGINS "maxlogins"
100 #define KEY_FAKEHOST "fakehost"
101 #define KEY_NOTES "notes"
102 #define KEY_NOTE_EXPIRES "expires"
103 #define KEY_NOTE_SET "set"
104 #define KEY_NOTE_SETTER "setter"
105 #define KEY_NOTE_NOTE "note"
106 #define KEY_KARMA "karma"
108 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
110 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
111 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
112 typedef OPTION_FUNC(option_func_t);
114 DEFINE_LIST(handle_info_list, struct handle_info*);
116 #define NICKSERV_MIN_PARMS(N) do { \
118 reply("MSG_MISSING_PARAMS", argv[0]); \
119 svccmd_send_help(user, nickserv, cmd); \
123 struct userNode *nickserv;
124 struct userList curr_helpers;
125 const char *handle_flags = HANDLE_FLAGS;
127 static struct module *nickserv_module;
128 static struct service *nickserv_service;
129 static struct log_type *NS_LOG;
130 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
131 static dict_t nickserv_id_dict; /* contains struct handle_info* */
132 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
133 static dict_t nickserv_opt_dict; /* contains option_func_t* */
134 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
135 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
136 static char handle_inverse_flags[256];
137 static unsigned int flag_access_levels[32];
138 static const struct message_entry msgtab[] = {
139 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
140 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
141 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
142 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
143 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
144 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
145 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
146 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
147 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
148 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
149 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
150 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
151 { "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." },
152 { "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." },
153 { "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." },
154 { "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." },
155 { "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." },
156 { "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." },
157 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
158 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
159 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
160 { "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." },
161 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
162 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
163 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
164 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
165 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
166 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
167 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
168 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
169 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
170 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
171 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
172 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
173 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
174 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
175 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
176 { "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)" },
177 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
178 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
179 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
180 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
181 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
182 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
183 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
184 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
185 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
186 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
187 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
188 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
189 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
190 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
191 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
192 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
193 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
194 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
195 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
196 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
197 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
198 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
199 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
200 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
201 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
202 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
203 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
204 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
205 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
206 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
207 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
208 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
209 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
210 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
211 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
212 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
213 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
214 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
215 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
216 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
217 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
218 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
219 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
220 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
221 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
222 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
223 { "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)." },
224 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
225 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
226 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
227 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
228 { "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." },
229 { "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." },
230 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
231 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
232 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
233 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
234 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
235 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
236 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
237 { "NSMSG_PASS_SUCCESS", "Password changed." },
238 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
239 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
240 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
241 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
242 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
243 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
244 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
245 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
246 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
247 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
248 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
249 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
250 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
251 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
252 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
253 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
254 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
255 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
256 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
257 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
258 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
259 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
260 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
261 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
262 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
263 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
264 { "NSMSG_NO_ACCESS", "Access denied." },
265 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
266 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
267 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
268 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
269 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
270 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
271 { "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." },
272 { "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." },
273 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
274 { "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." },
275 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
276 { "NSMSG_SEARCH_MATCH", "Match: %s" },
277 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
278 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
279 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
280 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
281 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
282 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
283 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
284 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
285 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
286 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
287 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
288 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
289 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
290 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
291 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
292 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
293 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
294 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
295 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
296 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
297 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
298 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
299 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
300 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
301 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
302 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
303 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
304 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
305 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
306 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
307 { "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." },
308 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
309 { "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." },
310 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
311 { "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." },
312 { "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." },
313 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
314 { "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." },
315 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
316 { "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." },
317 { "CHECKPASS_YES", "Yes." },
318 { "CHECKPASS_NO", "No." },
322 enum reclaim_action {
328 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
329 static void nickserv_reclaim_p(void *data);
330 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
333 unsigned int disable_nicks : 1;
334 unsigned int valid_handle_regex_set : 1;
335 unsigned int valid_nick_regex_set : 1;
336 unsigned int autogag_enabled : 1;
337 unsigned int email_enabled : 1;
338 unsigned int email_required : 1;
339 unsigned int default_hostmask : 1;
340 unsigned int warn_nick_owned : 1;
341 unsigned int warn_clone_auth : 1;
342 unsigned long nicks_per_handle;
343 unsigned long password_min_length;
344 unsigned long password_min_digits;
345 unsigned long password_min_upper;
346 unsigned long password_min_lower;
347 unsigned long db_backup_frequency;
348 unsigned long handle_expire_frequency;
349 unsigned long autogag_duration;
350 unsigned long email_visible_level;
351 unsigned long cookie_timeout;
352 unsigned long handle_expire_delay;
353 unsigned long nochan_handle_expire_delay;
354 unsigned long modoper_level;
355 unsigned long set_epithet_level;
356 unsigned long set_title_level;
357 unsigned long set_fakehost_level;
358 unsigned long handles_per_email;
359 unsigned long email_search_level;
360 const char *network_name;
361 const char *titlehost_suffix;
362 regex_t valid_handle_regex;
363 regex_t valid_nick_regex;
364 dict_t weak_password_dict;
365 struct policer_params *auth_policer_params;
366 enum reclaim_action reclaim_action;
367 enum reclaim_action auto_reclaim_action;
368 unsigned long auto_reclaim_delay;
369 unsigned char default_maxlogins;
370 unsigned char hard_maxlogins;
373 /* We have 2^32 unique account IDs to use. */
374 unsigned long int highest_id = 0;
376 #define WALK_NOTES(HANDLE, PREV, NOTE) \
377 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
378 if (NOTE->expires && NOTE->expires < now) { \
379 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
381 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
385 canonicalize_hostmask(char *mask)
387 char *out = mask, *temp;
388 if ((temp = strchr(mask, '!'))) {
390 while (*temp) *out++ = *temp++;
396 static struct handle_info *
397 register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
399 struct handle_info *hi;
401 #ifdef WITH_PROTOCOL_BAHAMUT
402 char id_base64[IDLEN + 1];
405 /* Assign a unique account ID to the account; note that 0 is
406 an invalid account ID. 1 is therefore the first account ID. */
408 id = 1 + highest_id++;
410 /* Note: highest_id is and must always be the highest ID. */
411 if (id > highest_id) {
415 inttobase64(id_base64, id, IDLEN);
417 /* Make sure an account with the same ID doesn't exist. If a
418 duplicate is found, log some details and assign a new one.
419 This should be impossible, but it never hurts to expect it. */
420 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
421 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
427 hi = calloc(1, sizeof(*hi));
428 hi->userlist_style = HI_DEFAULT_STYLE;
429 hi->handle = strdup(handle);
430 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
432 dict_insert(nickserv_handle_dict, hi->handle, hi);
434 #ifdef WITH_PROTOCOL_BAHAMUT
436 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
443 register_nick(const char *nick, struct handle_info *owner)
445 struct nick_info *ni;
446 ni = malloc(sizeof(struct nick_info));
447 safestrncpy(ni->nick, nick, sizeof(ni->nick));
449 ni->next = owner->nicks;
451 dict_insert(nickserv_nick_dict, ni->nick, ni);
455 delete_nick(struct nick_info *ni)
457 struct nick_info *last, *next;
458 struct userNode *user;
459 /* Check to see if we should mark a user as unregistered. */
460 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
461 user->modes &= ~FLAGS_REGNICK;
464 /* Remove ni from the nick_info linked list. */
465 if (ni == ni->owner->nicks) {
466 ni->owner->nicks = ni->next;
468 last = ni->owner->nicks;
474 last->next = next->next;
476 dict_remove(nickserv_nick_dict, ni->nick);
479 static unreg_func_t *unreg_func_list;
480 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
483 reg_unreg_func(unreg_func_t func)
485 if (unreg_func_used == unreg_func_size) {
486 if (unreg_func_size) {
487 unreg_func_size <<= 1;
488 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
491 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
494 unreg_func_list[unreg_func_used++] = func;
498 nickserv_free_cookie(void *data)
500 struct handle_cookie *cookie = data;
501 if (cookie->hi) cookie->hi->cookie = NULL;
502 if (cookie->data) free(cookie->data);
507 free_handle_info(void *vhi)
509 struct handle_info *hi = vhi;
511 #ifdef WITH_PROTOCOL_BAHAMUT
514 inttobase64(id, hi->id, IDLEN);
515 dict_remove(nickserv_id_dict, id);
518 free_string_list(hi->masks);
522 delete_nick(hi->nicks);
527 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
528 nickserv_free_cookie(hi->cookie);
531 struct handle_note *note = hi->notes;
532 hi->notes = note->next;
535 if (hi->email_addr) {
536 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
537 handle_info_list_remove(hil, hi);
539 dict_remove(nickserv_email_dict, hi->email_addr);
544 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
547 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
551 for (n=0; n<unreg_func_used; n++)
552 unreg_func_list[n](notify, hi);
554 set_user_handle_info(hi->users, NULL, 0);
556 if (nickserv_conf.disable_nicks)
557 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
559 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
561 dict_remove(nickserv_handle_dict, hi->handle);
565 get_handle_info(const char *handle)
567 return dict_find(nickserv_handle_dict, handle, 0);
571 get_nick_info(const char *nick)
573 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
577 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
582 for (nn=0; nn<channel->members.used; ++nn) {
583 mn = channel->members.list[nn];
584 if ((mn->user != except) && (mn->user->handle_info == handle))
591 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
592 if (!user->handle_info) {
594 send_message(user, bot, "MSG_AUTHENTICATE");
598 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
600 send_message(user, bot, "NSMSG_NO_ACCESS");
604 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
606 send_message(user, bot, "MSG_OPER_SUSPENDED");
610 if (user->handle_info->opserv_level < min_level) {
612 send_message(user, bot, "NSMSG_NO_ACCESS");
620 is_valid_handle(const char *handle)
622 struct userNode *user;
623 /* cant register a juped nick/service nick as handle, to prevent confusion */
624 user = GetUserH(handle);
625 if (user && IsLocal(user))
627 /* check against maximum length */
628 if (strlen(handle) > NICKSERV_HANDLE_LEN)
630 /* for consistency, only allow account names that could be nicks */
631 if (!is_valid_nick(handle))
633 /* disallow account names that look like bad words */
634 if (opserv_bad_channel(handle))
636 /* test either regex or containing all valid chars */
637 if (nickserv_conf.valid_handle_regex_set) {
638 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
641 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
642 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
646 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
651 is_registerable_nick(const char *nick)
653 /* make sure it could be used as an account name */
654 if (!is_valid_handle(nick))
657 if (strlen(nick) > NICKLEN)
659 /* test either regex or as valid handle */
660 if (nickserv_conf.valid_nick_regex_set) {
661 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
664 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
665 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
673 is_valid_email_addr(const char *email)
675 return strchr(email, '@') != NULL;
679 visible_email_addr(struct userNode *user, struct handle_info *hi)
681 if (hi->email_addr) {
682 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
683 return hi->email_addr;
693 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
695 struct handle_info *hi;
696 struct userNode *target;
700 if (!(hi = get_handle_info(++name))) {
701 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
706 if (!(target = GetUserH(name))) {
707 send_message(user, service, "MSG_NICK_UNKNOWN", name);
710 if (IsLocal(target)) {
711 if (IsService(target))
712 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
714 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
717 if (!(hi = target->handle_info)) {
718 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
726 oper_outranks(struct userNode *user, struct handle_info *hi) {
727 if (user->handle_info->opserv_level > hi->opserv_level)
729 if (user->handle_info->opserv_level == hi->opserv_level) {
730 if ((user->handle_info->opserv_level == 1000)
731 || (user->handle_info == hi)
732 || ((user->handle_info->opserv_level == 0)
733 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
734 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
738 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
742 static struct handle_info *
743 get_victim_oper(struct userNode *user, const char *target)
745 struct handle_info *hi;
746 if (!(hi = smart_get_handle_info(nickserv, user, target)))
748 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
749 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
752 return oper_outranks(user, hi) ? hi : NULL;
756 valid_user_for(struct userNode *user, struct handle_info *hi)
760 /* If no hostmasks on the account, allow it. */
761 if (!hi->masks->used)
763 /* If any hostmask matches, allow it. */
764 for (ii=0; ii<hi->masks->used; ii++)
765 if (user_matches_glob(user, hi->masks->list[ii], 0))
767 /* If they are allowauthed to this account, allow it (removing the aa). */
768 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
769 dict_remove(nickserv_allow_auth_dict, user->nick);
772 /* The user is not allowed to use this account. */
777 is_secure_password(const char *handle, const char *pass, struct userNode *user)
780 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
784 if (len < nickserv_conf.password_min_length) {
786 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
789 if (!irccasecmp(pass, handle)) {
791 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
794 dict_find(nickserv_conf.weak_password_dict, pass, &p);
797 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
800 for (i=0; i<len; i++) {
801 if (isdigit(pass[i]))
803 if (isupper(pass[i]))
805 if (islower(pass[i]))
808 if ((cnt_lower < nickserv_conf.password_min_lower)
809 || (cnt_upper < nickserv_conf.password_min_upper)
810 || (cnt_digits < nickserv_conf.password_min_digits)) {
812 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
818 static auth_func_t *auth_func_list;
819 static unsigned int auth_func_size = 0, auth_func_used = 0;
822 reg_auth_func(auth_func_t func)
824 if (auth_func_used == auth_func_size) {
825 if (auth_func_size) {
826 auth_func_size <<= 1;
827 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
830 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
833 auth_func_list[auth_func_used++] = func;
836 static handle_rename_func_t *rf_list;
837 static unsigned int rf_list_size, rf_list_used;
840 reg_handle_rename_func(handle_rename_func_t func)
842 if (rf_list_used == rf_list_size) {
845 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
848 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
851 rf_list[rf_list_used++] = func;
855 generate_fakehost(struct handle_info *handle)
857 extern const char *hidden_host_suffix;
858 static char buffer[HOSTLEN+1];
860 if (!handle->fakehost) {
861 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
863 } else if (handle->fakehost[0] == '.') {
864 /* A leading dot indicates the stored value is actually a title. */
865 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
868 return handle->fakehost;
872 apply_fakehost(struct handle_info *handle)
874 struct userNode *target;
879 fake = generate_fakehost(handle);
880 for (target = handle->users; target; target = target->next_authed)
881 assign_fakehost(target, fake, 1);
885 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
888 struct handle_info *old_info;
890 /* This can happen if somebody uses COOKIE while authed, or if
891 * they re-auth to their current handle (which is silly, but users
893 if (user->handle_info == hi)
896 if (user->handle_info) {
897 struct userNode *other;
900 userList_remove(&curr_helpers, user);
902 /* remove from next_authed linked list */
903 if (user->handle_info->users == user) {
904 user->handle_info->users = user->next_authed;
906 for (other = user->handle_info->users;
907 other->next_authed != user;
908 other = other->next_authed) ;
909 other->next_authed = user->next_authed;
911 /* if nobody left on old handle, and they're not an oper, remove !god */
912 if (!user->handle_info->users && !user->handle_info->opserv_level)
913 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
914 /* record them as being last seen at this time */
915 user->handle_info->lastseen = now;
916 /* and record their hostmask */
917 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
919 old_info = user->handle_info;
920 user->handle_info = hi;
921 if (hi && !hi->users && !hi->opserv_level)
922 HANDLE_CLEAR_FLAG(hi, HELPING);
923 for (n=0; n<auth_func_used; n++)
924 auth_func_list[n](user, old_info);
926 struct nick_info *ni;
928 HANDLE_CLEAR_FLAG(hi, FROZEN);
929 if (nickserv_conf.warn_clone_auth) {
930 struct userNode *other;
931 for (other = hi->users; other; other = other->next_authed)
932 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
934 user->next_authed = hi->users;
938 userList_append(&curr_helpers, user);
940 if (hi->fakehost || old_info)
944 #ifdef WITH_PROTOCOL_BAHAMUT
945 /* Stamp users with their account ID. */
947 inttobase64(id, hi->id, IDLEN);
948 #elif WITH_PROTOCOL_P10
949 /* Stamp users with their account name. */
950 char *id = hi->handle;
952 const char *id = "???";
954 if (!nickserv_conf.disable_nicks) {
955 struct nick_info *ni;
956 for (ni = hi->nicks; ni; ni = ni->next) {
957 if (!irccasecmp(user->nick, ni->nick)) {
958 user->modes |= FLAGS_REGNICK;
966 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
967 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
969 /* We cannot clear the user's account ID, unfortunately. */
970 user->next_authed = NULL;
974 static struct handle_info*
975 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
977 struct handle_info *hi;
978 struct nick_info *ni;
979 char crypted[MD5_CRYPT_LENGTH];
981 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
982 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
986 if (!is_secure_password(handle, passwd, user))
989 cryptpass(passwd, crypted);
990 hi = register_handle(handle, crypted, 0);
991 hi->masks = alloc_string_list(1);
993 hi->language = lang_C;
994 hi->registered = now;
996 hi->flags = HI_DEFAULT_FLAGS;
997 if (settee && !no_auth)
998 set_user_handle_info(settee, hi, 1);
1001 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1002 else if (nickserv_conf.disable_nicks)
1003 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1004 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1005 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1007 register_nick(user->nick, hi);
1008 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1010 if (settee && (user != settee))
1011 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1016 nickserv_bake_cookie(struct handle_cookie *cookie)
1018 cookie->hi->cookie = cookie;
1019 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1023 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1025 struct handle_cookie *cookie;
1026 char subject[128], body[4096], *misc;
1027 const char *netname, *fmt;
1031 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1035 cookie = calloc(1, sizeof(*cookie));
1037 cookie->type = type;
1038 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1039 cookie->expires = now + nickserv_conf.cookie_timeout;
1040 inttobase64(cookie->cookie, rand(), 5);
1041 inttobase64(cookie->cookie+5, rand(), 5);
1043 netname = nickserv_conf.network_name;
1046 switch (cookie->type) {
1048 hi->passwd[0] = 0; /* invalidate password */
1049 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1050 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1051 snprintf(subject, sizeof(subject), fmt, netname);
1052 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1053 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1056 case PASSWORD_CHANGE:
1057 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1058 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1059 snprintf(subject, sizeof(subject), fmt, netname);
1060 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1061 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1064 misc = hi->email_addr;
1065 hi->email_addr = cookie->data;
1067 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1068 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1069 snprintf(subject, sizeof(subject), fmt, netname);
1070 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1071 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1072 mail_send(nickserv, hi, subject, body, 1);
1073 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1074 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1076 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1077 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1078 snprintf(subject, sizeof(subject), fmt, netname);
1079 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1080 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1081 mail_send(nickserv, hi, subject, body, 1);
1084 hi->email_addr = misc;
1087 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1088 snprintf(subject, sizeof(subject), fmt, netname);
1089 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1090 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1091 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1094 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1098 mail_send(nickserv, hi, subject, body, first_time);
1099 nickserv_bake_cookie(cookie);
1103 nickserv_eat_cookie(struct handle_cookie *cookie)
1105 cookie->hi->cookie = NULL;
1106 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1107 nickserv_free_cookie(cookie);
1111 nickserv_free_email_addr(void *data)
1113 handle_info_list_clean(data);
1118 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1120 struct handle_info_list *hil;
1121 /* Remove from old handle_info_list ... */
1122 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1123 handle_info_list_remove(hil, hi);
1124 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1125 hi->email_addr = NULL;
1127 /* Add to the new list.. */
1128 if (new_email_addr) {
1129 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1130 hil = calloc(1, sizeof(*hil));
1131 hil->tag = strdup(new_email_addr);
1132 handle_info_list_init(hil);
1133 dict_insert(nickserv_email_dict, hil->tag, hil);
1135 handle_info_list_append(hil, hi);
1136 hi->email_addr = hil->tag;
1140 static NICKSERV_FUNC(cmd_register)
1143 struct handle_info *hi;
1144 const char *email_addr, *password;
1147 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1148 /* Require the first handle registered to belong to someone +o. */
1149 reply("NSMSG_REQUIRE_OPER");
1153 if (user->handle_info) {
1154 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1158 if (IsRegistering(user)) {
1159 reply("NSMSG_ALREADY_REGISTERING");
1163 if (IsStamped(user)) {
1164 /* Unauthenticated users might still have been stamped
1165 previously and could therefore have a hidden host;
1166 do not allow them to register a new account. */
1167 reply("NSMSG_STAMPED_REGISTER");
1171 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1173 if (!is_valid_handle(argv[1])) {
1174 reply("NSMSG_BAD_HANDLE", argv[1]);
1178 if ((argc >= 4) && nickserv_conf.email_enabled) {
1179 struct handle_info_list *hil;
1182 /* Remember email address. */
1183 email_addr = argv[3];
1185 /* Check that the email address looks valid.. */
1186 if (!is_valid_email_addr(email_addr)) {
1187 reply("NSMSG_BAD_EMAIL_ADDR");
1191 /* .. and that we are allowed to send to it. */
1192 if ((str = mail_prohibited_address(email_addr))) {
1193 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1197 /* If we do email verify, make sure we don't spam the address. */
1198 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1200 for (nn=0; nn<hil->used; nn++) {
1201 if (hil->list[nn]->cookie) {
1202 reply("NSMSG_EMAIL_UNACTIVATED");
1206 if (hil->used >= nickserv_conf.handles_per_email) {
1207 reply("NSMSG_EMAIL_OVERUSED");
1220 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1222 /* Add any masks they should get. */
1223 if (nickserv_conf.default_hostmask) {
1224 string_list_append(hi->masks, strdup("*@*"));
1226 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1227 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1228 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1231 /* If they're the first to register, give them level 1000. */
1232 if (dict_size(nickserv_handle_dict) == 1) {
1233 hi->opserv_level = 1000;
1234 reply("NSMSG_ROOT_HANDLE", argv[1]);
1237 /* Set their email address. */
1239 nickserv_set_email_addr(hi, email_addr);
1241 /* If they need to do email verification, tell them. */
1243 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1245 /* Set registering flag.. */
1246 user->modes |= FLAGS_REGISTERING;
1251 static NICKSERV_FUNC(cmd_oregister)
1254 struct userNode *settee;
1255 struct handle_info *hi;
1257 NICKSERV_MIN_PARMS(3);
1259 if (!is_valid_handle(argv[1])) {
1260 reply("NSMSG_BAD_HANDLE", argv[1]);
1267 } else if (strchr(argv[3], '@')) {
1268 mask = canonicalize_hostmask(strdup(argv[3]));
1270 settee = GetUserH(argv[4]);
1272 reply("MSG_NICK_UNKNOWN", argv[4]);
1279 } else if ((settee = GetUserH(argv[3]))) {
1280 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1282 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1285 if (settee && settee->handle_info) {
1286 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1290 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1295 string_list_append(hi->masks, mask);
1299 static NICKSERV_FUNC(cmd_handleinfo)
1302 unsigned int i, pos=0, herelen;
1303 struct userNode *target, *next_un;
1304 struct handle_info *hi;
1305 const char *nsmsg_none;
1308 if (!(hi = user->handle_info)) {
1309 reply("NSMSG_MUST_AUTH");
1312 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1316 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1317 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1318 #ifdef WITH_PROTOCOL_BAHAMUT
1319 reply("NSMSG_HANDLEINFO_ID", hi->id);
1321 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1324 intervalString(buff, now - hi->lastseen, user->handle_info);
1325 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1327 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1330 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1331 if (HANDLE_FLAGGED(hi, FROZEN))
1332 reply("NSMSG_HANDLEINFO_VACATION");
1334 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1335 struct do_not_register *dnr;
1336 if ((dnr = chanserv_is_dnr(NULL, hi)))
1337 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1338 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1340 } else if (hi != user->handle_info)
1344 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1346 if (nickserv_conf.email_enabled)
1347 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1351 switch (hi->cookie->type) {
1352 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1353 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1354 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1355 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1356 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1362 reply("NSMSG_HANDLEINFO_NO_NOTES");
1364 struct handle_note *prev, *note;
1366 WALK_NOTES(hi, prev, note) {
1367 char set_time[INTERVALLEN];
1368 intervalString(set_time, now - note->set, user->handle_info);
1369 if (note->expires) {
1370 char exp_time[INTERVALLEN];
1371 intervalString(exp_time, note->expires - now, user->handle_info);
1372 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1374 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1380 unsigned long flen = 1;
1381 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1383 for (i=0, flen=1; handle_flags[i]; i++)
1384 if (hi->flags & 1 << i)
1385 flags[flen++] = handle_flags[i];
1387 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1389 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1392 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1393 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1394 || (hi->opserv_level > 0)) {
1395 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1399 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1401 if (hi->last_quit_host[0])
1402 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1404 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1406 if (nickserv_conf.disable_nicks) {
1407 /* nicks disabled; don't show anything about registered nicks */
1408 } else if (hi->nicks) {
1409 struct nick_info *ni, *next_ni;
1410 for (ni = hi->nicks; ni; ni = next_ni) {
1411 herelen = strlen(ni->nick);
1412 if (pos + herelen + 1 > ArrayLength(buff)) {
1414 goto print_nicks_buff;
1418 memcpy(buff+pos, ni->nick, herelen);
1419 pos += herelen; buff[pos++] = ' ';
1423 reply("NSMSG_HANDLEINFO_NICKS", buff);
1428 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1431 if (hi->masks->used) {
1432 for (i=0; i < hi->masks->used; i++) {
1433 herelen = strlen(hi->masks->list[i]);
1434 if (pos + herelen + 1 > ArrayLength(buff)) {
1436 goto print_mask_buff;
1438 memcpy(buff+pos, hi->masks->list[i], herelen);
1439 pos += herelen; buff[pos++] = ' ';
1440 if (i+1 == hi->masks->used) {
1443 reply("NSMSG_HANDLEINFO_MASKS", buff);
1448 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1452 struct userData *channel, *next;
1455 for (channel = hi->channels; channel; channel = next) {
1456 next = channel->u_next;
1457 name = channel->channel->channel->name;
1458 herelen = strlen(name);
1459 if (pos + herelen + 7 > ArrayLength(buff)) {
1461 goto print_chans_buff;
1463 if (IsUserSuspended(channel))
1465 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1469 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1474 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1477 for (target = hi->users; target; target = next_un) {
1478 herelen = strlen(target->nick);
1479 if (pos + herelen + 1 > ArrayLength(buff)) {
1481 goto print_cnick_buff;
1483 next_un = target->next_authed;
1485 memcpy(buff+pos, target->nick, herelen);
1486 pos += herelen; buff[pos++] = ' ';
1490 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1498 static NICKSERV_FUNC(cmd_userinfo)
1500 struct userNode *target;
1502 NICKSERV_MIN_PARMS(2);
1503 if (!(target = GetUserH(argv[1]))) {
1504 reply("MSG_NICK_UNKNOWN", argv[1]);
1507 if (target->handle_info)
1508 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1510 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1514 static NICKSERV_FUNC(cmd_nickinfo)
1516 struct nick_info *ni;
1518 NICKSERV_MIN_PARMS(2);
1519 if (!(ni = get_nick_info(argv[1]))) {
1520 reply("MSG_NICK_UNKNOWN", argv[1]);
1523 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1527 static NICKSERV_FUNC(cmd_rename_handle)
1529 struct handle_info *hi;
1530 char msgbuf[MAXLEN], *old_handle;
1533 NICKSERV_MIN_PARMS(3);
1534 if (!(hi = get_victim_oper(user, argv[1])))
1536 if (!is_valid_handle(argv[2])) {
1537 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1540 if (get_handle_info(argv[2])) {
1541 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1545 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1546 hi->handle = strdup(argv[2]);
1547 dict_insert(nickserv_handle_dict, hi->handle, hi);
1548 for (nn=0; nn<rf_list_used; nn++)
1549 rf_list[nn](hi, old_handle);
1550 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1551 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1552 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1557 static failpw_func_t *failpw_func_list;
1558 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1561 reg_failpw_func(failpw_func_t func)
1563 if (failpw_func_used == failpw_func_size) {
1564 if (failpw_func_size) {
1565 failpw_func_size <<= 1;
1566 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1568 failpw_func_size = 8;
1569 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1572 failpw_func_list[failpw_func_used++] = func;
1575 static NICKSERV_FUNC(cmd_auth)
1577 int pw_arg, used, maxlogins;
1578 struct handle_info *hi;
1580 struct userNode *other;
1582 if (user->handle_info) {
1583 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1586 if (IsStamped(user)) {
1587 /* Unauthenticated users might still have been stamped
1588 previously and could therefore have a hidden host;
1589 do not allow them to authenticate. */
1590 reply("NSMSG_STAMPED_AUTH");
1594 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1596 } else if (argc == 2) {
1597 if (nickserv_conf.disable_nicks) {
1598 if (!(hi = get_handle_info(user->nick))) {
1599 reply("NSMSG_HANDLE_NOT_FOUND");
1603 /* try to look up their handle from their nick */
1604 struct nick_info *ni;
1605 ni = get_nick_info(user->nick);
1607 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1614 reply("MSG_MISSING_PARAMS", argv[0]);
1615 svccmd_send_help(user, nickserv, cmd);
1619 reply("NSMSG_HANDLE_NOT_FOUND");
1622 /* Responses from here on look up the language used by the handle they asked about. */
1623 passwd = argv[pw_arg];
1624 if (!valid_user_for(user, hi)) {
1625 if (hi->email_addr && nickserv_conf.email_enabled)
1626 send_message_type(4, user, cmd->parent->bot,
1627 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1630 send_message_type(4, user, cmd->parent->bot,
1631 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1633 argv[pw_arg] = "BADMASK";
1636 if (!checkpass(passwd, hi->passwd)) {
1638 send_message_type(4, user, cmd->parent->bot,
1639 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1640 argv[pw_arg] = "BADPASS";
1641 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1642 if (nickserv_conf.autogag_enabled) {
1643 if (!user->auth_policer.params) {
1644 user->auth_policer.last_req = now;
1645 user->auth_policer.params = nickserv_conf.auth_policer_params;
1647 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1649 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1650 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1651 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1653 argv[pw_arg] = "GAGGED";
1658 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1659 send_message_type(4, user, cmd->parent->bot,
1660 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1661 argv[pw_arg] = "SUSPENDED";
1664 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1665 for (used = 0, other = hi->users; other; other = other->next_authed) {
1666 if (++used >= maxlogins) {
1667 send_message_type(4, user, cmd->parent->bot,
1668 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1670 argv[pw_arg] = "MAXLOGINS";
1675 set_user_handle_info(user, hi, 1);
1676 if (nickserv_conf.email_required && !hi->email_addr)
1677 reply("NSMSG_PLEASE_SET_EMAIL");
1678 if (!is_secure_password(hi->handle, passwd, NULL))
1679 reply("NSMSG_WEAK_PASSWORD");
1680 if (hi->passwd[0] != '$')
1681 cryptpass(passwd, hi->passwd);
1682 if (!hi->masks->used) {
1684 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1685 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1686 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1688 argv[pw_arg] = "****";
1689 reply("NSMSG_AUTH_SUCCESS");
1693 static allowauth_func_t *allowauth_func_list;
1694 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1697 reg_allowauth_func(allowauth_func_t func)
1699 if (allowauth_func_used == allowauth_func_size) {
1700 if (allowauth_func_size) {
1701 allowauth_func_size <<= 1;
1702 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1704 allowauth_func_size = 8;
1705 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1708 allowauth_func_list[allowauth_func_used++] = func;
1711 static NICKSERV_FUNC(cmd_allowauth)
1713 struct userNode *target;
1714 struct handle_info *hi;
1717 NICKSERV_MIN_PARMS(2);
1718 if (!(target = GetUserH(argv[1]))) {
1719 reply("MSG_NICK_UNKNOWN", argv[1]);
1722 if (target->handle_info) {
1723 reply("NSMSG_USER_PREV_AUTH", target->nick);
1726 if (IsStamped(target)) {
1727 /* Unauthenticated users might still have been stamped
1728 previously and could therefore have a hidden host;
1729 do not allow them to authenticate to an account. */
1730 reply("NSMSG_USER_PREV_STAMP", target->nick);
1735 else if (!(hi = get_handle_info(argv[2]))) {
1736 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1740 if (hi->opserv_level > user->handle_info->opserv_level) {
1741 reply("MSG_USER_OUTRANKED", hi->handle);
1744 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1745 || (hi->opserv_level > 0))
1746 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1747 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1750 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1751 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1752 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1753 if (nickserv_conf.email_enabled)
1754 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1756 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1757 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1759 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1761 for (n=0; n<allowauth_func_used; n++)
1762 allowauth_func_list[n](user, target, hi);
1766 static NICKSERV_FUNC(cmd_authcookie)
1768 struct handle_info *hi;
1770 NICKSERV_MIN_PARMS(2);
1771 if (user->handle_info) {
1772 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1775 if (IsStamped(user)) {
1776 /* Unauthenticated users might still have been stamped
1777 previously and could therefore have a hidden host;
1778 do not allow them to authenticate to an account. */
1779 reply("NSMSG_STAMPED_AUTHCOOKIE");
1782 if (!(hi = get_handle_info(argv[1]))) {
1783 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1786 if (!hi->email_addr) {
1787 reply("MSG_SET_EMAIL_ADDR");
1790 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1794 static NICKSERV_FUNC(cmd_delcookie)
1796 struct handle_info *hi;
1798 hi = user->handle_info;
1800 reply("NSMSG_NO_COOKIE");
1803 switch (hi->cookie->type) {
1806 reply("NSMSG_MUST_TIME_OUT");
1809 nickserv_eat_cookie(hi->cookie);
1810 reply("NSMSG_ATE_COOKIE");
1816 static NICKSERV_FUNC(cmd_resetpass)
1818 struct handle_info *hi;
1819 char crypted[MD5_CRYPT_LENGTH];
1821 NICKSERV_MIN_PARMS(3);
1822 if (user->handle_info) {
1823 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1826 if (IsStamped(user)) {
1827 /* Unauthenticated users might still have been stamped
1828 previously and could therefore have a hidden host;
1829 do not allow them to activate an account. */
1830 reply("NSMSG_STAMPED_RESETPASS");
1833 if (!(hi = get_handle_info(argv[1]))) {
1834 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1837 if (!hi->email_addr) {
1838 reply("MSG_SET_EMAIL_ADDR");
1841 cryptpass(argv[2], crypted);
1843 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1847 static NICKSERV_FUNC(cmd_cookie)
1849 struct handle_info *hi;
1852 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1855 NICKSERV_MIN_PARMS(3);
1856 if (!(hi = get_handle_info(argv[1]))) {
1857 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1863 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1864 reply("NSMSG_HANDLE_SUSPENDED");
1869 reply("NSMSG_NO_COOKIE");
1873 /* Check validity of operation before comparing cookie to
1874 * prohibit guessing by authed users. */
1875 if (user->handle_info
1876 && (hi->cookie->type != EMAIL_CHANGE)
1877 && (hi->cookie->type != PASSWORD_CHANGE)) {
1878 reply("NSMSG_CANNOT_COOKIE");
1882 if (strcmp(cookie, hi->cookie->cookie)) {
1883 reply("NSMSG_BAD_COOKIE");
1887 switch (hi->cookie->type) {
1889 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1890 set_user_handle_info(user, hi, 1);
1891 reply("NSMSG_HANDLE_ACTIVATED");
1893 case PASSWORD_CHANGE:
1894 set_user_handle_info(user, hi, 1);
1895 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1896 reply("NSMSG_PASSWORD_CHANGED");
1899 nickserv_set_email_addr(hi, hi->cookie->data);
1900 reply("NSMSG_EMAIL_CHANGED");
1903 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1904 set_user_handle_info(user, hi, 1);
1905 nickserv_addmask(user, hi, mask);
1906 reply("NSMSG_AUTH_SUCCESS");
1911 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1912 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1916 nickserv_eat_cookie(hi->cookie);
1921 static NICKSERV_FUNC(cmd_oregnick) {
1923 struct handle_info *target;
1924 struct nick_info *ni;
1926 NICKSERV_MIN_PARMS(3);
1927 if (!(target = modcmd_get_handle_info(user, argv[1])))
1930 if (!is_registerable_nick(nick)) {
1931 reply("NSMSG_BAD_NICK", nick);
1934 ni = dict_find(nickserv_nick_dict, nick, NULL);
1936 reply("NSMSG_NICK_EXISTS", nick);
1939 register_nick(nick, target);
1940 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1944 static NICKSERV_FUNC(cmd_regnick) {
1946 struct nick_info *ni;
1948 if (!is_registerable_nick(user->nick)) {
1949 reply("NSMSG_BAD_NICK", user->nick);
1952 /* count their nicks, see if it's too many */
1953 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1954 if (n >= nickserv_conf.nicks_per_handle) {
1955 reply("NSMSG_TOO_MANY_NICKS");
1958 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1960 reply("NSMSG_NICK_EXISTS", user->nick);
1963 register_nick(user->nick, user->handle_info);
1964 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1968 static NICKSERV_FUNC(cmd_pass)
1970 struct handle_info *hi;
1971 const char *old_pass, *new_pass;
1973 NICKSERV_MIN_PARMS(3);
1974 hi = user->handle_info;
1978 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1979 if (!checkpass(old_pass, hi->passwd)) {
1980 argv[1] = "BADPASS";
1981 reply("NSMSG_PASSWORD_INVALID");
1984 cryptpass(new_pass, hi->passwd);
1986 reply("NSMSG_PASS_SUCCESS");
1991 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1994 char *new_mask = canonicalize_hostmask(strdup(mask));
1995 for (i=0; i<hi->masks->used; i++) {
1996 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1997 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2002 string_list_append(hi->masks, new_mask);
2003 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2007 static NICKSERV_FUNC(cmd_addmask)
2010 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2011 int res = nickserv_addmask(user, user->handle_info, mask);
2015 if (!is_gline(argv[1])) {
2016 reply("NSMSG_MASK_INVALID", argv[1]);
2019 return nickserv_addmask(user, user->handle_info, argv[1]);
2023 static NICKSERV_FUNC(cmd_oaddmask)
2025 struct handle_info *hi;
2027 NICKSERV_MIN_PARMS(3);
2028 if (!(hi = get_victim_oper(user, argv[1])))
2030 return nickserv_addmask(user, hi, argv[2]);
2034 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2037 for (i=0; i<hi->masks->used; i++) {
2038 if (!strcmp(del_mask, hi->masks->list[i])) {
2039 char *old_mask = hi->masks->list[i];
2040 if (hi->masks->used == 1 && !force) {
2041 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2044 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2045 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2050 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2054 static NICKSERV_FUNC(cmd_delmask)
2056 NICKSERV_MIN_PARMS(2);
2057 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2060 static NICKSERV_FUNC(cmd_odelmask)
2062 struct handle_info *hi;
2063 NICKSERV_MIN_PARMS(3);
2064 if (!(hi = get_victim_oper(user, argv[1])))
2066 return nickserv_delmask(user, hi, argv[2], 1);
2070 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2071 unsigned int nn, add = 1, pos;
2072 unsigned long added, removed, flag;
2074 for (added=removed=nn=0; str[nn]; nn++) {
2076 case '+': add = 1; break;
2077 case '-': add = 0; break;
2079 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2080 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2083 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2084 /* cheesy avoidance of looking up the flag name.. */
2085 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2088 flag = 1 << (pos - 1);
2090 added |= flag, removed &= ~flag;
2092 removed |= flag, added &= ~flag;
2097 *premoved = removed;
2102 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2104 unsigned long before, after, added, removed;
2105 struct userNode *uNode;
2107 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2108 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2110 hi->flags = (hi->flags | added) & ~removed;
2111 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2113 /* Strip helping flag if they're only a support helper and not
2114 * currently in #support. */
2115 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2116 struct channelList *schannels;
2118 schannels = chanserv_support_channels();
2119 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2120 for (ii = 0; ii < schannels->used; ++ii)
2121 if (GetUserMode(schannels->list[ii], uNode))
2123 if (ii < schannels->used)
2127 HANDLE_CLEAR_FLAG(hi, HELPING);
2130 if (after && !before) {
2131 /* Add user to current helper list. */
2132 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2133 userList_append(&curr_helpers, uNode);
2134 } else if (!after && before) {
2135 /* Remove user from current helper list. */
2136 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2137 userList_remove(&curr_helpers, uNode);
2144 set_list(struct userNode *user, struct handle_info *hi, int override)
2148 char *set_display[] = {
2149 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2150 "EMAIL", "MAXLOGINS", "LANGUAGE"
2153 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2155 /* Do this so options are presented in a consistent order. */
2156 for (i = 0; i < ArrayLength(set_display); ++i)
2157 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2158 opt(user, hi, override, 0, NULL);
2161 static NICKSERV_FUNC(cmd_set)
2163 struct handle_info *hi;
2166 hi = user->handle_info;
2168 set_list(user, hi, 0);
2171 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2172 reply("NSMSG_INVALID_OPTION", argv[1]);
2175 return opt(user, hi, 0, argc-1, argv+1);
2178 static NICKSERV_FUNC(cmd_oset)
2180 struct handle_info *hi;
2183 NICKSERV_MIN_PARMS(2);
2185 if (!(hi = get_victim_oper(user, argv[1])))
2189 set_list(user, hi, 0);
2193 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2194 reply("NSMSG_INVALID_OPTION", argv[2]);
2198 return opt(user, hi, 1, argc-2, argv+2);
2201 static OPTION_FUNC(opt_info)
2205 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2207 hi->infoline = NULL;
2209 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2213 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2214 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2218 static OPTION_FUNC(opt_width)
2221 hi->screen_width = strtoul(argv[1], NULL, 0);
2223 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2224 hi->screen_width = MIN_LINE_SIZE;
2225 else if (hi->screen_width > MAX_LINE_SIZE)
2226 hi->screen_width = MAX_LINE_SIZE;
2228 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2232 static OPTION_FUNC(opt_tablewidth)
2235 hi->table_width = strtoul(argv[1], NULL, 0);
2237 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2238 hi->table_width = MIN_LINE_SIZE;
2239 else if (hi->screen_width > MAX_LINE_SIZE)
2240 hi->table_width = MAX_LINE_SIZE;
2242 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2246 static OPTION_FUNC(opt_color)
2249 if (enabled_string(argv[1]))
2250 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2251 else if (disabled_string(argv[1]))
2252 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2254 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2259 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2263 static OPTION_FUNC(opt_privmsg)
2266 if (enabled_string(argv[1]))
2267 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2268 else if (disabled_string(argv[1]))
2269 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2271 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2276 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2280 static OPTION_FUNC(opt_style)
2285 if (!irccasecmp(argv[1], "Zoot"))
2286 hi->userlist_style = HI_STYLE_ZOOT;
2287 else if (!irccasecmp(argv[1], "def"))
2288 hi->userlist_style = HI_STYLE_DEF;
2291 switch (hi->userlist_style) {
2300 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2304 static OPTION_FUNC(opt_password)
2307 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2312 cryptpass(argv[1], hi->passwd);
2314 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2318 static OPTION_FUNC(opt_flags)
2321 unsigned int ii, flen;
2324 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2329 nickserv_apply_flags(user, hi, argv[1]);
2331 for (ii = flen = 0; handle_flags[ii]; ii++)
2332 if (hi->flags & (1 << ii))
2333 flags[flen++] = handle_flags[ii];
2336 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2338 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2342 static OPTION_FUNC(opt_email)
2346 if (!is_valid_email_addr(argv[1])) {
2347 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2350 if ((str = mail_prohibited_address(argv[1]))) {
2351 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2354 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2355 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2357 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2359 nickserv_set_email_addr(hi, argv[1]);
2361 nickserv_eat_cookie(hi->cookie);
2362 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2365 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2369 static OPTION_FUNC(opt_maxlogins)
2371 unsigned char maxlogins;
2373 maxlogins = strtoul(argv[1], NULL, 0);
2374 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2375 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2378 hi->maxlogins = maxlogins;
2380 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2381 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2385 static OPTION_FUNC(opt_language)
2387 struct language *lang;
2389 lang = language_find(argv[1]);
2390 if (irccasecmp(lang->name, argv[1]))
2391 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2392 hi->language = lang;
2394 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2398 static OPTION_FUNC(opt_karma)
2401 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2406 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2407 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2408 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2409 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2411 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2415 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2420 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2421 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2423 if ((user->handle_info->opserv_level < target->opserv_level)
2424 || ((user->handle_info->opserv_level == target->opserv_level)
2425 && (user->handle_info->opserv_level < 1000))) {
2426 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2429 if ((user->handle_info->opserv_level < new_level)
2430 || ((user->handle_info->opserv_level == new_level)
2431 && (user->handle_info->opserv_level < 1000))) {
2432 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2435 if (user->handle_info == target) {
2436 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2439 if (target->opserv_level == new_level)
2441 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2442 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2443 target->opserv_level = new_level;
2447 static OPTION_FUNC(opt_level)
2452 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2456 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2457 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2461 static OPTION_FUNC(opt_epithet)
2464 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2468 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2469 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2472 if ((epithet[0] == '*') && !epithet[1])
2475 hi->epithet = strdup(epithet);
2479 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2481 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2485 static OPTION_FUNC(opt_title)
2490 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2494 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2496 if (strchr(title, '.')) {
2497 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2500 if ((strlen(user->handle_info->handle) + strlen(title) +
2501 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2502 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2507 if (!strcmp(title, "*")) {
2508 hi->fakehost = NULL;
2510 hi->fakehost = malloc(strlen(title)+2);
2511 hi->fakehost[0] = '.';
2512 strcpy(hi->fakehost+1, title);
2515 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2516 title = hi->fakehost + 1;
2520 title = user_find_message(user, "MSG_NONE");
2521 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2525 static OPTION_FUNC(opt_fakehost)
2530 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2534 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2536 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2537 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2541 if (!strcmp(fake, "*"))
2542 hi->fakehost = NULL;
2544 hi->fakehost = strdup(fake);
2545 fake = hi->fakehost;
2548 fake = generate_fakehost(hi);
2550 fake = user_find_message(user, "MSG_NONE");
2551 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2555 static NICKSERV_FUNC(cmd_reclaim)
2557 struct handle_info *hi;
2558 struct nick_info *ni;
2559 struct userNode *victim;
2561 NICKSERV_MIN_PARMS(2);
2562 hi = user->handle_info;
2563 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2565 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2568 if (ni->owner != user->handle_info) {
2569 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2572 victim = GetUserH(ni->nick);
2574 reply("MSG_NICK_UNKNOWN", ni->nick);
2577 if (victim == user) {
2578 reply("NSMSG_NICK_USER_YOU");
2581 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2582 switch (nickserv_conf.reclaim_action) {
2583 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2584 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2585 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2586 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2591 static NICKSERV_FUNC(cmd_unregnick)
2594 struct handle_info *hi;
2595 struct nick_info *ni;
2597 hi = user->handle_info;
2598 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2599 ni = dict_find(nickserv_nick_dict, nick, NULL);
2601 reply("NSMSG_UNKNOWN_NICK", nick);
2604 if (hi != ni->owner) {
2605 reply("NSMSG_NOT_YOUR_NICK", nick);
2608 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2613 static NICKSERV_FUNC(cmd_ounregnick)
2615 struct nick_info *ni;
2617 NICKSERV_MIN_PARMS(2);
2618 if (!(ni = get_nick_info(argv[1]))) {
2619 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2622 if (!oper_outranks(user, ni->owner))
2624 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2629 static NICKSERV_FUNC(cmd_unregister)
2631 struct handle_info *hi;
2634 NICKSERV_MIN_PARMS(2);
2635 hi = user->handle_info;
2638 if (checkpass(passwd, hi->passwd)) {
2639 nickserv_unregister_handle(hi, user);
2642 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2643 reply("NSMSG_PASSWORD_INVALID");
2648 static NICKSERV_FUNC(cmd_ounregister)
2650 struct handle_info *hi;
2651 char reason[MAXLEN];
2653 NICKSERV_MIN_PARMS(2);
2654 if (!(hi = get_victim_oper(user, argv[1])))
2656 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2657 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2658 nickserv_unregister_handle(hi, user);
2662 static NICKSERV_FUNC(cmd_status)
2664 if (nickserv_conf.disable_nicks) {
2665 reply("NSMSG_GLOBAL_STATS_NONICK",
2666 dict_size(nickserv_handle_dict));
2668 if (user->handle_info) {
2670 struct nick_info *ni;
2671 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2672 reply("NSMSG_HANDLE_STATS", cnt);
2674 reply("NSMSG_HANDLE_NONE");
2676 reply("NSMSG_GLOBAL_STATS",
2677 dict_size(nickserv_handle_dict),
2678 dict_size(nickserv_nick_dict));
2683 static NICKSERV_FUNC(cmd_ghost)
2685 struct userNode *target;
2686 char reason[MAXLEN];
2688 NICKSERV_MIN_PARMS(2);
2689 if (!(target = GetUserH(argv[1]))) {
2690 reply("MSG_NICK_UNKNOWN", argv[1]);
2693 if (target == user) {
2694 reply("NSMSG_CANNOT_GHOST_SELF");
2697 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2698 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2701 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2702 DelUser(target, nickserv, 1, reason);
2703 reply("NSMSG_GHOST_KILLED", argv[1]);
2707 static NICKSERV_FUNC(cmd_vacation)
2709 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2710 reply("NSMSG_ON_VACATION");
2714 static NICKSERV_FUNC(cmd_addnote)
2716 struct handle_info *hi;
2717 unsigned long duration;
2720 struct handle_note *prev;
2721 struct handle_note *note;
2723 /* Parse parameters and figure out values for note's fields. */
2724 NICKSERV_MIN_PARMS(4);
2725 hi = get_victim_oper(user, argv[1]);
2728 duration = ParseInterval(argv[2]);
2729 if (duration > 2*365*86400) {
2730 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2733 unsplit_string(argv + 3, argc - 3, text);
2734 WALK_NOTES(hi, prev, note) {}
2735 id = prev ? (prev->id + 1) : 1;
2737 /* Create the new note structure. */
2738 note = calloc(1, sizeof(*note) + strlen(text));
2740 note->expires = duration ? (now + duration) : 0;
2743 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2744 strcpy(note->note, text);
2749 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2753 static NICKSERV_FUNC(cmd_delnote)
2755 struct handle_info *hi;
2756 struct handle_note *prev;
2757 struct handle_note *note;
2760 NICKSERV_MIN_PARMS(3);
2761 hi = get_victim_oper(user, argv[1]);
2764 id = strtoul(argv[2], NULL, 10);
2765 WALK_NOTES(hi, prev, note) {
2766 if (id == note->id) {
2768 prev->next = note->next;
2770 hi->notes = note->next;
2772 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2776 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2781 nickserv_saxdb_write(struct saxdb_context *ctx) {
2783 struct handle_info *hi;
2786 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2788 #ifdef WITH_PROTOCOL_BAHAMUT
2791 saxdb_start_record(ctx, iter_key(it), 0);
2793 struct handle_cookie *cookie = hi->cookie;
2796 switch (cookie->type) {
2797 case ACTIVATION: type = KEY_ACTIVATION; break;
2798 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2799 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2800 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2801 default: type = NULL; break;
2804 saxdb_start_record(ctx, KEY_COOKIE, 0);
2805 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2806 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2808 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2809 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2810 saxdb_end_record(ctx);
2814 struct handle_note *prev, *note;
2815 saxdb_start_record(ctx, KEY_NOTES, 0);
2816 WALK_NOTES(hi, prev, note) {
2817 snprintf(flags, sizeof(flags), "%d", note->id);
2818 saxdb_start_record(ctx, flags, 0);
2820 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
2821 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
2822 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
2823 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
2824 saxdb_end_record(ctx);
2826 saxdb_end_record(ctx);
2829 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2831 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2833 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2837 for (ii=flen=0; handle_flags[ii]; ++ii)
2838 if (hi->flags & (1 << ii))
2839 flags[flen++] = handle_flags[ii];
2841 saxdb_write_string(ctx, KEY_FLAGS, flags);
2843 #ifdef WITH_PROTOCOL_BAHAMUT
2844 saxdb_write_int(ctx, KEY_ID, hi->id);
2847 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2848 if (hi->last_quit_host[0])
2849 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2850 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2852 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
2853 if (hi->masks->used)
2854 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2856 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2858 struct string_list *slist;
2859 struct nick_info *ni;
2861 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2862 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2863 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2867 if (hi->opserv_level)
2868 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2869 if (hi->language != lang_C)
2870 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2871 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2872 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2873 if (hi->screen_width)
2874 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2875 if (hi->table_width)
2876 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2877 flags[0] = hi->userlist_style;
2879 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2880 saxdb_end_record(ctx);
2885 static handle_merge_func_t *handle_merge_func_list;
2886 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2889 reg_handle_merge_func(handle_merge_func_t func)
2891 if (handle_merge_func_used == handle_merge_func_size) {
2892 if (handle_merge_func_size) {
2893 handle_merge_func_size <<= 1;
2894 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2896 handle_merge_func_size = 8;
2897 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2900 handle_merge_func_list[handle_merge_func_used++] = func;
2903 static NICKSERV_FUNC(cmd_merge)
2905 struct handle_info *hi_from, *hi_to;
2906 struct userNode *last_user;
2907 struct userData *cList, *cListNext;
2908 unsigned int ii, jj, n;
2909 char buffer[MAXLEN];
2911 NICKSERV_MIN_PARMS(3);
2913 if (!(hi_from = get_victim_oper(user, argv[1])))
2915 if (!(hi_to = get_victim_oper(user, argv[2])))
2917 if (hi_to == hi_from) {
2918 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2922 for (n=0; n<handle_merge_func_used; n++)
2923 handle_merge_func_list[n](user, hi_to, hi_from);
2925 /* Append "from" handle's nicks to "to" handle's nick list. */
2927 struct nick_info *last_ni;
2928 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2929 last_ni->next = hi_from->nicks;
2931 while (hi_from->nicks) {
2932 hi_from->nicks->owner = hi_to;
2933 hi_from->nicks = hi_from->nicks->next;
2936 /* Merge the hostmasks. */
2937 for (ii=0; ii<hi_from->masks->used; ii++) {
2938 char *mask = hi_from->masks->list[ii];
2939 for (jj=0; jj<hi_to->masks->used; jj++)
2940 if (match_ircglobs(hi_to->masks->list[jj], mask))
2942 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2943 string_list_append(hi_to->masks, strdup(mask));
2946 /* Merge the lists of authed users. */
2948 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2949 last_user->next_authed = hi_from->users;
2951 hi_to->users = hi_from->users;
2953 /* Repoint the old "from" handle's users. */
2954 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2955 last_user->handle_info = hi_to;
2957 hi_from->users = NULL;
2959 /* Merge channel userlists. */
2960 for (cList=hi_from->channels; cList; cList=cListNext) {
2961 struct userData *cList2;
2962 cListNext = cList->u_next;
2963 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2964 if (cList->channel == cList2->channel)
2966 if (cList2 && (cList2->access >= cList->access)) {
2967 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);
2968 /* keep cList2 in hi_to; remove cList from hi_from */
2969 del_channel_user(cList, 1);
2972 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);
2973 /* remove the lower-ranking cList2 from hi_to */
2974 del_channel_user(cList2, 1);
2976 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
2978 /* cList needs to be moved from hi_from to hi_to */
2979 cList->handle = hi_to;
2980 /* Remove from linked list for hi_from */
2981 assert(!cList->u_prev);
2982 hi_from->channels = cList->u_next;
2984 cList->u_next->u_prev = cList->u_prev;
2985 /* Add to linked list for hi_to */
2986 cList->u_prev = NULL;
2987 cList->u_next = hi_to->channels;
2988 if (hi_to->channels)
2989 hi_to->channels->u_prev = cList;
2990 hi_to->channels = cList;
2994 /* Do they get an OpServ level promotion? */
2995 if (hi_from->opserv_level > hi_to->opserv_level)
2996 hi_to->opserv_level = hi_from->opserv_level;
2998 /* What about last seen time? */
2999 if (hi_from->lastseen > hi_to->lastseen)
3000 hi_to->lastseen = hi_from->lastseen;
3002 /* New karma is the sum of the two original karmas. */
3003 hi_to->karma += hi_from->karma;
3005 /* Does a fakehost carry over? (This intentionally doesn't set it
3006 * for users previously attached to hi_to. They'll just have to
3009 if (hi_from->fakehost && !hi_to->fakehost)
3010 hi_to->fakehost = strdup(hi_from->fakehost);
3012 /* Notify of success. */
3013 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3014 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3015 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3017 /* Unregister the "from" handle. */
3018 nickserv_unregister_handle(hi_from, NULL);
3023 struct nickserv_discrim {
3024 unsigned long flags_on, flags_off;
3025 time_t min_registered, max_registered;
3028 int min_level, max_level;
3029 int min_karma, max_karma;
3030 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3031 const char *nickmask;
3032 const char *hostmask;
3033 const char *handlemask;
3034 const char *emailmask;
3037 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3039 struct discrim_apply_info {
3040 struct nickserv_discrim *discrim;
3041 discrim_search_func func;
3042 struct userNode *source;
3043 unsigned int matched;
3046 static struct nickserv_discrim *
3047 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3050 struct nickserv_discrim *discrim;
3052 discrim = malloc(sizeof(*discrim));
3053 memset(discrim, 0, sizeof(*discrim));
3054 discrim->min_level = 0;
3055 discrim->max_level = ~0;
3056 discrim->limit = 50;
3057 discrim->min_registered = 0;
3058 discrim->max_registered = INT_MAX;
3059 discrim->lastseen = now;
3060 discrim->min_karma = INT_MIN;
3061 discrim->max_karma = INT_MAX;
3063 for (i=0; i<argc; i++) {
3064 if (i == argc - 1) {
3065 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3068 if (!irccasecmp(argv[i], "limit")) {
3069 discrim->limit = strtoul(argv[++i], NULL, 0);
3070 } else if (!irccasecmp(argv[i], "flags")) {
3071 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3072 } else if (!irccasecmp(argv[i], "registered")) {
3073 const char *cmp = argv[++i];
3074 if (cmp[0] == '<') {
3075 if (cmp[1] == '=') {
3076 discrim->min_registered = now - ParseInterval(cmp+2);
3078 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3080 } else if (cmp[0] == '=') {
3081 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3082 } else if (cmp[0] == '>') {
3083 if (cmp[1] == '=') {
3084 discrim->max_registered = now - ParseInterval(cmp+2);
3086 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3089 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3091 } else if (!irccasecmp(argv[i], "seen")) {
3092 discrim->lastseen = now - ParseInterval(argv[++i]);
3093 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3094 discrim->nickmask = argv[++i];
3095 } else if (!irccasecmp(argv[i], "hostmask")) {
3097 if (!irccasecmp(argv[i], "exact")) {
3098 if (i == argc - 1) {
3099 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3102 discrim->hostmask_type = EXACT;
3103 } else if (!irccasecmp(argv[i], "subset")) {
3104 if (i == argc - 1) {
3105 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3108 discrim->hostmask_type = SUBSET;
3109 } else if (!irccasecmp(argv[i], "superset")) {
3110 if (i == argc - 1) {
3111 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3114 discrim->hostmask_type = SUPERSET;
3115 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3116 if (i == argc - 1) {
3117 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3120 discrim->hostmask_type = LASTQUIT;
3123 discrim->hostmask_type = SUPERSET;
3125 discrim->hostmask = argv[++i];
3126 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3127 if (!irccasecmp(argv[++i], "*")) {
3128 discrim->handlemask = 0;
3130 discrim->handlemask = argv[i];
3132 } else if (!irccasecmp(argv[i], "email")) {
3133 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3134 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3136 } else if (!irccasecmp(argv[++i], "*")) {
3137 discrim->emailmask = 0;
3139 discrim->emailmask = argv[i];
3141 } else if (!irccasecmp(argv[i], "access")) {
3142 const char *cmp = argv[++i];
3143 if (cmp[0] == '<') {
3144 if (discrim->min_level == 0) discrim->min_level = 1;
3145 if (cmp[1] == '=') {
3146 discrim->max_level = strtoul(cmp+2, NULL, 0);
3148 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3150 } else if (cmp[0] == '=') {
3151 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3152 } else if (cmp[0] == '>') {
3153 if (cmp[1] == '=') {
3154 discrim->min_level = strtoul(cmp+2, NULL, 0);
3156 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3159 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3161 } else if (!irccasecmp(argv[i], "karma")) {
3162 const char *cmp = argv[++i];
3163 if (cmp[0] == '<') {
3164 if (cmp[1] == '=') {
3165 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3167 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3169 } else if (cmp[0] == '=') {
3170 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3171 } else if (cmp[0] == '>') {
3172 if (cmp[1] == '=') {
3173 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3175 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3178 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3181 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3192 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3194 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3195 || (discrim->flags_off & hi->flags)
3196 || (discrim->min_registered > hi->registered)
3197 || (discrim->max_registered < hi->registered)
3198 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3199 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3200 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3201 || (discrim->min_level > hi->opserv_level)
3202 || (discrim->max_level < hi->opserv_level)
3203 || (discrim->min_karma > hi->karma)
3204 || (discrim->max_karma < hi->karma)
3208 if (discrim->hostmask) {
3210 for (i=0; i<hi->masks->used; i++) {
3211 const char *mask = hi->masks->list[i];
3212 if ((discrim->hostmask_type == SUBSET)
3213 && (match_ircglobs(discrim->hostmask, mask))) break;
3214 else if ((discrim->hostmask_type == EXACT)
3215 && !irccasecmp(discrim->hostmask, mask)) break;
3216 else if ((discrim->hostmask_type == SUPERSET)
3217 && (match_ircglobs(mask, discrim->hostmask))) break;
3218 else if ((discrim->hostmask_type == LASTQUIT)
3219 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3221 if (i==hi->masks->used) return 0;
3223 if (discrim->nickmask) {
3224 struct nick_info *nick = hi->nicks;
3226 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3229 if (!nick) return 0;
3235 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3237 dict_iterator_t it, next;
3238 unsigned int matched;
3240 for (it = dict_first(nickserv_handle_dict), matched = 0;
3241 it && (matched < discrim->limit);
3243 next = iter_next(it);
3244 if (nickserv_discrim_match(discrim, iter_data(it))) {
3245 dsf(source, iter_data(it));
3253 search_print_func(struct userNode *source, struct handle_info *match)
3255 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3259 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3264 search_unregister_func (struct userNode *source, struct handle_info *match)
3266 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3267 nickserv_unregister_handle(match, source);
3271 nickserv_sort_accounts_by_access(const void *a, const void *b)
3273 const struct handle_info *hi_a = *(const struct handle_info**)a;
3274 const struct handle_info *hi_b = *(const struct handle_info**)b;
3275 if (hi_a->opserv_level != hi_b->opserv_level)
3276 return hi_b->opserv_level - hi_a->opserv_level;
3277 return irccasecmp(hi_a->handle, hi_b->handle);
3281 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3283 struct handle_info_list hil;
3284 struct helpfile_table tbl;
3289 memset(&hil, 0, sizeof(hil));
3290 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3291 struct handle_info *hi = iter_data(it);
3292 if (hi->opserv_level)
3293 handle_info_list_append(&hil, hi);
3295 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3296 tbl.length = hil.used + 1;
3298 tbl.flags = TABLE_NO_FREE;
3299 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3300 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3303 for (ii = 0; ii < hil.used; ) {
3304 ary = malloc(tbl.width * sizeof(ary[0]));
3305 ary[0] = hil.list[ii]->handle;
3306 ary[1] = strtab(hil.list[ii]->opserv_level);
3307 tbl.contents[++ii] = ary;
3309 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3310 reply("MSG_MATCH_COUNT", hil.used);
3311 for (ii = 0; ii < hil.used; ii++)
3312 free(tbl.contents[ii]);
3317 static NICKSERV_FUNC(cmd_search)
3319 struct nickserv_discrim *discrim;
3320 discrim_search_func action;
3321 struct svccmd *subcmd;
3322 unsigned int matches;
3325 NICKSERV_MIN_PARMS(3);
3326 sprintf(buf, "search %s", argv[1]);
3327 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3328 if (!irccasecmp(argv[1], "print"))
3329 action = search_print_func;
3330 else if (!irccasecmp(argv[1], "count"))
3331 action = search_count_func;
3332 else if (!irccasecmp(argv[1], "unregister"))
3333 action = search_unregister_func;
3335 reply("NSMSG_INVALID_ACTION", argv[1]);
3339 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3342 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3346 if (action == search_print_func)
3347 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3348 else if (action == search_count_func)
3349 discrim->limit = INT_MAX;
3351 matches = nickserv_discrim_search(discrim, action, user);
3354 reply("MSG_MATCH_COUNT", matches);
3356 reply("MSG_NO_MATCHES");
3362 static MODCMD_FUNC(cmd_checkpass)
3364 struct handle_info *hi;
3366 NICKSERV_MIN_PARMS(3);
3367 if (!(hi = get_handle_info(argv[1]))) {
3368 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3371 if (checkpass(argv[2], hi->passwd))
3372 reply("CHECKPASS_YES");
3374 reply("CHECKPASS_NO");
3380 nickserv_db_read_handle(const char *handle, dict_t obj)
3383 struct string_list *masks, *slist;
3384 struct handle_info *hi;
3385 struct userNode *authed_users;
3386 struct userData *channels;
3387 unsigned long int id;
3391 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3392 id = str ? strtoul(str, NULL, 0) : 0;
3393 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3395 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3398 if ((hi = get_handle_info(handle))) {
3399 authed_users = hi->users;
3400 channels = hi->channels;
3402 hi->channels = NULL;
3403 dict_remove(nickserv_handle_dict, hi->handle);
3405 authed_users = NULL;
3408 hi = register_handle(handle, str, id);
3410 hi->users = authed_users;
3411 while (authed_users) {
3412 authed_users->handle_info = hi;
3413 authed_users = authed_users->next_authed;
3416 hi->channels = channels;
3417 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3418 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3419 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3420 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3421 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3422 hi->language = language_find(str ? str : "C");
3423 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3424 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3425 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3427 hi->infoline = strdup(str);
3428 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3429 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3430 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3431 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3432 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3433 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3434 /* We want to read the nicks even if disable_nicks is set. This is so
3435 * that we don't lose the nick data entirely. */
3436 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3438 for (ii=0; ii<slist->used; ii++)
3439 register_nick(slist->list[ii], hi);
3441 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3443 for (ii=0; str[ii]; ii++)
3444 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3446 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3447 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3448 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3449 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3450 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3451 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3452 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3454 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3456 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3457 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3459 nickserv_set_email_addr(hi, str);
3460 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3462 hi->epithet = strdup(str);
3463 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3465 hi->fakehost = strdup(str);
3466 /* Read the "cookie" sub-database (if it exists). */
3467 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3469 const char *data, *type, *expires, *cookie_str;
3470 struct handle_cookie *cookie;
3472 cookie = calloc(1, sizeof(*cookie));
3473 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3474 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3475 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3476 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3477 if (!type || !expires || !cookie_str) {
3478 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3481 if (!irccasecmp(type, KEY_ACTIVATION))
3482 cookie->type = ACTIVATION;
3483 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3484 cookie->type = PASSWORD_CHANGE;
3485 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3486 cookie->type = EMAIL_CHANGE;
3487 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3488 cookie->type = ALLOWAUTH;
3490 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3493 cookie->expires = strtoul(expires, NULL, 0);
3494 if (cookie->expires < now)
3497 cookie->data = strdup(data);
3498 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3502 nickserv_bake_cookie(cookie);
3504 nickserv_free_cookie(cookie);
3506 /* Read the "notes" sub-database (if it exists). */
3507 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3510 struct handle_note *last_note;
3511 struct handle_note *note;
3514 for (it = dict_first(subdb); it; it = iter_next(it)) {
3515 const char *expires;
3523 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3525 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", id, hi->handle);
3528 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3529 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3530 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3531 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3532 if (!setter || !text || !set) {
3533 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", id, hi->handle);
3536 note = calloc(1, sizeof(*note) + strlen(text));
3538 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3539 note->set = strtoul(set, NULL, 10);
3540 note->id = strtoul(id, NULL, 10);
3541 safestrncpy(note->setter, setter, sizeof(note->setter));
3542 strcpy(note->note, text);
3544 last_note->next = note;
3553 nickserv_saxdb_read(dict_t db) {
3555 struct record_data *rd;
3557 for (it=dict_first(db); it; it=iter_next(it)) {
3559 nickserv_db_read_handle(iter_key(it), rd->d.object);
3564 static NICKSERV_FUNC(cmd_mergedb)
3566 struct timeval start, stop;
3569 NICKSERV_MIN_PARMS(2);
3570 gettimeofday(&start, NULL);
3571 if (!(db = parse_database(argv[1]))) {
3572 reply("NSMSG_DB_UNREADABLE", argv[1]);
3575 nickserv_saxdb_read(db);
3577 gettimeofday(&stop, NULL);
3578 stop.tv_sec -= start.tv_sec;
3579 stop.tv_usec -= start.tv_usec;
3580 if (stop.tv_usec < 0) {
3582 stop.tv_usec += 1000000;
3584 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3589 expire_handles(UNUSED_ARG(void *data))
3591 dict_iterator_t it, next;
3593 struct handle_info *hi;
3595 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3596 next = iter_next(it);
3598 if ((hi->opserv_level > 0)
3600 || HANDLE_FLAGGED(hi, FROZEN)
3601 || HANDLE_FLAGGED(hi, NODELETE)) {
3604 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3605 if ((now - hi->lastseen) > expiry) {
3606 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3607 nickserv_unregister_handle(hi, NULL);
3611 if (nickserv_conf.handle_expire_frequency)
3612 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3616 nickserv_load_dict(const char *fname)
3620 if (!(file = fopen(fname, "r"))) {
3621 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3624 while (!feof(file)) {
3625 fgets(line, sizeof(line), file);
3628 if (line[strlen(line)-1] == '\n')
3629 line[strlen(line)-1] = 0;
3630 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3633 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3636 static enum reclaim_action
3637 reclaim_action_from_string(const char *str) {
3639 return RECLAIM_NONE;
3640 else if (!irccasecmp(str, "warn"))
3641 return RECLAIM_WARN;
3642 else if (!irccasecmp(str, "svsnick"))
3643 return RECLAIM_SVSNICK;
3644 else if (!irccasecmp(str, "kill"))
3645 return RECLAIM_KILL;
3647 return RECLAIM_NONE;
3651 nickserv_conf_read(void)
3653 dict_t conf_node, child;
3657 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3658 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3661 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3663 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3664 if (nickserv_conf.valid_handle_regex_set)
3665 regfree(&nickserv_conf.valid_handle_regex);
3667 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3668 nickserv_conf.valid_handle_regex_set = !err;
3669 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3671 nickserv_conf.valid_handle_regex_set = 0;
3673 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3674 if (nickserv_conf.valid_nick_regex_set)
3675 regfree(&nickserv_conf.valid_nick_regex);
3677 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3678 nickserv_conf.valid_nick_regex_set = !err;
3679 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3681 nickserv_conf.valid_nick_regex_set = 0;
3683 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3685 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3686 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3687 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3688 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3689 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3690 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3691 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3692 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3693 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3694 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3695 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3696 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3697 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3698 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3699 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3700 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3701 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3702 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3703 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3704 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3705 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3706 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3707 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3708 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3709 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3711 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3712 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3713 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3715 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3716 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3717 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3719 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3720 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3721 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3722 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3723 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3724 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3725 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3726 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3727 if (!nickserv_conf.disable_nicks) {
3728 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3729 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3730 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3731 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3732 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3733 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3734 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3735 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3737 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3738 for (it=dict_first(child); it; it=iter_next(it)) {
3739 const char *key = iter_key(it), *value;
3743 if (!strncasecmp(key, "uc_", 3))
3744 flag = toupper(key[3]);
3745 else if (!strncasecmp(key, "lc_", 3))
3746 flag = tolower(key[3]);
3750 if ((pos = handle_inverse_flags[flag])) {
3751 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3752 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3755 if (nickserv_conf.weak_password_dict)
3756 dict_delete(nickserv_conf.weak_password_dict);
3757 nickserv_conf.weak_password_dict = dict_new();
3758 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3759 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3760 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3761 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3763 nickserv_load_dict(str);
3764 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3765 if (nickserv && str)
3766 NickChange(nickserv, str, 0);
3767 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3768 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3769 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3770 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3771 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3772 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3773 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3774 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3775 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3776 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3777 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3778 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3779 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3780 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3781 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3782 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3783 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3784 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3785 str = conf_get_data("server/network", RECDB_QSTRING);
3786 nickserv_conf.network_name = str ? str : "some IRC network";
3787 if (!nickserv_conf.auth_policer_params) {
3788 nickserv_conf.auth_policer_params = policer_params_new();
3789 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3790 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3792 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3793 for (it=dict_first(child); it; it=iter_next(it))
3794 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3798 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3800 char newnick[NICKLEN+1];
3809 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3811 case RECLAIM_SVSNICK:
3813 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3814 } while (GetUserH(newnick));
3815 irc_svsnick(nickserv, user, newnick);
3818 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3819 DelUser(user, nickserv, 1, msg);
3825 nickserv_reclaim_p(void *data) {
3826 struct userNode *user = data;
3827 struct nick_info *ni = get_nick_info(user->nick);
3829 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3833 check_user_nick(struct userNode *user) {
3834 struct nick_info *ni;
3835 user->modes &= ~FLAGS_REGNICK;
3836 if (!(ni = get_nick_info(user->nick)))
3838 if (user->handle_info == ni->owner) {
3839 user->modes |= FLAGS_REGNICK;
3843 if (nickserv_conf.warn_nick_owned)
3844 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3845 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3847 if (nickserv_conf.auto_reclaim_delay)
3848 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3850 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3855 handle_new_user(struct userNode *user)
3857 return check_user_nick(user);
3861 handle_account(struct userNode *user, const char *stamp)
3863 struct handle_info *hi;
3865 #ifdef WITH_PROTOCOL_P10
3866 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3868 hi = dict_find(nickserv_id_dict, stamp, NULL);
3872 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3875 set_user_handle_info(user, hi, 0);
3877 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3882 handle_nick_change(struct userNode *user, const char *old_nick)
3884 struct handle_info *hi;
3886 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3887 dict_remove(nickserv_allow_auth_dict, old_nick);
3888 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3890 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3891 check_user_nick(user);
3895 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3897 dict_remove(nickserv_allow_auth_dict, user->nick);
3898 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3899 set_user_handle_info(user, NULL, 0);
3902 static struct modcmd *
3903 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3905 if (min_level > 0) {
3907 sprintf(buf, "%u", min_level);
3908 if (must_be_qualified) {
3909 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3911 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3913 } else if (min_level == 0) {
3914 if (must_be_qualified) {
3915 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3917 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3920 if (must_be_qualified) {
3921 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3923 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3929 nickserv_db_cleanup(void)
3931 unreg_del_user_func(nickserv_remove_user);
3932 userList_clean(&curr_helpers);
3933 policer_params_delete(nickserv_conf.auth_policer_params);
3934 dict_delete(nickserv_handle_dict);
3935 dict_delete(nickserv_nick_dict);
3936 dict_delete(nickserv_opt_dict);
3937 dict_delete(nickserv_allow_auth_dict);
3938 dict_delete(nickserv_email_dict);
3939 dict_delete(nickserv_id_dict);
3940 dict_delete(nickserv_conf.weak_password_dict);
3941 free(auth_func_list);
3942 free(unreg_func_list);
3944 free(allowauth_func_list);
3945 free(handle_merge_func_list);
3946 free(failpw_func_list);
3947 if (nickserv_conf.valid_handle_regex_set)
3948 regfree(&nickserv_conf.valid_handle_regex);
3949 if (nickserv_conf.valid_nick_regex_set)
3950 regfree(&nickserv_conf.valid_nick_regex);
3954 init_nickserv(const char *nick)
3957 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3958 reg_new_user_func(handle_new_user);
3959 reg_nick_change_func(handle_nick_change);
3960 reg_del_user_func(nickserv_remove_user);
3961 reg_account_func(handle_account);
3963 /* set up handle_inverse_flags */
3964 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3965 for (i=0; handle_flags[i]; i++) {
3966 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3967 flag_access_levels[i] = 0;
3970 conf_register_reload(nickserv_conf_read);
3971 nickserv_opt_dict = dict_new();
3972 nickserv_email_dict = dict_new();
3973 dict_set_free_keys(nickserv_email_dict, free);
3974 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3976 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3977 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3978 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3979 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3980 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3981 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3982 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3983 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3984 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3985 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3986 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3987 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3988 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3989 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3990 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3991 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3992 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3993 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3994 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
3995 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
3996 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
3997 if (!nickserv_conf.disable_nicks) {
3998 /* nick management commands */
3999 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4000 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4001 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4002 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4003 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4004 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4006 if (nickserv_conf.email_enabled) {
4007 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4008 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4009 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4010 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4011 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4013 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4014 /* miscellaneous commands */
4015 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4016 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4017 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4018 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4019 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4021 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4022 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4023 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4024 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4025 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4026 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4027 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4028 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4029 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4030 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4031 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4032 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4033 if (nickserv_conf.titlehost_suffix) {
4034 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4035 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4037 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4038 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4039 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4041 nickserv_handle_dict = dict_new();
4042 dict_set_free_keys(nickserv_handle_dict, free);
4043 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4045 nickserv_id_dict = dict_new();
4046 dict_set_free_keys(nickserv_id_dict, free);
4048 nickserv_nick_dict = dict_new();
4049 dict_set_free_data(nickserv_nick_dict, free);
4051 nickserv_allow_auth_dict = dict_new();
4053 userList_init(&curr_helpers);
4056 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4057 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4058 nickserv_service = service_register(nickserv);
4060 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4061 reg_exit_func(nickserv_db_cleanup);
4062 if(nickserv_conf.handle_expire_frequency)
4063 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4064 message_register_table(msgtab);