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"
74 #define KEY_OUNREGISTER_INACTIVE "ounregister_inactive"
75 #define KEY_OUNREGISTER_FLAGS "ounregister_flags"
78 #define KEY_PASSWD "passwd"
79 #define KEY_NICKS "nicks"
80 #define KEY_MASKS "masks"
81 #define KEY_OPSERV_LEVEL "opserv_level"
82 #define KEY_FLAGS "flags"
83 #define KEY_REGISTER_ON "register"
84 #define KEY_LAST_SEEN "lastseen"
85 #define KEY_INFO "info"
86 #define KEY_USERLIST_STYLE "user_style"
87 #define KEY_SCREEN_WIDTH "screen_width"
88 #define KEY_LAST_AUTHED_HOST "last_authed_host"
89 #define KEY_LAST_QUIT_HOST "last_quit_host"
90 #define KEY_EMAIL_ADDR "email_addr"
91 #define KEY_COOKIE "cookie"
92 #define KEY_COOKIE_DATA "data"
93 #define KEY_COOKIE_TYPE "type"
94 #define KEY_COOKIE_EXPIRES "expires"
95 #define KEY_ACTIVATION "activation"
96 #define KEY_PASSWORD_CHANGE "password change"
97 #define KEY_EMAIL_CHANGE "email change"
98 #define KEY_ALLOWAUTH "allowauth"
99 #define KEY_EPITHET "epithet"
100 #define KEY_TABLE_WIDTH "table_width"
101 #define KEY_MAXLOGINS "maxlogins"
102 #define KEY_FAKEHOST "fakehost"
103 #define KEY_NOTES "notes"
104 #define KEY_NOTE_EXPIRES "expires"
105 #define KEY_NOTE_SET "set"
106 #define KEY_NOTE_SETTER "setter"
107 #define KEY_NOTE_NOTE "note"
108 #define KEY_KARMA "karma"
110 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
112 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
113 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
114 typedef OPTION_FUNC(option_func_t);
116 DEFINE_LIST(handle_info_list, struct handle_info*);
118 #define NICKSERV_MIN_PARMS(N) do { \
120 reply("MSG_MISSING_PARAMS", argv[0]); \
121 svccmd_send_help(user, nickserv, cmd); \
125 struct userNode *nickserv;
126 struct userList curr_helpers;
127 const char *handle_flags = HANDLE_FLAGS;
129 static struct module *nickserv_module;
130 static struct service *nickserv_service;
131 static struct log_type *NS_LOG;
132 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
133 static dict_t nickserv_id_dict; /* contains struct handle_info* */
134 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
135 static dict_t nickserv_opt_dict; /* contains option_func_t* */
136 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
137 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
138 static char handle_inverse_flags[256];
139 static unsigned int flag_access_levels[32];
140 static const struct message_entry msgtab[] = {
141 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
142 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
143 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
144 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
145 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
146 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
147 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
148 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
149 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
150 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
151 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
152 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
153 { "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." },
154 { "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." },
155 { "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." },
156 { "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." },
157 { "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." },
158 { "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." },
159 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
160 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
161 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
162 { "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." },
163 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
164 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
165 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
166 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
167 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
168 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
169 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
170 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
171 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
172 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
173 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
174 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
175 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
176 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
177 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
178 { "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)" },
179 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
180 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
181 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
182 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
183 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
184 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
185 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
186 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
187 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
188 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
189 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
190 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
191 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
192 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
193 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
194 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
195 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
196 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
197 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
198 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
199 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
200 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
201 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
202 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
203 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
204 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
205 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
206 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
207 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
208 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
209 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
210 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
211 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
212 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
213 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
214 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
215 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
216 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
217 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
218 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
219 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
220 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
221 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
222 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
223 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
224 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
225 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
226 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
227 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
228 { "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)." },
229 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
230 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
231 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
232 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
233 { "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." },
234 { "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." },
235 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
236 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
237 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
238 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
239 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
240 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
241 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
242 { "NSMSG_PASS_SUCCESS", "Password changed." },
243 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
244 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
245 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
246 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
247 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
248 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
249 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
250 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
251 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
252 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
253 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
254 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
255 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
256 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
257 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
258 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
259 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
260 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
261 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
262 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
263 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
264 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
265 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
266 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
267 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
268 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
269 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
270 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
271 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
272 { "NSMSG_NO_ACCESS", "Access denied." },
273 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
274 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
275 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
276 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
277 { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
278 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
279 { "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." },
280 { "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." },
281 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
282 { "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." },
283 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
284 { "NSMSG_SEARCH_MATCH", "Match: %s" },
285 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
286 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
287 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
288 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
289 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
290 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
291 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
292 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
293 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
294 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
295 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
296 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
297 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
298 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
299 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
300 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
301 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
302 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
303 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
304 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
305 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
306 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
307 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
308 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
309 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
310 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
311 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
312 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
313 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
314 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
315 { "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." },
316 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
317 { "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." },
318 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
319 { "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." },
320 { "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." },
321 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
322 { "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." },
323 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
324 { "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." },
325 { "CHECKPASS_YES", "Yes." },
326 { "CHECKPASS_NO", "No." },
330 enum reclaim_action {
336 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
337 static void nickserv_reclaim_p(void *data);
338 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
341 unsigned int disable_nicks : 1;
342 unsigned int valid_handle_regex_set : 1;
343 unsigned int valid_nick_regex_set : 1;
344 unsigned int autogag_enabled : 1;
345 unsigned int email_enabled : 1;
346 unsigned int email_required : 1;
347 unsigned int default_hostmask : 1;
348 unsigned int warn_nick_owned : 1;
349 unsigned int warn_clone_auth : 1;
350 unsigned long nicks_per_handle;
351 unsigned long password_min_length;
352 unsigned long password_min_digits;
353 unsigned long password_min_upper;
354 unsigned long password_min_lower;
355 unsigned long db_backup_frequency;
356 unsigned long handle_expire_frequency;
357 unsigned long autogag_duration;
358 unsigned long email_visible_level;
359 unsigned long cookie_timeout;
360 unsigned long handle_expire_delay;
361 unsigned long nochan_handle_expire_delay;
362 unsigned long modoper_level;
363 unsigned long set_epithet_level;
364 unsigned long set_title_level;
365 unsigned long set_fakehost_level;
366 unsigned long handles_per_email;
367 unsigned long email_search_level;
368 const char *network_name;
369 const char *titlehost_suffix;
370 regex_t valid_handle_regex;
371 regex_t valid_nick_regex;
372 dict_t weak_password_dict;
373 struct policer_params *auth_policer_params;
374 enum reclaim_action reclaim_action;
375 enum reclaim_action auto_reclaim_action;
376 unsigned long auto_reclaim_delay;
377 unsigned char default_maxlogins;
378 unsigned char hard_maxlogins;
379 unsigned long ounregister_inactive;
380 unsigned long ounregister_flags;
383 /* We have 2^32 unique account IDs to use. */
384 unsigned long int highest_id = 0;
386 #define WALK_NOTES(HANDLE, PREV, NOTE) \
387 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
388 if (NOTE->expires && NOTE->expires < now) { \
389 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
391 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
395 canonicalize_hostmask(char *mask)
397 char *out = mask, *temp;
398 if ((temp = strchr(mask, '!'))) {
400 while (*temp) *out++ = *temp++;
406 static struct handle_info *
407 register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
409 struct handle_info *hi;
411 #ifdef WITH_PROTOCOL_BAHAMUT
412 char id_base64[IDLEN + 1];
415 /* Assign a unique account ID to the account; note that 0 is
416 an invalid account ID. 1 is therefore the first account ID. */
418 id = 1 + highest_id++;
420 /* Note: highest_id is and must always be the highest ID. */
421 if (id > highest_id) {
425 inttobase64(id_base64, id, IDLEN);
427 /* Make sure an account with the same ID doesn't exist. If a
428 duplicate is found, log some details and assign a new one.
429 This should be impossible, but it never hurts to expect it. */
430 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
431 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
437 hi = calloc(1, sizeof(*hi));
438 hi->userlist_style = HI_DEFAULT_STYLE;
439 hi->handle = strdup(handle);
440 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
442 dict_insert(nickserv_handle_dict, hi->handle, hi);
444 #ifdef WITH_PROTOCOL_BAHAMUT
446 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
453 register_nick(const char *nick, struct handle_info *owner)
455 struct nick_info *ni;
456 ni = malloc(sizeof(struct nick_info));
457 safestrncpy(ni->nick, nick, sizeof(ni->nick));
459 ni->next = owner->nicks;
461 dict_insert(nickserv_nick_dict, ni->nick, ni);
465 delete_nick(struct nick_info *ni)
467 struct nick_info *last, *next;
468 struct userNode *user;
469 /* Check to see if we should mark a user as unregistered. */
470 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
471 user->modes &= ~FLAGS_REGNICK;
474 /* Remove ni from the nick_info linked list. */
475 if (ni == ni->owner->nicks) {
476 ni->owner->nicks = ni->next;
478 last = ni->owner->nicks;
484 last->next = next->next;
486 dict_remove(nickserv_nick_dict, ni->nick);
489 static unreg_func_t *unreg_func_list;
490 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
493 reg_unreg_func(unreg_func_t func)
495 if (unreg_func_used == unreg_func_size) {
496 if (unreg_func_size) {
497 unreg_func_size <<= 1;
498 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
501 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
504 unreg_func_list[unreg_func_used++] = func;
508 nickserv_free_cookie(void *data)
510 struct handle_cookie *cookie = data;
511 if (cookie->hi) cookie->hi->cookie = NULL;
512 if (cookie->data) free(cookie->data);
517 free_handle_info(void *vhi)
519 struct handle_info *hi = vhi;
521 #ifdef WITH_PROTOCOL_BAHAMUT
524 inttobase64(id, hi->id, IDLEN);
525 dict_remove(nickserv_id_dict, id);
528 free_string_list(hi->masks);
532 delete_nick(hi->nicks);
537 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
538 nickserv_free_cookie(hi->cookie);
541 struct handle_note *note = hi->notes;
542 hi->notes = note->next;
545 if (hi->email_addr) {
546 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
547 handle_info_list_remove(hil, hi);
549 dict_remove(nickserv_email_dict, hi->email_addr);
554 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
557 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
561 for (n=0; n<unreg_func_used; n++)
562 unreg_func_list[n](notify, hi);
564 set_user_handle_info(hi->users, NULL, 0);
566 if (nickserv_conf.disable_nicks)
567 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
569 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
571 dict_remove(nickserv_handle_dict, hi->handle);
575 get_handle_info(const char *handle)
577 return dict_find(nickserv_handle_dict, handle, 0);
581 get_nick_info(const char *nick)
583 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
587 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
592 for (nn=0; nn<channel->members.used; ++nn) {
593 mn = channel->members.list[nn];
594 if ((mn->user != except) && (mn->user->handle_info == handle))
601 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
602 if (!user->handle_info) {
604 send_message(user, bot, "MSG_AUTHENTICATE");
608 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
610 send_message(user, bot, "NSMSG_NO_ACCESS");
614 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
616 send_message(user, bot, "MSG_OPER_SUSPENDED");
620 if (user->handle_info->opserv_level < min_level) {
622 send_message(user, bot, "NSMSG_NO_ACCESS");
630 is_valid_handle(const char *handle)
632 struct userNode *user;
633 /* cant register a juped nick/service nick as handle, to prevent confusion */
634 user = GetUserH(handle);
635 if (user && IsLocal(user))
637 /* check against maximum length */
638 if (strlen(handle) > NICKSERV_HANDLE_LEN)
640 /* for consistency, only allow account names that could be nicks */
641 if (!is_valid_nick(handle))
643 /* disallow account names that look like bad words */
644 if (opserv_bad_channel(handle))
646 /* test either regex or containing all valid chars */
647 if (nickserv_conf.valid_handle_regex_set) {
648 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
651 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
652 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
656 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
661 is_registerable_nick(const char *nick)
663 /* make sure it could be used as an account name */
664 if (!is_valid_handle(nick))
667 if (strlen(nick) > NICKLEN)
669 /* test either regex or as valid handle */
670 if (nickserv_conf.valid_nick_regex_set) {
671 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
674 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
675 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
683 is_valid_email_addr(const char *email)
685 return strchr(email, '@') != NULL;
689 visible_email_addr(struct userNode *user, struct handle_info *hi)
691 if (hi->email_addr) {
692 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
693 return hi->email_addr;
703 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
705 struct handle_info *hi;
706 struct userNode *target;
710 if (!(hi = get_handle_info(++name))) {
711 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
716 if (!(target = GetUserH(name))) {
717 send_message(user, service, "MSG_NICK_UNKNOWN", name);
720 if (IsLocal(target)) {
721 if (IsService(target))
722 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
724 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
727 if (!(hi = target->handle_info)) {
728 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
736 oper_outranks(struct userNode *user, struct handle_info *hi) {
737 if (user->handle_info->opserv_level > hi->opserv_level)
739 if (user->handle_info->opserv_level == hi->opserv_level) {
740 if ((user->handle_info->opserv_level == 1000)
741 || (user->handle_info == hi)
742 || ((user->handle_info->opserv_level == 0)
743 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
744 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
748 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
752 static struct handle_info *
753 get_victim_oper(struct userNode *user, const char *target)
755 struct handle_info *hi;
756 if (!(hi = smart_get_handle_info(nickserv, user, target)))
758 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
759 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
762 return oper_outranks(user, hi) ? hi : NULL;
766 valid_user_for(struct userNode *user, struct handle_info *hi)
770 /* If no hostmasks on the account, allow it. */
771 if (!hi->masks->used)
773 /* If any hostmask matches, allow it. */
774 for (ii=0; ii<hi->masks->used; ii++)
775 if (user_matches_glob(user, hi->masks->list[ii], 0))
777 /* If they are allowauthed to this account, allow it (removing the aa). */
778 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
779 dict_remove(nickserv_allow_auth_dict, user->nick);
782 /* The user is not allowed to use this account. */
787 is_secure_password(const char *handle, const char *pass, struct userNode *user)
790 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
794 if (len < nickserv_conf.password_min_length) {
796 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
799 if (!irccasecmp(pass, handle)) {
801 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
804 dict_find(nickserv_conf.weak_password_dict, pass, &p);
807 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
810 for (i=0; i<len; i++) {
811 if (isdigit(pass[i]))
813 if (isupper(pass[i]))
815 if (islower(pass[i]))
818 if ((cnt_lower < nickserv_conf.password_min_lower)
819 || (cnt_upper < nickserv_conf.password_min_upper)
820 || (cnt_digits < nickserv_conf.password_min_digits)) {
822 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
828 static auth_func_t *auth_func_list;
829 static unsigned int auth_func_size = 0, auth_func_used = 0;
832 reg_auth_func(auth_func_t func)
834 if (auth_func_used == auth_func_size) {
835 if (auth_func_size) {
836 auth_func_size <<= 1;
837 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
840 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
843 auth_func_list[auth_func_used++] = func;
846 static handle_rename_func_t *rf_list;
847 static unsigned int rf_list_size, rf_list_used;
850 reg_handle_rename_func(handle_rename_func_t func)
852 if (rf_list_used == rf_list_size) {
855 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
858 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
861 rf_list[rf_list_used++] = func;
865 generate_fakehost(struct handle_info *handle)
867 extern const char *hidden_host_suffix;
868 static char buffer[HOSTLEN+1];
870 if (!handle->fakehost) {
871 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
873 } else if (handle->fakehost[0] == '.') {
874 /* A leading dot indicates the stored value is actually a title. */
875 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
878 return handle->fakehost;
882 apply_fakehost(struct handle_info *handle)
884 struct userNode *target;
889 fake = generate_fakehost(handle);
890 for (target = handle->users; target; target = target->next_authed)
891 assign_fakehost(target, fake, 1);
895 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
898 struct handle_info *old_info;
900 /* This can happen if somebody uses COOKIE while authed, or if
901 * they re-auth to their current handle (which is silly, but users
903 if (user->handle_info == hi)
906 if (user->handle_info) {
907 struct userNode *other;
910 userList_remove(&curr_helpers, user);
912 /* remove from next_authed linked list */
913 if (user->handle_info->users == user) {
914 user->handle_info->users = user->next_authed;
916 for (other = user->handle_info->users;
917 other->next_authed != user;
918 other = other->next_authed) ;
919 other->next_authed = user->next_authed;
921 /* if nobody left on old handle, and they're not an oper, remove !god */
922 if (!user->handle_info->users && !user->handle_info->opserv_level)
923 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
924 /* record them as being last seen at this time */
925 user->handle_info->lastseen = now;
926 /* and record their hostmask */
927 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
929 old_info = user->handle_info;
930 user->handle_info = hi;
931 if (hi && !hi->users && !hi->opserv_level)
932 HANDLE_CLEAR_FLAG(hi, HELPING);
933 for (n=0; n<auth_func_used; n++)
934 auth_func_list[n](user, old_info);
936 struct nick_info *ni;
938 HANDLE_CLEAR_FLAG(hi, FROZEN);
939 if (nickserv_conf.warn_clone_auth) {
940 struct userNode *other;
941 for (other = hi->users; other; other = other->next_authed)
942 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
944 user->next_authed = hi->users;
947 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
948 userList_append(&curr_helpers, user);
950 if (hi->fakehost || old_info)
954 #ifdef WITH_PROTOCOL_BAHAMUT
955 /* Stamp users with their account ID. */
957 inttobase64(id, hi->id, IDLEN);
958 #elif WITH_PROTOCOL_P10
959 /* Stamp users with their account name. */
960 char *id = hi->handle;
962 const char *id = "???";
964 if (!nickserv_conf.disable_nicks) {
965 struct nick_info *ni;
966 for (ni = hi->nicks; ni; ni = ni->next) {
967 if (!irccasecmp(user->nick, ni->nick)) {
968 user->modes |= FLAGS_REGNICK;
976 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
977 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
979 /* We cannot clear the user's account ID, unfortunately. */
980 user->next_authed = NULL;
984 static struct handle_info*
985 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
987 struct handle_info *hi;
988 struct nick_info *ni;
989 char crypted[MD5_CRYPT_LENGTH];
991 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
992 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
996 if (!is_secure_password(handle, passwd, user))
999 cryptpass(passwd, crypted);
1000 hi = register_handle(handle, crypted, 0);
1001 hi->masks = alloc_string_list(1);
1003 hi->language = lang_C;
1004 hi->registered = now;
1006 hi->flags = HI_DEFAULT_FLAGS;
1007 if (settee && !no_auth)
1008 set_user_handle_info(settee, hi, 1);
1011 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1012 else if (nickserv_conf.disable_nicks)
1013 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1014 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1015 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1017 register_nick(user->nick, hi);
1018 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1020 if (settee && (user != settee))
1021 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1026 nickserv_bake_cookie(struct handle_cookie *cookie)
1028 cookie->hi->cookie = cookie;
1029 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1033 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1035 struct handle_cookie *cookie;
1036 char subject[128], body[4096], *misc;
1037 const char *netname, *fmt;
1041 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1045 cookie = calloc(1, sizeof(*cookie));
1047 cookie->type = type;
1048 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1049 cookie->expires = now + nickserv_conf.cookie_timeout;
1050 inttobase64(cookie->cookie, rand(), 5);
1051 inttobase64(cookie->cookie+5, rand(), 5);
1053 netname = nickserv_conf.network_name;
1056 switch (cookie->type) {
1058 hi->passwd[0] = 0; /* invalidate password */
1059 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1060 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1061 snprintf(subject, sizeof(subject), fmt, netname);
1062 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1063 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1066 case PASSWORD_CHANGE:
1067 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1068 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1069 snprintf(subject, sizeof(subject), fmt, netname);
1070 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1071 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1074 misc = hi->email_addr;
1075 hi->email_addr = cookie->data;
1077 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1078 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1079 snprintf(subject, sizeof(subject), fmt, netname);
1080 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1081 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1082 mail_send(nickserv, hi, subject, body, 1);
1083 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1084 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1086 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1087 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1088 snprintf(subject, sizeof(subject), fmt, netname);
1089 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1090 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1091 mail_send(nickserv, hi, subject, body, 1);
1094 hi->email_addr = misc;
1097 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1098 snprintf(subject, sizeof(subject), fmt, netname);
1099 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1100 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1101 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1104 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1108 mail_send(nickserv, hi, subject, body, first_time);
1109 nickserv_bake_cookie(cookie);
1113 nickserv_eat_cookie(struct handle_cookie *cookie)
1115 cookie->hi->cookie = NULL;
1116 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1117 nickserv_free_cookie(cookie);
1121 nickserv_free_email_addr(void *data)
1123 handle_info_list_clean(data);
1128 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1130 struct handle_info_list *hil;
1131 /* Remove from old handle_info_list ... */
1132 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1133 handle_info_list_remove(hil, hi);
1134 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1135 hi->email_addr = NULL;
1137 /* Add to the new list.. */
1138 if (new_email_addr) {
1139 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1140 hil = calloc(1, sizeof(*hil));
1141 hil->tag = strdup(new_email_addr);
1142 handle_info_list_init(hil);
1143 dict_insert(nickserv_email_dict, hil->tag, hil);
1145 handle_info_list_append(hil, hi);
1146 hi->email_addr = hil->tag;
1150 static NICKSERV_FUNC(cmd_register)
1153 struct handle_info *hi;
1154 const char *email_addr, *password;
1157 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1158 /* Require the first handle registered to belong to someone +o. */
1159 reply("NSMSG_REQUIRE_OPER");
1163 if (user->handle_info) {
1164 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1168 if (IsRegistering(user)) {
1169 reply("NSMSG_ALREADY_REGISTERING");
1173 if (IsStamped(user)) {
1174 /* Unauthenticated users might still have been stamped
1175 previously and could therefore have a hidden host;
1176 do not allow them to register a new account. */
1177 reply("NSMSG_STAMPED_REGISTER");
1181 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1183 if (!is_valid_handle(argv[1])) {
1184 reply("NSMSG_BAD_HANDLE", argv[1]);
1188 if ((argc >= 4) && nickserv_conf.email_enabled) {
1189 struct handle_info_list *hil;
1192 /* Remember email address. */
1193 email_addr = argv[3];
1195 /* Check that the email address looks valid.. */
1196 if (!is_valid_email_addr(email_addr)) {
1197 reply("NSMSG_BAD_EMAIL_ADDR");
1201 /* .. and that we are allowed to send to it. */
1202 if ((str = mail_prohibited_address(email_addr))) {
1203 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1207 /* If we do email verify, make sure we don't spam the address. */
1208 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1210 for (nn=0; nn<hil->used; nn++) {
1211 if (hil->list[nn]->cookie) {
1212 reply("NSMSG_EMAIL_UNACTIVATED");
1216 if (hil->used >= nickserv_conf.handles_per_email) {
1217 reply("NSMSG_EMAIL_OVERUSED");
1230 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1232 /* Add any masks they should get. */
1233 if (nickserv_conf.default_hostmask) {
1234 string_list_append(hi->masks, strdup("*@*"));
1236 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1237 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1238 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1241 /* If they're the first to register, give them level 1000. */
1242 if (dict_size(nickserv_handle_dict) == 1) {
1243 hi->opserv_level = 1000;
1244 reply("NSMSG_ROOT_HANDLE", argv[1]);
1247 /* Set their email address. */
1249 nickserv_set_email_addr(hi, email_addr);
1251 /* If they need to do email verification, tell them. */
1253 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1255 /* Set registering flag.. */
1256 user->modes |= FLAGS_REGISTERING;
1261 static NICKSERV_FUNC(cmd_oregister)
1264 struct userNode *settee;
1265 struct handle_info *hi;
1267 NICKSERV_MIN_PARMS(3);
1269 if (!is_valid_handle(argv[1])) {
1270 reply("NSMSG_BAD_HANDLE", argv[1]);
1277 } else if (strchr(argv[3], '@')) {
1278 mask = canonicalize_hostmask(strdup(argv[3]));
1280 settee = GetUserH(argv[4]);
1282 reply("MSG_NICK_UNKNOWN", argv[4]);
1289 } else if ((settee = GetUserH(argv[3]))) {
1290 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1292 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1295 if (settee && settee->handle_info) {
1296 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1300 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1305 string_list_append(hi->masks, mask);
1309 static NICKSERV_FUNC(cmd_handleinfo)
1312 unsigned int i, pos=0, herelen;
1313 struct userNode *target, *next_un;
1314 struct handle_info *hi;
1315 const char *nsmsg_none;
1319 if (!(hi = user->handle_info)) {
1320 reply("NSMSG_MUST_AUTH");
1323 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1327 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1328 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1329 #ifdef WITH_PROTOCOL_BAHAMUT
1330 reply("NSMSG_HANDLEINFO_ID", hi->id);
1332 feh = hi->registered;
1333 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1336 intervalString(buff, now - hi->lastseen, user->handle_info);
1337 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1339 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1342 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1343 if (HANDLE_FLAGGED(hi, FROZEN))
1344 reply("NSMSG_HANDLEINFO_VACATION");
1346 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1347 struct do_not_register *dnr;
1348 if ((dnr = chanserv_is_dnr(NULL, hi)))
1349 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1350 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1352 } else if (hi != user->handle_info)
1356 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1358 if (nickserv_conf.email_enabled)
1359 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1363 switch (hi->cookie->type) {
1364 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1365 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1366 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1367 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1368 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1373 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1375 reply("NSMSG_HANDLEINFO_NO_NOTES");
1377 struct handle_note *prev, *note;
1379 WALK_NOTES(hi, prev, note) {
1380 char set_time[INTERVALLEN];
1381 intervalString(set_time, now - note->set, user->handle_info);
1382 if (note->expires) {
1383 char exp_time[INTERVALLEN];
1384 intervalString(exp_time, note->expires - now, user->handle_info);
1385 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1387 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1394 unsigned long flen = 1;
1395 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1397 for (i=0, flen=1; handle_flags[i]; i++)
1398 if (hi->flags & 1 << i)
1399 flags[flen++] = handle_flags[i];
1401 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1403 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1406 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1407 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1408 || (hi->opserv_level > 0)) {
1409 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1413 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1415 if (hi->last_quit_host[0])
1416 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1418 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1420 if (nickserv_conf.disable_nicks) {
1421 /* nicks disabled; don't show anything about registered nicks */
1422 } else if (hi->nicks) {
1423 struct nick_info *ni, *next_ni;
1424 for (ni = hi->nicks; ni; ni = next_ni) {
1425 herelen = strlen(ni->nick);
1426 if (pos + herelen + 1 > ArrayLength(buff)) {
1428 goto print_nicks_buff;
1432 memcpy(buff+pos, ni->nick, herelen);
1433 pos += herelen; buff[pos++] = ' ';
1437 reply("NSMSG_HANDLEINFO_NICKS", buff);
1442 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1445 if (hi->masks->used) {
1446 for (i=0; i < hi->masks->used; i++) {
1447 herelen = strlen(hi->masks->list[i]);
1448 if (pos + herelen + 1 > ArrayLength(buff)) {
1450 goto print_mask_buff;
1452 memcpy(buff+pos, hi->masks->list[i], herelen);
1453 pos += herelen; buff[pos++] = ' ';
1454 if (i+1 == hi->masks->used) {
1457 reply("NSMSG_HANDLEINFO_MASKS", buff);
1462 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1466 struct userData *channel, *next;
1469 for (channel = hi->channels; channel; channel = next) {
1470 next = channel->u_next;
1471 name = channel->channel->channel->name;
1472 herelen = strlen(name);
1473 if (pos + herelen + 7 > ArrayLength(buff)) {
1475 goto print_chans_buff;
1477 if (IsUserSuspended(channel))
1479 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1483 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1488 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1491 for (target = hi->users; target; target = next_un) {
1492 herelen = strlen(target->nick);
1493 if (pos + herelen + 1 > ArrayLength(buff)) {
1495 goto print_cnick_buff;
1497 next_un = target->next_authed;
1499 memcpy(buff+pos, target->nick, herelen);
1500 pos += herelen; buff[pos++] = ' ';
1504 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1512 static NICKSERV_FUNC(cmd_userinfo)
1514 struct userNode *target;
1516 NICKSERV_MIN_PARMS(2);
1517 if (!(target = GetUserH(argv[1]))) {
1518 reply("MSG_NICK_UNKNOWN", argv[1]);
1521 if (target->handle_info)
1522 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1524 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1528 static NICKSERV_FUNC(cmd_nickinfo)
1530 struct nick_info *ni;
1532 NICKSERV_MIN_PARMS(2);
1533 if (!(ni = get_nick_info(argv[1]))) {
1534 reply("MSG_NICK_UNKNOWN", argv[1]);
1537 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1541 static NICKSERV_FUNC(cmd_notes)
1543 struct handle_info *hi;
1544 struct handle_note *prev, *note;
1547 NICKSERV_MIN_PARMS(2);
1548 if (!(hi = get_victim_oper(user, argv[1])))
1551 WALK_NOTES(hi, prev, note) {
1552 char set_time[INTERVALLEN];
1553 intervalString(set_time, now - note->set, user->handle_info);
1554 if (note->expires) {
1555 char exp_time[INTERVALLEN];
1556 intervalString(exp_time, note->expires - now, user->handle_info);
1557 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1559 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1563 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1567 static NICKSERV_FUNC(cmd_rename_handle)
1569 struct handle_info *hi;
1570 char msgbuf[MAXLEN], *old_handle;
1573 NICKSERV_MIN_PARMS(3);
1574 if (!(hi = get_victim_oper(user, argv[1])))
1576 if (!is_valid_handle(argv[2])) {
1577 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1580 if (get_handle_info(argv[2])) {
1581 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1585 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1586 hi->handle = strdup(argv[2]);
1587 dict_insert(nickserv_handle_dict, hi->handle, hi);
1588 for (nn=0; nn<rf_list_used; nn++)
1589 rf_list[nn](hi, old_handle);
1590 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1591 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1592 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1597 static failpw_func_t *failpw_func_list;
1598 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1601 reg_failpw_func(failpw_func_t func)
1603 if (failpw_func_used == failpw_func_size) {
1604 if (failpw_func_size) {
1605 failpw_func_size <<= 1;
1606 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1608 failpw_func_size = 8;
1609 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1612 failpw_func_list[failpw_func_used++] = func;
1615 static NICKSERV_FUNC(cmd_auth)
1617 int pw_arg, used, maxlogins;
1618 struct handle_info *hi;
1620 struct userNode *other;
1622 if (user->handle_info) {
1623 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1626 if (IsStamped(user)) {
1627 /* Unauthenticated users might still have been stamped
1628 previously and could therefore have a hidden host;
1629 do not allow them to authenticate. */
1630 reply("NSMSG_STAMPED_AUTH");
1634 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1636 } else if (argc == 2) {
1637 if (nickserv_conf.disable_nicks) {
1638 if (!(hi = get_handle_info(user->nick))) {
1639 reply("NSMSG_HANDLE_NOT_FOUND");
1643 /* try to look up their handle from their nick */
1644 struct nick_info *ni;
1645 ni = get_nick_info(user->nick);
1647 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1654 reply("MSG_MISSING_PARAMS", argv[0]);
1655 svccmd_send_help(user, nickserv, cmd);
1659 reply("NSMSG_HANDLE_NOT_FOUND");
1662 /* Responses from here on look up the language used by the handle they asked about. */
1663 passwd = argv[pw_arg];
1664 if (!valid_user_for(user, hi)) {
1665 if (hi->email_addr && nickserv_conf.email_enabled)
1666 send_message_type(4, user, cmd->parent->bot,
1667 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1670 send_message_type(4, user, cmd->parent->bot,
1671 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1673 argv[pw_arg] = "BADMASK";
1676 if (!checkpass(passwd, hi->passwd)) {
1678 send_message_type(4, user, cmd->parent->bot,
1679 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1680 argv[pw_arg] = "BADPASS";
1681 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1682 if (nickserv_conf.autogag_enabled) {
1683 if (!user->auth_policer.params) {
1684 user->auth_policer.last_req = now;
1685 user->auth_policer.params = nickserv_conf.auth_policer_params;
1687 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1689 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1690 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1691 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1693 argv[pw_arg] = "GAGGED";
1698 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1699 send_message_type(4, user, cmd->parent->bot,
1700 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1701 argv[pw_arg] = "SUSPENDED";
1704 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1705 for (used = 0, other = hi->users; other; other = other->next_authed) {
1706 if (++used >= maxlogins) {
1707 send_message_type(4, user, cmd->parent->bot,
1708 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1710 argv[pw_arg] = "MAXLOGINS";
1715 set_user_handle_info(user, hi, 1);
1716 if (nickserv_conf.email_required && !hi->email_addr)
1717 reply("NSMSG_PLEASE_SET_EMAIL");
1718 if (!is_secure_password(hi->handle, passwd, NULL))
1719 reply("NSMSG_WEAK_PASSWORD");
1720 if (hi->passwd[0] != '$')
1721 cryptpass(passwd, hi->passwd);
1722 if (!hi->masks->used) {
1724 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1725 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1726 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1728 argv[pw_arg] = "****";
1729 reply("NSMSG_AUTH_SUCCESS");
1733 static allowauth_func_t *allowauth_func_list;
1734 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1737 reg_allowauth_func(allowauth_func_t func)
1739 if (allowauth_func_used == allowauth_func_size) {
1740 if (allowauth_func_size) {
1741 allowauth_func_size <<= 1;
1742 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1744 allowauth_func_size = 8;
1745 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1748 allowauth_func_list[allowauth_func_used++] = func;
1751 static NICKSERV_FUNC(cmd_allowauth)
1753 struct userNode *target;
1754 struct handle_info *hi;
1757 NICKSERV_MIN_PARMS(2);
1758 if (!(target = GetUserH(argv[1]))) {
1759 reply("MSG_NICK_UNKNOWN", argv[1]);
1762 if (target->handle_info) {
1763 reply("NSMSG_USER_PREV_AUTH", target->nick);
1766 if (IsStamped(target)) {
1767 /* Unauthenticated users might still have been stamped
1768 previously and could therefore have a hidden host;
1769 do not allow them to authenticate to an account. */
1770 reply("NSMSG_USER_PREV_STAMP", target->nick);
1775 else if (!(hi = get_handle_info(argv[2]))) {
1776 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1780 if (hi->opserv_level > user->handle_info->opserv_level) {
1781 reply("MSG_USER_OUTRANKED", hi->handle);
1784 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1785 || (hi->opserv_level > 0))
1786 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1787 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1790 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1791 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1792 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1793 if (nickserv_conf.email_enabled)
1794 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1796 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1797 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1799 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1801 for (n=0; n<allowauth_func_used; n++)
1802 allowauth_func_list[n](user, target, hi);
1806 static NICKSERV_FUNC(cmd_authcookie)
1808 struct handle_info *hi;
1810 NICKSERV_MIN_PARMS(2);
1811 if (user->handle_info) {
1812 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1815 if (IsStamped(user)) {
1816 /* Unauthenticated users might still have been stamped
1817 previously and could therefore have a hidden host;
1818 do not allow them to authenticate to an account. */
1819 reply("NSMSG_STAMPED_AUTHCOOKIE");
1822 if (!(hi = get_handle_info(argv[1]))) {
1823 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1826 if (!hi->email_addr) {
1827 reply("MSG_SET_EMAIL_ADDR");
1830 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1834 static NICKSERV_FUNC(cmd_delcookie)
1836 struct handle_info *hi;
1838 hi = user->handle_info;
1840 reply("NSMSG_NO_COOKIE");
1843 switch (hi->cookie->type) {
1846 reply("NSMSG_MUST_TIME_OUT");
1849 nickserv_eat_cookie(hi->cookie);
1850 reply("NSMSG_ATE_COOKIE");
1856 static NICKSERV_FUNC(cmd_resetpass)
1858 struct handle_info *hi;
1859 char crypted[MD5_CRYPT_LENGTH];
1861 NICKSERV_MIN_PARMS(3);
1862 if (user->handle_info) {
1863 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1866 if (IsStamped(user)) {
1867 /* Unauthenticated users might still have been stamped
1868 previously and could therefore have a hidden host;
1869 do not allow them to activate an account. */
1870 reply("NSMSG_STAMPED_RESETPASS");
1873 if (!(hi = get_handle_info(argv[1]))) {
1874 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1877 if (!hi->email_addr) {
1878 reply("MSG_SET_EMAIL_ADDR");
1881 cryptpass(argv[2], crypted);
1883 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1887 static NICKSERV_FUNC(cmd_cookie)
1889 struct handle_info *hi;
1892 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1895 NICKSERV_MIN_PARMS(3);
1896 if (!(hi = get_handle_info(argv[1]))) {
1897 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1903 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1904 reply("NSMSG_HANDLE_SUSPENDED");
1909 reply("NSMSG_NO_COOKIE");
1913 /* Check validity of operation before comparing cookie to
1914 * prohibit guessing by authed users. */
1915 if (user->handle_info
1916 && (hi->cookie->type != EMAIL_CHANGE)
1917 && (hi->cookie->type != PASSWORD_CHANGE)) {
1918 reply("NSMSG_CANNOT_COOKIE");
1922 if (strcmp(cookie, hi->cookie->cookie)) {
1923 reply("NSMSG_BAD_COOKIE");
1927 switch (hi->cookie->type) {
1929 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1930 set_user_handle_info(user, hi, 1);
1931 reply("NSMSG_HANDLE_ACTIVATED");
1933 case PASSWORD_CHANGE:
1934 set_user_handle_info(user, hi, 1);
1935 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1936 reply("NSMSG_PASSWORD_CHANGED");
1939 nickserv_set_email_addr(hi, hi->cookie->data);
1940 reply("NSMSG_EMAIL_CHANGED");
1943 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1944 set_user_handle_info(user, hi, 1);
1945 nickserv_addmask(user, hi, mask);
1946 reply("NSMSG_AUTH_SUCCESS");
1951 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1952 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1956 nickserv_eat_cookie(hi->cookie);
1961 static NICKSERV_FUNC(cmd_oregnick) {
1963 struct handle_info *target;
1964 struct nick_info *ni;
1966 NICKSERV_MIN_PARMS(3);
1967 if (!(target = modcmd_get_handle_info(user, argv[1])))
1970 if (!is_registerable_nick(nick)) {
1971 reply("NSMSG_BAD_NICK", nick);
1974 ni = dict_find(nickserv_nick_dict, nick, NULL);
1976 reply("NSMSG_NICK_EXISTS", nick);
1979 register_nick(nick, target);
1980 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1984 static NICKSERV_FUNC(cmd_regnick) {
1986 struct nick_info *ni;
1988 if (!is_registerable_nick(user->nick)) {
1989 reply("NSMSG_BAD_NICK", user->nick);
1992 /* count their nicks, see if it's too many */
1993 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1994 if (n >= nickserv_conf.nicks_per_handle) {
1995 reply("NSMSG_TOO_MANY_NICKS");
1998 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2000 reply("NSMSG_NICK_EXISTS", user->nick);
2003 register_nick(user->nick, user->handle_info);
2004 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2008 static NICKSERV_FUNC(cmd_pass)
2010 struct handle_info *hi;
2011 const char *old_pass, *new_pass;
2013 NICKSERV_MIN_PARMS(3);
2014 hi = user->handle_info;
2018 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2019 if (!checkpass(old_pass, hi->passwd)) {
2020 argv[1] = "BADPASS";
2021 reply("NSMSG_PASSWORD_INVALID");
2024 cryptpass(new_pass, hi->passwd);
2026 reply("NSMSG_PASS_SUCCESS");
2031 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2034 char *new_mask = canonicalize_hostmask(strdup(mask));
2035 for (i=0; i<hi->masks->used; i++) {
2036 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2037 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2042 string_list_append(hi->masks, new_mask);
2043 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2047 static NICKSERV_FUNC(cmd_addmask)
2050 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2051 int res = nickserv_addmask(user, user->handle_info, mask);
2055 if (!is_gline(argv[1])) {
2056 reply("NSMSG_MASK_INVALID", argv[1]);
2059 return nickserv_addmask(user, user->handle_info, argv[1]);
2063 static NICKSERV_FUNC(cmd_oaddmask)
2065 struct handle_info *hi;
2067 NICKSERV_MIN_PARMS(3);
2068 if (!(hi = get_victim_oper(user, argv[1])))
2070 return nickserv_addmask(user, hi, argv[2]);
2074 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2077 for (i=0; i<hi->masks->used; i++) {
2078 if (!strcmp(del_mask, hi->masks->list[i])) {
2079 char *old_mask = hi->masks->list[i];
2080 if (hi->masks->used == 1 && !force) {
2081 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2084 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2085 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2090 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2094 static NICKSERV_FUNC(cmd_delmask)
2096 NICKSERV_MIN_PARMS(2);
2097 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2100 static NICKSERV_FUNC(cmd_odelmask)
2102 struct handle_info *hi;
2103 NICKSERV_MIN_PARMS(3);
2104 if (!(hi = get_victim_oper(user, argv[1])))
2106 return nickserv_delmask(user, hi, argv[2], 1);
2110 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2111 unsigned int nn, add = 1, pos;
2112 unsigned long added, removed, flag;
2114 for (added=removed=nn=0; str[nn]; nn++) {
2116 case '+': add = 1; break;
2117 case '-': add = 0; break;
2119 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2120 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2123 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2124 /* cheesy avoidance of looking up the flag name.. */
2125 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2128 flag = 1 << (pos - 1);
2130 added |= flag, removed &= ~flag;
2132 removed |= flag, added &= ~flag;
2137 *premoved = removed;
2142 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2144 unsigned long before, after, added, removed;
2145 struct userNode *uNode;
2147 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2148 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2150 hi->flags = (hi->flags | added) & ~removed;
2151 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2153 /* Strip helping flag if they're only a support helper and not
2154 * currently in #support. */
2155 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2156 struct channelList *schannels;
2158 schannels = chanserv_support_channels();
2159 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2160 for (ii = 0; ii < schannels->used; ++ii)
2161 if (GetUserMode(schannels->list[ii], uNode))
2163 if (ii < schannels->used)
2167 HANDLE_CLEAR_FLAG(hi, HELPING);
2170 if (after && !before) {
2171 /* Add user to current helper list. */
2172 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2173 userList_append(&curr_helpers, uNode);
2174 } else if (!after && before) {
2175 /* Remove user from current helper list. */
2176 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2177 userList_remove(&curr_helpers, uNode);
2184 set_list(struct userNode *user, struct handle_info *hi, int override)
2188 char *set_display[] = {
2189 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2190 "EMAIL", "MAXLOGINS", "LANGUAGE"
2193 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2195 /* Do this so options are presented in a consistent order. */
2196 for (i = 0; i < ArrayLength(set_display); ++i)
2197 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2198 opt(user, hi, override, 0, NULL);
2201 static NICKSERV_FUNC(cmd_set)
2203 struct handle_info *hi;
2206 hi = user->handle_info;
2208 set_list(user, hi, 0);
2211 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2212 reply("NSMSG_INVALID_OPTION", argv[1]);
2215 return opt(user, hi, 0, argc-1, argv+1);
2218 static NICKSERV_FUNC(cmd_oset)
2220 struct handle_info *hi;
2221 struct svccmd *subcmd;
2223 char cmdname[MAXLEN];
2225 NICKSERV_MIN_PARMS(2);
2227 if (!(hi = get_victim_oper(user, argv[1])))
2231 set_list(user, hi, 0);
2235 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2236 reply("NSMSG_INVALID_OPTION", argv[2]);
2240 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2241 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2242 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2245 return opt(user, hi, 1, argc-2, argv+2);
2248 static OPTION_FUNC(opt_info)
2252 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2254 hi->infoline = NULL;
2256 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2260 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2261 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2265 static OPTION_FUNC(opt_width)
2268 hi->screen_width = strtoul(argv[1], NULL, 0);
2270 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2271 hi->screen_width = MIN_LINE_SIZE;
2272 else if (hi->screen_width > MAX_LINE_SIZE)
2273 hi->screen_width = MAX_LINE_SIZE;
2275 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2279 static OPTION_FUNC(opt_tablewidth)
2282 hi->table_width = strtoul(argv[1], NULL, 0);
2284 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2285 hi->table_width = MIN_LINE_SIZE;
2286 else if (hi->screen_width > MAX_LINE_SIZE)
2287 hi->table_width = MAX_LINE_SIZE;
2289 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2293 static OPTION_FUNC(opt_color)
2296 if (enabled_string(argv[1]))
2297 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2298 else if (disabled_string(argv[1]))
2299 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2301 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2306 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2310 static OPTION_FUNC(opt_privmsg)
2313 if (enabled_string(argv[1]))
2314 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2315 else if (disabled_string(argv[1]))
2316 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2318 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2323 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2327 static OPTION_FUNC(opt_style)
2332 if (!irccasecmp(argv[1], "Zoot"))
2333 hi->userlist_style = HI_STYLE_ZOOT;
2334 else if (!irccasecmp(argv[1], "def"))
2335 hi->userlist_style = HI_STYLE_DEF;
2338 switch (hi->userlist_style) {
2347 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2351 static OPTION_FUNC(opt_password)
2354 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2359 cryptpass(argv[1], hi->passwd);
2361 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2365 static OPTION_FUNC(opt_flags)
2368 unsigned int ii, flen;
2371 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2376 nickserv_apply_flags(user, hi, argv[1]);
2378 for (ii = flen = 0; handle_flags[ii]; ii++)
2379 if (hi->flags & (1 << ii))
2380 flags[flen++] = handle_flags[ii];
2383 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2385 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2389 static OPTION_FUNC(opt_email)
2393 if (!is_valid_email_addr(argv[1])) {
2394 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2397 if ((str = mail_prohibited_address(argv[1]))) {
2398 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2401 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2402 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2404 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2406 nickserv_set_email_addr(hi, argv[1]);
2408 nickserv_eat_cookie(hi->cookie);
2409 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2412 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2416 static OPTION_FUNC(opt_maxlogins)
2418 unsigned char maxlogins;
2420 maxlogins = strtoul(argv[1], NULL, 0);
2421 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2422 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2425 hi->maxlogins = maxlogins;
2427 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2428 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2432 static OPTION_FUNC(opt_language)
2434 struct language *lang;
2436 lang = language_find(argv[1]);
2437 if (irccasecmp(lang->name, argv[1]))
2438 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2439 hi->language = lang;
2441 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2445 static OPTION_FUNC(opt_karma)
2448 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2453 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2454 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2455 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2456 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2458 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2462 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2467 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2468 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2470 if ((user->handle_info->opserv_level < target->opserv_level)
2471 || ((user->handle_info->opserv_level == target->opserv_level)
2472 && (user->handle_info->opserv_level < 1000))) {
2473 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2476 if ((user->handle_info->opserv_level < new_level)
2477 || ((user->handle_info->opserv_level == new_level)
2478 && (user->handle_info->opserv_level < 1000))) {
2479 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2482 if (user->handle_info == target) {
2483 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2486 if (target->opserv_level == new_level)
2488 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2489 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2490 target->opserv_level = new_level;
2494 static OPTION_FUNC(opt_level)
2499 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2503 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2504 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2508 static OPTION_FUNC(opt_epithet)
2511 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2515 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2516 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2519 if ((epithet[0] == '*') && !epithet[1])
2522 hi->epithet = strdup(epithet);
2526 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2528 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2532 static OPTION_FUNC(opt_title)
2537 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2541 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2543 if (strchr(title, '.')) {
2544 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2547 if ((strlen(user->handle_info->handle) + strlen(title) +
2548 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2549 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2554 if (!strcmp(title, "*")) {
2555 hi->fakehost = NULL;
2557 hi->fakehost = malloc(strlen(title)+2);
2558 hi->fakehost[0] = '.';
2559 strcpy(hi->fakehost+1, title);
2562 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2563 title = hi->fakehost + 1;
2567 title = user_find_message(user, "MSG_NONE");
2568 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2572 static OPTION_FUNC(opt_fakehost)
2577 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2581 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2583 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2584 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2588 if (!strcmp(fake, "*"))
2589 hi->fakehost = NULL;
2591 hi->fakehost = strdup(fake);
2592 fake = hi->fakehost;
2595 fake = generate_fakehost(hi);
2597 fake = user_find_message(user, "MSG_NONE");
2598 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2602 static NICKSERV_FUNC(cmd_reclaim)
2604 struct handle_info *hi;
2605 struct nick_info *ni;
2606 struct userNode *victim;
2608 NICKSERV_MIN_PARMS(2);
2609 hi = user->handle_info;
2610 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2612 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2615 if (ni->owner != user->handle_info) {
2616 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2619 victim = GetUserH(ni->nick);
2621 reply("MSG_NICK_UNKNOWN", ni->nick);
2624 if (victim == user) {
2625 reply("NSMSG_NICK_USER_YOU");
2628 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2629 switch (nickserv_conf.reclaim_action) {
2630 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2631 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2632 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2633 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2638 static NICKSERV_FUNC(cmd_unregnick)
2641 struct handle_info *hi;
2642 struct nick_info *ni;
2644 hi = user->handle_info;
2645 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2646 ni = dict_find(nickserv_nick_dict, nick, NULL);
2648 reply("NSMSG_UNKNOWN_NICK", nick);
2651 if (hi != ni->owner) {
2652 reply("NSMSG_NOT_YOUR_NICK", nick);
2655 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2660 static NICKSERV_FUNC(cmd_ounregnick)
2662 struct nick_info *ni;
2664 NICKSERV_MIN_PARMS(2);
2665 if (!(ni = get_nick_info(argv[1]))) {
2666 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2669 if (!oper_outranks(user, ni->owner))
2671 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2676 static NICKSERV_FUNC(cmd_unregister)
2678 struct handle_info *hi;
2681 NICKSERV_MIN_PARMS(2);
2682 hi = user->handle_info;
2685 if (checkpass(passwd, hi->passwd)) {
2686 nickserv_unregister_handle(hi, user);
2689 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2690 reply("NSMSG_PASSWORD_INVALID");
2695 static NICKSERV_FUNC(cmd_ounregister)
2697 struct handle_info *hi;
2698 char reason[MAXLEN];
2701 NICKSERV_MIN_PARMS(2);
2702 if (!(hi = get_victim_oper(user, argv[1])))
2705 if (HANDLE_FLAGGED(hi, NODELETE)) {
2706 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
2710 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
2712 ((hi->flags & nickserv_conf.ounregister_flags)
2714 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
2715 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
2719 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2720 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2721 nickserv_unregister_handle(hi, user);
2725 static NICKSERV_FUNC(cmd_status)
2727 if (nickserv_conf.disable_nicks) {
2728 reply("NSMSG_GLOBAL_STATS_NONICK",
2729 dict_size(nickserv_handle_dict));
2731 if (user->handle_info) {
2733 struct nick_info *ni;
2734 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2735 reply("NSMSG_HANDLE_STATS", cnt);
2737 reply("NSMSG_HANDLE_NONE");
2739 reply("NSMSG_GLOBAL_STATS",
2740 dict_size(nickserv_handle_dict),
2741 dict_size(nickserv_nick_dict));
2746 static NICKSERV_FUNC(cmd_ghost)
2748 struct userNode *target;
2749 char reason[MAXLEN];
2751 NICKSERV_MIN_PARMS(2);
2752 if (!(target = GetUserH(argv[1]))) {
2753 reply("MSG_NICK_UNKNOWN", argv[1]);
2756 if (target == user) {
2757 reply("NSMSG_CANNOT_GHOST_SELF");
2760 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2761 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2764 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2765 DelUser(target, nickserv, 1, reason);
2766 reply("NSMSG_GHOST_KILLED", argv[1]);
2770 static NICKSERV_FUNC(cmd_vacation)
2772 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2773 reply("NSMSG_ON_VACATION");
2777 static NICKSERV_FUNC(cmd_addnote)
2779 struct handle_info *hi;
2780 unsigned long duration;
2783 struct handle_note *prev;
2784 struct handle_note *note;
2786 /* Parse parameters and figure out values for note's fields. */
2787 NICKSERV_MIN_PARMS(4);
2788 hi = get_victim_oper(user, argv[1]);
2791 if(!strcmp(argv[2], "0"))
2793 else if(!(duration = ParseInterval(argv[2])))
2795 reply("MSG_INVALID_DURATION", argv[2]);
2798 if (duration > 2*365*86400) {
2799 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2802 unsplit_string(argv + 3, argc - 3, text);
2803 WALK_NOTES(hi, prev, note) {}
2804 id = prev ? (prev->id + 1) : 1;
2806 /* Create the new note structure. */
2807 note = calloc(1, sizeof(*note) + strlen(text));
2809 note->expires = duration ? (now + duration) : 0;
2812 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2813 strcpy(note->note, text);
2818 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2822 static NICKSERV_FUNC(cmd_delnote)
2824 struct handle_info *hi;
2825 struct handle_note *prev;
2826 struct handle_note *note;
2829 NICKSERV_MIN_PARMS(3);
2830 hi = get_victim_oper(user, argv[1]);
2833 id = strtoul(argv[2], NULL, 10);
2834 WALK_NOTES(hi, prev, note) {
2835 if (id == note->id) {
2837 prev->next = note->next;
2839 hi->notes = note->next;
2841 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2845 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2850 nickserv_saxdb_write(struct saxdb_context *ctx) {
2852 struct handle_info *hi;
2855 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2857 #ifdef WITH_PROTOCOL_BAHAMUT
2860 saxdb_start_record(ctx, iter_key(it), 0);
2862 struct handle_cookie *cookie = hi->cookie;
2865 switch (cookie->type) {
2866 case ACTIVATION: type = KEY_ACTIVATION; break;
2867 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2868 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2869 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2870 default: type = NULL; break;
2873 saxdb_start_record(ctx, KEY_COOKIE, 0);
2874 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2875 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2877 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2878 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2879 saxdb_end_record(ctx);
2883 struct handle_note *prev, *note;
2884 saxdb_start_record(ctx, KEY_NOTES, 0);
2885 WALK_NOTES(hi, prev, note) {
2886 snprintf(flags, sizeof(flags), "%d", note->id);
2887 saxdb_start_record(ctx, flags, 0);
2889 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
2890 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
2891 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
2892 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
2893 saxdb_end_record(ctx);
2895 saxdb_end_record(ctx);
2898 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2900 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2902 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2906 for (ii=flen=0; handle_flags[ii]; ++ii)
2907 if (hi->flags & (1 << ii))
2908 flags[flen++] = handle_flags[ii];
2910 saxdb_write_string(ctx, KEY_FLAGS, flags);
2912 #ifdef WITH_PROTOCOL_BAHAMUT
2913 saxdb_write_int(ctx, KEY_ID, hi->id);
2916 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2917 if (hi->last_quit_host[0])
2918 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2919 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2921 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
2922 if (hi->masks->used)
2923 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2925 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2927 struct string_list *slist;
2928 struct nick_info *ni;
2930 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2931 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2932 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2936 if (hi->opserv_level)
2937 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2938 if (hi->language != lang_C)
2939 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2940 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2941 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2942 if (hi->screen_width)
2943 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2944 if (hi->table_width)
2945 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2946 flags[0] = hi->userlist_style;
2948 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2949 saxdb_end_record(ctx);
2954 static handle_merge_func_t *handle_merge_func_list;
2955 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2958 reg_handle_merge_func(handle_merge_func_t func)
2960 if (handle_merge_func_used == handle_merge_func_size) {
2961 if (handle_merge_func_size) {
2962 handle_merge_func_size <<= 1;
2963 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2965 handle_merge_func_size = 8;
2966 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2969 handle_merge_func_list[handle_merge_func_used++] = func;
2972 static NICKSERV_FUNC(cmd_merge)
2974 struct handle_info *hi_from, *hi_to;
2975 struct userNode *last_user;
2976 struct userData *cList, *cListNext;
2977 unsigned int ii, jj, n;
2978 char buffer[MAXLEN];
2980 NICKSERV_MIN_PARMS(3);
2982 if (!(hi_from = get_victim_oper(user, argv[1])))
2984 if (!(hi_to = get_victim_oper(user, argv[2])))
2986 if (hi_to == hi_from) {
2987 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2991 for (n=0; n<handle_merge_func_used; n++)
2992 handle_merge_func_list[n](user, hi_to, hi_from);
2994 /* Append "from" handle's nicks to "to" handle's nick list. */
2996 struct nick_info *last_ni;
2997 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2998 last_ni->next = hi_from->nicks;
3000 while (hi_from->nicks) {
3001 hi_from->nicks->owner = hi_to;
3002 hi_from->nicks = hi_from->nicks->next;
3005 /* Merge the hostmasks. */
3006 for (ii=0; ii<hi_from->masks->used; ii++) {
3007 char *mask = hi_from->masks->list[ii];
3008 for (jj=0; jj<hi_to->masks->used; jj++)
3009 if (match_ircglobs(hi_to->masks->list[jj], mask))
3011 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3012 string_list_append(hi_to->masks, strdup(mask));
3015 /* Merge the lists of authed users. */
3017 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3018 last_user->next_authed = hi_from->users;
3020 hi_to->users = hi_from->users;
3022 /* Repoint the old "from" handle's users. */
3023 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3024 last_user->handle_info = hi_to;
3026 hi_from->users = NULL;
3028 /* Merge channel userlists. */
3029 for (cList=hi_from->channels; cList; cList=cListNext) {
3030 struct userData *cList2;
3031 cListNext = cList->u_next;
3032 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3033 if (cList->channel == cList2->channel)
3035 if (cList2 && (cList2->access >= cList->access)) {
3036 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);
3037 /* keep cList2 in hi_to; remove cList from hi_from */
3038 del_channel_user(cList, 1);
3041 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);
3042 /* remove the lower-ranking cList2 from hi_to */
3043 del_channel_user(cList2, 1);
3045 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3047 /* cList needs to be moved from hi_from to hi_to */
3048 cList->handle = hi_to;
3049 /* Remove from linked list for hi_from */
3050 assert(!cList->u_prev);
3051 hi_from->channels = cList->u_next;
3053 cList->u_next->u_prev = cList->u_prev;
3054 /* Add to linked list for hi_to */
3055 cList->u_prev = NULL;
3056 cList->u_next = hi_to->channels;
3057 if (hi_to->channels)
3058 hi_to->channels->u_prev = cList;
3059 hi_to->channels = cList;
3063 /* Do they get an OpServ level promotion? */
3064 if (hi_from->opserv_level > hi_to->opserv_level)
3065 hi_to->opserv_level = hi_from->opserv_level;
3067 /* What about last seen time? */
3068 if (hi_from->lastseen > hi_to->lastseen)
3069 hi_to->lastseen = hi_from->lastseen;
3071 /* New karma is the sum of the two original karmas. */
3072 hi_to->karma += hi_from->karma;
3074 /* Does a fakehost carry over? (This intentionally doesn't set it
3075 * for users previously attached to hi_to. They'll just have to
3078 if (hi_from->fakehost && !hi_to->fakehost)
3079 hi_to->fakehost = strdup(hi_from->fakehost);
3081 /* Notify of success. */
3082 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3083 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3084 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3086 /* Unregister the "from" handle. */
3087 nickserv_unregister_handle(hi_from, NULL);
3092 struct nickserv_discrim {
3093 unsigned long flags_on, flags_off;
3094 unsigned long min_registered, max_registered;
3095 unsigned long lastseen;
3097 int min_level, max_level;
3098 int min_karma, max_karma;
3099 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3100 const char *nickmask;
3101 const char *hostmask;
3102 const char *fakehostmask;
3103 const char *handlemask;
3104 const char *emailmask;
3107 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3109 struct discrim_apply_info {
3110 struct nickserv_discrim *discrim;
3111 discrim_search_func func;
3112 struct userNode *source;
3113 unsigned int matched;
3116 static struct nickserv_discrim *
3117 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3120 struct nickserv_discrim *discrim;
3122 discrim = malloc(sizeof(*discrim));
3123 memset(discrim, 0, sizeof(*discrim));
3124 discrim->min_level = 0;
3125 discrim->max_level = INT_MAX;
3126 discrim->limit = 50;
3127 discrim->min_registered = 0;
3128 discrim->max_registered = ULONG_MAX;
3129 discrim->lastseen = ULONG_MAX;
3130 discrim->min_karma = INT_MIN;
3131 discrim->max_karma = INT_MAX;
3133 for (i=0; i<argc; i++) {
3134 if (i == argc - 1) {
3135 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3138 if (!irccasecmp(argv[i], "limit")) {
3139 discrim->limit = strtoul(argv[++i], NULL, 0);
3140 } else if (!irccasecmp(argv[i], "flags")) {
3141 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3142 } else if (!irccasecmp(argv[i], "registered")) {
3143 const char *cmp = argv[++i];
3144 if (cmp[0] == '<') {
3145 if (cmp[1] == '=') {
3146 discrim->min_registered = now - ParseInterval(cmp+2);
3148 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3150 } else if (cmp[0] == '=') {
3151 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3152 } else if (cmp[0] == '>') {
3153 if (cmp[1] == '=') {
3154 discrim->max_registered = now - ParseInterval(cmp+2);
3156 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3159 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3161 } else if (!irccasecmp(argv[i], "seen")) {
3162 discrim->lastseen = now - ParseInterval(argv[++i]);
3163 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3164 discrim->nickmask = argv[++i];
3165 } else if (!irccasecmp(argv[i], "hostmask")) {
3167 if (!irccasecmp(argv[i], "exact")) {
3168 if (i == argc - 1) {
3169 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3172 discrim->hostmask_type = EXACT;
3173 } else if (!irccasecmp(argv[i], "subset")) {
3174 if (i == argc - 1) {
3175 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3178 discrim->hostmask_type = SUBSET;
3179 } else if (!irccasecmp(argv[i], "superset")) {
3180 if (i == argc - 1) {
3181 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3184 discrim->hostmask_type = SUPERSET;
3185 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3186 if (i == argc - 1) {
3187 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3190 discrim->hostmask_type = LASTQUIT;
3193 discrim->hostmask_type = SUPERSET;
3195 discrim->hostmask = argv[++i];
3196 } else if (!irccasecmp(argv[i], "fakehost")) {
3197 if (!irccasecmp(argv[++i], "*")) {
3198 discrim->fakehostmask = 0;
3200 discrim->fakehostmask = argv[i];
3202 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3203 if (!irccasecmp(argv[++i], "*")) {
3204 discrim->handlemask = 0;
3206 discrim->handlemask = argv[i];
3208 } else if (!irccasecmp(argv[i], "email")) {
3209 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3210 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3212 } else if (!irccasecmp(argv[++i], "*")) {
3213 discrim->emailmask = 0;
3215 discrim->emailmask = argv[i];
3217 } else if (!irccasecmp(argv[i], "access")) {
3218 const char *cmp = argv[++i];
3219 if (cmp[0] == '<') {
3220 if (discrim->min_level == 0) discrim->min_level = 1;
3221 if (cmp[1] == '=') {
3222 discrim->max_level = strtoul(cmp+2, NULL, 0);
3224 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3226 } else if (cmp[0] == '=') {
3227 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3228 } else if (cmp[0] == '>') {
3229 if (cmp[1] == '=') {
3230 discrim->min_level = strtoul(cmp+2, NULL, 0);
3232 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3235 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3237 } else if (!irccasecmp(argv[i], "karma")) {
3238 const char *cmp = argv[++i];
3239 if (cmp[0] == '<') {
3240 if (cmp[1] == '=') {
3241 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3243 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3245 } else if (cmp[0] == '=') {
3246 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3247 } else if (cmp[0] == '>') {
3248 if (cmp[1] == '=') {
3249 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3251 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3254 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3257 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3268 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3270 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3271 || (discrim->flags_off & hi->flags)
3272 || (discrim->min_registered > hi->registered)
3273 || (discrim->max_registered < hi->registered)
3274 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3275 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3276 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3277 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3278 || (discrim->min_level > hi->opserv_level)
3279 || (discrim->max_level < hi->opserv_level)
3280 || (discrim->min_karma > hi->karma)
3281 || (discrim->max_karma < hi->karma)
3285 if (discrim->hostmask) {
3287 for (i=0; i<hi->masks->used; i++) {
3288 const char *mask = hi->masks->list[i];
3289 if ((discrim->hostmask_type == SUBSET)
3290 && (match_ircglobs(discrim->hostmask, mask))) break;
3291 else if ((discrim->hostmask_type == EXACT)
3292 && !irccasecmp(discrim->hostmask, mask)) break;
3293 else if ((discrim->hostmask_type == SUPERSET)
3294 && (match_ircglobs(mask, discrim->hostmask))) break;
3295 else if ((discrim->hostmask_type == LASTQUIT)
3296 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3298 if (i==hi->masks->used) return 0;
3300 if (discrim->nickmask) {
3301 struct nick_info *nick = hi->nicks;
3303 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3306 if (!nick) return 0;
3312 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3314 dict_iterator_t it, next;
3315 unsigned int matched;
3317 for (it = dict_first(nickserv_handle_dict), matched = 0;
3318 it && (matched < discrim->limit);
3320 next = iter_next(it);
3321 if (nickserv_discrim_match(discrim, iter_data(it))) {
3322 dsf(source, iter_data(it));
3330 search_print_func(struct userNode *source, struct handle_info *match)
3332 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3336 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3341 search_unregister_func (struct userNode *source, struct handle_info *match)
3343 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3344 nickserv_unregister_handle(match, source);
3348 nickserv_sort_accounts_by_access(const void *a, const void *b)
3350 const struct handle_info *hi_a = *(const struct handle_info**)a;
3351 const struct handle_info *hi_b = *(const struct handle_info**)b;
3352 if (hi_a->opserv_level != hi_b->opserv_level)
3353 return hi_b->opserv_level - hi_a->opserv_level;
3354 return irccasecmp(hi_a->handle, hi_b->handle);
3358 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3360 struct handle_info_list hil;
3361 struct helpfile_table tbl;
3366 memset(&hil, 0, sizeof(hil));
3367 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3368 struct handle_info *hi = iter_data(it);
3369 if (hi->opserv_level)
3370 handle_info_list_append(&hil, hi);
3372 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3373 tbl.length = hil.used + 1;
3375 tbl.flags = TABLE_NO_FREE;
3376 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3377 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3380 for (ii = 0; ii < hil.used; ) {
3381 ary = malloc(tbl.width * sizeof(ary[0]));
3382 ary[0] = hil.list[ii]->handle;
3383 ary[1] = strtab(hil.list[ii]->opserv_level);
3384 tbl.contents[++ii] = ary;
3386 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3387 reply("MSG_MATCH_COUNT", hil.used);
3388 for (ii = 0; ii < hil.used; ii++)
3389 free(tbl.contents[ii]);
3394 static NICKSERV_FUNC(cmd_search)
3396 struct nickserv_discrim *discrim;
3397 discrim_search_func action;
3398 struct svccmd *subcmd;
3399 unsigned int matches;
3402 NICKSERV_MIN_PARMS(3);
3403 sprintf(buf, "search %s", argv[1]);
3404 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3405 if (!irccasecmp(argv[1], "print"))
3406 action = search_print_func;
3407 else if (!irccasecmp(argv[1], "count"))
3408 action = search_count_func;
3409 else if (!irccasecmp(argv[1], "unregister"))
3410 action = search_unregister_func;
3412 reply("NSMSG_INVALID_ACTION", argv[1]);
3416 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3419 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3423 if (action == search_print_func)
3424 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3425 else if (action == search_count_func)
3426 discrim->limit = INT_MAX;
3428 matches = nickserv_discrim_search(discrim, action, user);
3431 reply("MSG_MATCH_COUNT", matches);
3433 reply("MSG_NO_MATCHES");
3439 static MODCMD_FUNC(cmd_checkpass)
3441 struct handle_info *hi;
3443 NICKSERV_MIN_PARMS(3);
3444 if (!(hi = get_handle_info(argv[1]))) {
3445 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3448 if (checkpass(argv[2], hi->passwd))
3449 reply("CHECKPASS_YES");
3451 reply("CHECKPASS_NO");
3457 nickserv_db_read_handle(const char *handle, dict_t obj)
3460 struct string_list *masks, *slist;
3461 struct handle_info *hi;
3462 struct userNode *authed_users;
3463 struct userData *channels;
3464 unsigned long int id;
3468 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3469 id = str ? strtoul(str, NULL, 0) : 0;
3470 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3472 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3475 if ((hi = get_handle_info(handle))) {
3476 authed_users = hi->users;
3477 channels = hi->channels;
3479 hi->channels = NULL;
3480 dict_remove(nickserv_handle_dict, hi->handle);
3482 authed_users = NULL;
3485 hi = register_handle(handle, str, id);
3487 hi->users = authed_users;
3488 while (authed_users) {
3489 authed_users->handle_info = hi;
3490 authed_users = authed_users->next_authed;
3493 hi->channels = channels;
3494 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3495 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3496 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3497 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3498 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3499 hi->language = language_find(str ? str : "C");
3500 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3501 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3502 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3504 hi->infoline = strdup(str);
3505 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3506 hi->registered = str ? strtoul(str, NULL, 0) : now;
3507 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3508 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3509 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3510 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3511 /* We want to read the nicks even if disable_nicks is set. This is so
3512 * that we don't lose the nick data entirely. */
3513 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3515 for (ii=0; ii<slist->used; ii++)
3516 register_nick(slist->list[ii], hi);
3518 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3520 for (ii=0; str[ii]; ii++)
3521 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3523 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3524 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3525 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3526 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3527 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3528 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3529 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3531 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3533 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3534 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3536 nickserv_set_email_addr(hi, str);
3537 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3539 hi->epithet = strdup(str);
3540 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3542 hi->fakehost = strdup(str);
3543 /* Read the "cookie" sub-database (if it exists). */
3544 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3546 const char *data, *type, *expires, *cookie_str;
3547 struct handle_cookie *cookie;
3549 cookie = calloc(1, sizeof(*cookie));
3550 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3551 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3552 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3553 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3554 if (!type || !expires || !cookie_str) {
3555 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3558 if (!irccasecmp(type, KEY_ACTIVATION))
3559 cookie->type = ACTIVATION;
3560 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3561 cookie->type = PASSWORD_CHANGE;
3562 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3563 cookie->type = EMAIL_CHANGE;
3564 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3565 cookie->type = ALLOWAUTH;
3567 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3570 cookie->expires = strtoul(expires, NULL, 0);
3571 if (cookie->expires < now)
3574 cookie->data = strdup(data);
3575 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3579 nickserv_bake_cookie(cookie);
3581 nickserv_free_cookie(cookie);
3583 /* Read the "notes" sub-database (if it exists). */
3584 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3587 struct handle_note *last_note;
3588 struct handle_note *note;
3591 for (it = dict_first(subdb); it; it = iter_next(it)) {
3592 const char *expires;
3600 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3602 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", id, hi->handle);
3605 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3606 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3607 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3608 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3609 if (!setter || !text || !set) {
3610 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", id, hi->handle);
3613 note = calloc(1, sizeof(*note) + strlen(text));
3615 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3616 note->set = strtoul(set, NULL, 10);
3617 note->id = strtoul(id, NULL, 10);
3618 safestrncpy(note->setter, setter, sizeof(note->setter));
3619 strcpy(note->note, text);
3621 last_note->next = note;
3630 nickserv_saxdb_read(dict_t db) {
3632 struct record_data *rd;
3634 for (it=dict_first(db); it; it=iter_next(it)) {
3636 nickserv_db_read_handle(iter_key(it), rd->d.object);
3641 static NICKSERV_FUNC(cmd_mergedb)
3643 struct timeval start, stop;
3646 NICKSERV_MIN_PARMS(2);
3647 gettimeofday(&start, NULL);
3648 if (!(db = parse_database(argv[1]))) {
3649 reply("NSMSG_DB_UNREADABLE", argv[1]);
3652 nickserv_saxdb_read(db);
3654 gettimeofday(&stop, NULL);
3655 stop.tv_sec -= start.tv_sec;
3656 stop.tv_usec -= start.tv_usec;
3657 if (stop.tv_usec < 0) {
3659 stop.tv_usec += 1000000;
3661 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
3666 expire_handles(UNUSED_ARG(void *data))
3668 dict_iterator_t it, next;
3669 unsigned long expiry;
3670 struct handle_info *hi;
3672 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3673 next = iter_next(it);
3675 if ((hi->opserv_level > 0)
3677 || HANDLE_FLAGGED(hi, FROZEN)
3678 || HANDLE_FLAGGED(hi, NODELETE)) {
3681 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3682 if ((now - hi->lastseen) > expiry) {
3683 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3684 nickserv_unregister_handle(hi, NULL);
3688 if (nickserv_conf.handle_expire_frequency)
3689 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3693 nickserv_load_dict(const char *fname)
3697 if (!(file = fopen(fname, "r"))) {
3698 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3701 while (!feof(file)) {
3702 fgets(line, sizeof(line), file);
3705 if (line[strlen(line)-1] == '\n')
3706 line[strlen(line)-1] = 0;
3707 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3710 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3713 static enum reclaim_action
3714 reclaim_action_from_string(const char *str) {
3716 return RECLAIM_NONE;
3717 else if (!irccasecmp(str, "warn"))
3718 return RECLAIM_WARN;
3719 else if (!irccasecmp(str, "svsnick"))
3720 return RECLAIM_SVSNICK;
3721 else if (!irccasecmp(str, "kill"))
3722 return RECLAIM_KILL;
3724 return RECLAIM_NONE;
3728 nickserv_conf_read(void)
3730 dict_t conf_node, child;
3734 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3735 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3738 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3740 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3741 if (nickserv_conf.valid_handle_regex_set)
3742 regfree(&nickserv_conf.valid_handle_regex);
3744 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3745 nickserv_conf.valid_handle_regex_set = !err;
3746 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3748 nickserv_conf.valid_handle_regex_set = 0;
3750 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3751 if (nickserv_conf.valid_nick_regex_set)
3752 regfree(&nickserv_conf.valid_nick_regex);
3754 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3755 nickserv_conf.valid_nick_regex_set = !err;
3756 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3758 nickserv_conf.valid_nick_regex_set = 0;
3760 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3762 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3763 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3764 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3765 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3766 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3767 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3768 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3769 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3770 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3771 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3772 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3773 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3774 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3775 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3776 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3777 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3778 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3779 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3780 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3781 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3782 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3783 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3784 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3785 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3786 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3788 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3789 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3790 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3792 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3793 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3794 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3796 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3797 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3798 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3799 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3800 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3801 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3802 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3803 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3804 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
3805 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
3806 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
3809 nickserv_conf.ounregister_flags = 0;
3811 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
3814 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
3816 if (!nickserv_conf.disable_nicks) {
3817 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3818 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3819 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3820 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3821 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3822 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3823 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3824 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3826 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3827 for (it=dict_first(child); it; it=iter_next(it)) {
3828 const char *key = iter_key(it), *value;
3832 if (!strncasecmp(key, "uc_", 3))
3833 flag = toupper(key[3]);
3834 else if (!strncasecmp(key, "lc_", 3))
3835 flag = tolower(key[3]);
3839 if ((pos = handle_inverse_flags[flag])) {
3840 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3841 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3844 if (nickserv_conf.weak_password_dict)
3845 dict_delete(nickserv_conf.weak_password_dict);
3846 nickserv_conf.weak_password_dict = dict_new();
3847 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3848 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3849 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3850 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3852 nickserv_load_dict(str);
3853 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3854 if (nickserv && str)
3855 NickChange(nickserv, str, 0);
3856 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3857 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3858 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3859 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3860 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3861 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3862 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3863 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3864 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3865 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3866 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3867 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3868 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3869 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3870 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3871 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3872 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3873 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3874 str = conf_get_data("server/network", RECDB_QSTRING);
3875 nickserv_conf.network_name = str ? str : "some IRC network";
3876 if (!nickserv_conf.auth_policer_params) {
3877 nickserv_conf.auth_policer_params = policer_params_new();
3878 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3879 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3881 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3882 for (it=dict_first(child); it; it=iter_next(it))
3883 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3887 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3889 char newnick[NICKLEN+1];
3898 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3900 case RECLAIM_SVSNICK:
3902 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3903 } while (GetUserH(newnick));
3904 irc_svsnick(nickserv, user, newnick);
3907 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3908 DelUser(user, nickserv, 1, msg);
3914 nickserv_reclaim_p(void *data) {
3915 struct userNode *user = data;
3916 struct nick_info *ni = get_nick_info(user->nick);
3918 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3922 check_user_nick(struct userNode *user) {
3923 struct nick_info *ni;
3924 user->modes &= ~FLAGS_REGNICK;
3925 if (!(ni = get_nick_info(user->nick)))
3927 if (user->handle_info == ni->owner) {
3928 user->modes |= FLAGS_REGNICK;
3932 if (nickserv_conf.warn_nick_owned)
3933 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3934 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3936 if (nickserv_conf.auto_reclaim_delay)
3937 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3939 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3944 handle_new_user(struct userNode *user)
3946 return check_user_nick(user);
3950 handle_account(struct userNode *user, const char *stamp)
3952 struct handle_info *hi;
3954 #ifdef WITH_PROTOCOL_P10
3955 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3957 hi = dict_find(nickserv_id_dict, stamp, NULL);
3961 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3964 set_user_handle_info(user, hi, 0);
3966 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3971 handle_nick_change(struct userNode *user, const char *old_nick)
3973 struct handle_info *hi;
3975 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3976 dict_remove(nickserv_allow_auth_dict, old_nick);
3977 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3979 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3980 check_user_nick(user);
3984 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3986 dict_remove(nickserv_allow_auth_dict, user->nick);
3987 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3988 set_user_handle_info(user, NULL, 0);
3991 static struct modcmd *
3992 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3994 if (min_level > 0) {
3996 sprintf(buf, "%u", min_level);
3997 if (must_be_qualified) {
3998 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4000 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4002 } else if (min_level == 0) {
4003 if (must_be_qualified) {
4004 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4006 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4009 if (must_be_qualified) {
4010 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4012 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4018 nickserv_db_cleanup(void)
4020 unreg_del_user_func(nickserv_remove_user);
4021 userList_clean(&curr_helpers);
4022 policer_params_delete(nickserv_conf.auth_policer_params);
4023 dict_delete(nickserv_handle_dict);
4024 dict_delete(nickserv_nick_dict);
4025 dict_delete(nickserv_opt_dict);
4026 dict_delete(nickserv_allow_auth_dict);
4027 dict_delete(nickserv_email_dict);
4028 dict_delete(nickserv_id_dict);
4029 dict_delete(nickserv_conf.weak_password_dict);
4030 free(auth_func_list);
4031 free(unreg_func_list);
4033 free(allowauth_func_list);
4034 free(handle_merge_func_list);
4035 free(failpw_func_list);
4036 if (nickserv_conf.valid_handle_regex_set)
4037 regfree(&nickserv_conf.valid_handle_regex);
4038 if (nickserv_conf.valid_nick_regex_set)
4039 regfree(&nickserv_conf.valid_nick_regex);
4043 init_nickserv(const char *nick)
4046 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4047 reg_new_user_func(handle_new_user);
4048 reg_nick_change_func(handle_nick_change);
4049 reg_del_user_func(nickserv_remove_user);
4050 reg_account_func(handle_account);
4052 /* set up handle_inverse_flags */
4053 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4054 for (i=0; handle_flags[i]; i++) {
4055 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4056 flag_access_levels[i] = 0;
4059 conf_register_reload(nickserv_conf_read);
4060 nickserv_opt_dict = dict_new();
4061 nickserv_email_dict = dict_new();
4062 dict_set_free_keys(nickserv_email_dict, free);
4063 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4065 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4066 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4067 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4068 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4069 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4070 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4071 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4072 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4073 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4074 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4075 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4076 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4077 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4078 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4079 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4080 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4081 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4082 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4083 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4084 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4085 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4086 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4087 if (!nickserv_conf.disable_nicks) {
4088 /* nick management commands */
4089 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4090 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4091 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4092 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4093 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4094 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4096 if (nickserv_conf.email_enabled) {
4097 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4098 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4099 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4100 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4101 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4103 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4104 /* miscellaneous commands */
4105 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4106 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4107 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4108 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4109 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4111 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4112 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4113 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4114 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4115 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4116 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4117 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4118 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4119 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4120 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4121 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4122 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4123 if (nickserv_conf.titlehost_suffix) {
4124 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4125 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4127 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4128 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4129 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4130 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4132 nickserv_handle_dict = dict_new();
4133 dict_set_free_keys(nickserv_handle_dict, free);
4134 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4136 nickserv_id_dict = dict_new();
4137 dict_set_free_keys(nickserv_id_dict, free);
4139 nickserv_nick_dict = dict_new();
4140 dict_set_free_data(nickserv_nick_dict, free);
4142 nickserv_allow_auth_dict = dict_new();
4144 userList_init(&curr_helpers);
4147 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4148 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4149 nickserv_service = service_register(nickserv);
4151 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4152 reg_exit_func(nickserv_db_cleanup);
4153 if(nickserv_conf.handle_expire_frequency)
4154 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4155 message_register_table(msgtab);