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 "FMT_TIME_T".%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;
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;
1318 if (!(hi = user->handle_info)) {
1319 reply("NSMSG_MUST_AUTH");
1322 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1326 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1327 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1328 #ifdef WITH_PROTOCOL_BAHAMUT
1329 reply("NSMSG_HANDLEINFO_ID", hi->id);
1331 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1334 intervalString(buff, now - hi->lastseen, user->handle_info);
1335 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1337 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1340 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1341 if (HANDLE_FLAGGED(hi, FROZEN))
1342 reply("NSMSG_HANDLEINFO_VACATION");
1344 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1345 struct do_not_register *dnr;
1346 if ((dnr = chanserv_is_dnr(NULL, hi)))
1347 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1348 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1350 } else if (hi != user->handle_info)
1354 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1356 if (nickserv_conf.email_enabled)
1357 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1361 switch (hi->cookie->type) {
1362 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1363 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1364 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1365 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1366 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1371 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsSupport(user)) {
1373 reply("NSMSG_HANDLEINFO_NO_NOTES");
1375 struct handle_note *prev, *note;
1377 WALK_NOTES(hi, prev, note) {
1378 char set_time[INTERVALLEN];
1379 intervalString(set_time, now - note->set, user->handle_info);
1380 if (note->expires) {
1381 char exp_time[INTERVALLEN];
1382 intervalString(exp_time, note->expires - now, user->handle_info);
1383 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1385 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1392 unsigned long flen = 1;
1393 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1395 for (i=0, flen=1; handle_flags[i]; i++)
1396 if (hi->flags & 1 << i)
1397 flags[flen++] = handle_flags[i];
1399 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1401 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1404 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1405 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1406 || (hi->opserv_level > 0)) {
1407 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1411 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1413 if (hi->last_quit_host[0])
1414 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1416 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1418 if (nickserv_conf.disable_nicks) {
1419 /* nicks disabled; don't show anything about registered nicks */
1420 } else if (hi->nicks) {
1421 struct nick_info *ni, *next_ni;
1422 for (ni = hi->nicks; ni; ni = next_ni) {
1423 herelen = strlen(ni->nick);
1424 if (pos + herelen + 1 > ArrayLength(buff)) {
1426 goto print_nicks_buff;
1430 memcpy(buff+pos, ni->nick, herelen);
1431 pos += herelen; buff[pos++] = ' ';
1435 reply("NSMSG_HANDLEINFO_NICKS", buff);
1440 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1443 if (hi->masks->used) {
1444 for (i=0; i < hi->masks->used; i++) {
1445 herelen = strlen(hi->masks->list[i]);
1446 if (pos + herelen + 1 > ArrayLength(buff)) {
1448 goto print_mask_buff;
1450 memcpy(buff+pos, hi->masks->list[i], herelen);
1451 pos += herelen; buff[pos++] = ' ';
1452 if (i+1 == hi->masks->used) {
1455 reply("NSMSG_HANDLEINFO_MASKS", buff);
1460 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1464 struct userData *channel, *next;
1467 for (channel = hi->channels; channel; channel = next) {
1468 next = channel->u_next;
1469 name = channel->channel->channel->name;
1470 herelen = strlen(name);
1471 if (pos + herelen + 7 > ArrayLength(buff)) {
1473 goto print_chans_buff;
1475 if (IsUserSuspended(channel))
1477 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1481 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1486 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1489 for (target = hi->users; target; target = next_un) {
1490 herelen = strlen(target->nick);
1491 if (pos + herelen + 1 > ArrayLength(buff)) {
1493 goto print_cnick_buff;
1495 next_un = target->next_authed;
1497 memcpy(buff+pos, target->nick, herelen);
1498 pos += herelen; buff[pos++] = ' ';
1502 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1510 static NICKSERV_FUNC(cmd_userinfo)
1512 struct userNode *target;
1514 NICKSERV_MIN_PARMS(2);
1515 if (!(target = GetUserH(argv[1]))) {
1516 reply("MSG_NICK_UNKNOWN", argv[1]);
1519 if (target->handle_info)
1520 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1522 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1526 static NICKSERV_FUNC(cmd_nickinfo)
1528 struct nick_info *ni;
1530 NICKSERV_MIN_PARMS(2);
1531 if (!(ni = get_nick_info(argv[1]))) {
1532 reply("MSG_NICK_UNKNOWN", argv[1]);
1535 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1539 static NICKSERV_FUNC(cmd_notes)
1541 struct handle_info *hi;
1542 struct handle_note *prev, *note;
1545 NICKSERV_MIN_PARMS(2);
1546 if (!(hi = get_victim_oper(user, argv[1])))
1549 WALK_NOTES(hi, prev, note) {
1550 char set_time[INTERVALLEN];
1551 intervalString(set_time, now - note->set, user->handle_info);
1552 if (note->expires) {
1553 char exp_time[INTERVALLEN];
1554 intervalString(exp_time, note->expires - now, user->handle_info);
1555 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1557 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1561 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1565 static NICKSERV_FUNC(cmd_rename_handle)
1567 struct handle_info *hi;
1568 char msgbuf[MAXLEN], *old_handle;
1571 NICKSERV_MIN_PARMS(3);
1572 if (!(hi = get_victim_oper(user, argv[1])))
1574 if (!is_valid_handle(argv[2])) {
1575 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1578 if (get_handle_info(argv[2])) {
1579 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1583 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1584 hi->handle = strdup(argv[2]);
1585 dict_insert(nickserv_handle_dict, hi->handle, hi);
1586 for (nn=0; nn<rf_list_used; nn++)
1587 rf_list[nn](hi, old_handle);
1588 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1589 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1590 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1595 static failpw_func_t *failpw_func_list;
1596 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1599 reg_failpw_func(failpw_func_t func)
1601 if (failpw_func_used == failpw_func_size) {
1602 if (failpw_func_size) {
1603 failpw_func_size <<= 1;
1604 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1606 failpw_func_size = 8;
1607 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1610 failpw_func_list[failpw_func_used++] = func;
1613 static NICKSERV_FUNC(cmd_auth)
1615 int pw_arg, used, maxlogins;
1616 struct handle_info *hi;
1618 struct userNode *other;
1620 if (user->handle_info) {
1621 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1624 if (IsStamped(user)) {
1625 /* Unauthenticated users might still have been stamped
1626 previously and could therefore have a hidden host;
1627 do not allow them to authenticate. */
1628 reply("NSMSG_STAMPED_AUTH");
1632 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1634 } else if (argc == 2) {
1635 if (nickserv_conf.disable_nicks) {
1636 if (!(hi = get_handle_info(user->nick))) {
1637 reply("NSMSG_HANDLE_NOT_FOUND");
1641 /* try to look up their handle from their nick */
1642 struct nick_info *ni;
1643 ni = get_nick_info(user->nick);
1645 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1652 reply("MSG_MISSING_PARAMS", argv[0]);
1653 svccmd_send_help(user, nickserv, cmd);
1657 reply("NSMSG_HANDLE_NOT_FOUND");
1660 /* Responses from here on look up the language used by the handle they asked about. */
1661 passwd = argv[pw_arg];
1662 if (!valid_user_for(user, hi)) {
1663 if (hi->email_addr && nickserv_conf.email_enabled)
1664 send_message_type(4, user, cmd->parent->bot,
1665 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1668 send_message_type(4, user, cmd->parent->bot,
1669 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1671 argv[pw_arg] = "BADMASK";
1674 if (!checkpass(passwd, hi->passwd)) {
1676 send_message_type(4, user, cmd->parent->bot,
1677 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1678 argv[pw_arg] = "BADPASS";
1679 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1680 if (nickserv_conf.autogag_enabled) {
1681 if (!user->auth_policer.params) {
1682 user->auth_policer.last_req = now;
1683 user->auth_policer.params = nickserv_conf.auth_policer_params;
1685 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1687 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1688 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1689 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1691 argv[pw_arg] = "GAGGED";
1696 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1697 send_message_type(4, user, cmd->parent->bot,
1698 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1699 argv[pw_arg] = "SUSPENDED";
1702 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1703 for (used = 0, other = hi->users; other; other = other->next_authed) {
1704 if (++used >= maxlogins) {
1705 send_message_type(4, user, cmd->parent->bot,
1706 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1708 argv[pw_arg] = "MAXLOGINS";
1713 set_user_handle_info(user, hi, 1);
1714 if (nickserv_conf.email_required && !hi->email_addr)
1715 reply("NSMSG_PLEASE_SET_EMAIL");
1716 if (!is_secure_password(hi->handle, passwd, NULL))
1717 reply("NSMSG_WEAK_PASSWORD");
1718 if (hi->passwd[0] != '$')
1719 cryptpass(passwd, hi->passwd);
1720 if (!hi->masks->used) {
1722 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1723 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1724 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1726 argv[pw_arg] = "****";
1727 reply("NSMSG_AUTH_SUCCESS");
1731 static allowauth_func_t *allowauth_func_list;
1732 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1735 reg_allowauth_func(allowauth_func_t func)
1737 if (allowauth_func_used == allowauth_func_size) {
1738 if (allowauth_func_size) {
1739 allowauth_func_size <<= 1;
1740 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1742 allowauth_func_size = 8;
1743 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1746 allowauth_func_list[allowauth_func_used++] = func;
1749 static NICKSERV_FUNC(cmd_allowauth)
1751 struct userNode *target;
1752 struct handle_info *hi;
1755 NICKSERV_MIN_PARMS(2);
1756 if (!(target = GetUserH(argv[1]))) {
1757 reply("MSG_NICK_UNKNOWN", argv[1]);
1760 if (target->handle_info) {
1761 reply("NSMSG_USER_PREV_AUTH", target->nick);
1764 if (IsStamped(target)) {
1765 /* Unauthenticated users might still have been stamped
1766 previously and could therefore have a hidden host;
1767 do not allow them to authenticate to an account. */
1768 reply("NSMSG_USER_PREV_STAMP", target->nick);
1773 else if (!(hi = get_handle_info(argv[2]))) {
1774 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1778 if (hi->opserv_level > user->handle_info->opserv_level) {
1779 reply("MSG_USER_OUTRANKED", hi->handle);
1782 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1783 || (hi->opserv_level > 0))
1784 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1785 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1788 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1789 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1790 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1791 if (nickserv_conf.email_enabled)
1792 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1794 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1795 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1797 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1799 for (n=0; n<allowauth_func_used; n++)
1800 allowauth_func_list[n](user, target, hi);
1804 static NICKSERV_FUNC(cmd_authcookie)
1806 struct handle_info *hi;
1808 NICKSERV_MIN_PARMS(2);
1809 if (user->handle_info) {
1810 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1813 if (IsStamped(user)) {
1814 /* Unauthenticated users might still have been stamped
1815 previously and could therefore have a hidden host;
1816 do not allow them to authenticate to an account. */
1817 reply("NSMSG_STAMPED_AUTHCOOKIE");
1820 if (!(hi = get_handle_info(argv[1]))) {
1821 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1824 if (!hi->email_addr) {
1825 reply("MSG_SET_EMAIL_ADDR");
1828 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1832 static NICKSERV_FUNC(cmd_delcookie)
1834 struct handle_info *hi;
1836 hi = user->handle_info;
1838 reply("NSMSG_NO_COOKIE");
1841 switch (hi->cookie->type) {
1844 reply("NSMSG_MUST_TIME_OUT");
1847 nickserv_eat_cookie(hi->cookie);
1848 reply("NSMSG_ATE_COOKIE");
1854 static NICKSERV_FUNC(cmd_resetpass)
1856 struct handle_info *hi;
1857 char crypted[MD5_CRYPT_LENGTH];
1859 NICKSERV_MIN_PARMS(3);
1860 if (user->handle_info) {
1861 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1864 if (IsStamped(user)) {
1865 /* Unauthenticated users might still have been stamped
1866 previously and could therefore have a hidden host;
1867 do not allow them to activate an account. */
1868 reply("NSMSG_STAMPED_RESETPASS");
1871 if (!(hi = get_handle_info(argv[1]))) {
1872 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1875 if (!hi->email_addr) {
1876 reply("MSG_SET_EMAIL_ADDR");
1879 cryptpass(argv[2], crypted);
1881 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1885 static NICKSERV_FUNC(cmd_cookie)
1887 struct handle_info *hi;
1890 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1893 NICKSERV_MIN_PARMS(3);
1894 if (!(hi = get_handle_info(argv[1]))) {
1895 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1901 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1902 reply("NSMSG_HANDLE_SUSPENDED");
1907 reply("NSMSG_NO_COOKIE");
1911 /* Check validity of operation before comparing cookie to
1912 * prohibit guessing by authed users. */
1913 if (user->handle_info
1914 && (hi->cookie->type != EMAIL_CHANGE)
1915 && (hi->cookie->type != PASSWORD_CHANGE)) {
1916 reply("NSMSG_CANNOT_COOKIE");
1920 if (strcmp(cookie, hi->cookie->cookie)) {
1921 reply("NSMSG_BAD_COOKIE");
1925 switch (hi->cookie->type) {
1927 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1928 set_user_handle_info(user, hi, 1);
1929 reply("NSMSG_HANDLE_ACTIVATED");
1931 case PASSWORD_CHANGE:
1932 set_user_handle_info(user, hi, 1);
1933 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1934 reply("NSMSG_PASSWORD_CHANGED");
1937 nickserv_set_email_addr(hi, hi->cookie->data);
1938 reply("NSMSG_EMAIL_CHANGED");
1941 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1942 set_user_handle_info(user, hi, 1);
1943 nickserv_addmask(user, hi, mask);
1944 reply("NSMSG_AUTH_SUCCESS");
1949 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1950 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1954 nickserv_eat_cookie(hi->cookie);
1959 static NICKSERV_FUNC(cmd_oregnick) {
1961 struct handle_info *target;
1962 struct nick_info *ni;
1964 NICKSERV_MIN_PARMS(3);
1965 if (!(target = modcmd_get_handle_info(user, argv[1])))
1968 if (!is_registerable_nick(nick)) {
1969 reply("NSMSG_BAD_NICK", nick);
1972 ni = dict_find(nickserv_nick_dict, nick, NULL);
1974 reply("NSMSG_NICK_EXISTS", nick);
1977 register_nick(nick, target);
1978 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1982 static NICKSERV_FUNC(cmd_regnick) {
1984 struct nick_info *ni;
1986 if (!is_registerable_nick(user->nick)) {
1987 reply("NSMSG_BAD_NICK", user->nick);
1990 /* count their nicks, see if it's too many */
1991 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1992 if (n >= nickserv_conf.nicks_per_handle) {
1993 reply("NSMSG_TOO_MANY_NICKS");
1996 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1998 reply("NSMSG_NICK_EXISTS", user->nick);
2001 register_nick(user->nick, user->handle_info);
2002 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2006 static NICKSERV_FUNC(cmd_pass)
2008 struct handle_info *hi;
2009 const char *old_pass, *new_pass;
2011 NICKSERV_MIN_PARMS(3);
2012 hi = user->handle_info;
2016 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2017 if (!checkpass(old_pass, hi->passwd)) {
2018 argv[1] = "BADPASS";
2019 reply("NSMSG_PASSWORD_INVALID");
2022 cryptpass(new_pass, hi->passwd);
2024 reply("NSMSG_PASS_SUCCESS");
2029 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2032 char *new_mask = canonicalize_hostmask(strdup(mask));
2033 for (i=0; i<hi->masks->used; i++) {
2034 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2035 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2040 string_list_append(hi->masks, new_mask);
2041 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2045 static NICKSERV_FUNC(cmd_addmask)
2048 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2049 int res = nickserv_addmask(user, user->handle_info, mask);
2053 if (!is_gline(argv[1])) {
2054 reply("NSMSG_MASK_INVALID", argv[1]);
2057 return nickserv_addmask(user, user->handle_info, argv[1]);
2061 static NICKSERV_FUNC(cmd_oaddmask)
2063 struct handle_info *hi;
2065 NICKSERV_MIN_PARMS(3);
2066 if (!(hi = get_victim_oper(user, argv[1])))
2068 return nickserv_addmask(user, hi, argv[2]);
2072 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2075 for (i=0; i<hi->masks->used; i++) {
2076 if (!strcmp(del_mask, hi->masks->list[i])) {
2077 char *old_mask = hi->masks->list[i];
2078 if (hi->masks->used == 1 && !force) {
2079 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2082 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2083 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2088 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2092 static NICKSERV_FUNC(cmd_delmask)
2094 NICKSERV_MIN_PARMS(2);
2095 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2098 static NICKSERV_FUNC(cmd_odelmask)
2100 struct handle_info *hi;
2101 NICKSERV_MIN_PARMS(3);
2102 if (!(hi = get_victim_oper(user, argv[1])))
2104 return nickserv_delmask(user, hi, argv[2], 1);
2108 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2109 unsigned int nn, add = 1, pos;
2110 unsigned long added, removed, flag;
2112 for (added=removed=nn=0; str[nn]; nn++) {
2114 case '+': add = 1; break;
2115 case '-': add = 0; break;
2117 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2118 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2121 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2122 /* cheesy avoidance of looking up the flag name.. */
2123 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2126 flag = 1 << (pos - 1);
2128 added |= flag, removed &= ~flag;
2130 removed |= flag, added &= ~flag;
2135 *premoved = removed;
2140 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2142 unsigned long before, after, added, removed;
2143 struct userNode *uNode;
2145 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2146 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2148 hi->flags = (hi->flags | added) & ~removed;
2149 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2151 /* Strip helping flag if they're only a support helper and not
2152 * currently in #support. */
2153 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2154 struct channelList *schannels;
2156 schannels = chanserv_support_channels();
2157 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2158 for (ii = 0; ii < schannels->used; ++ii)
2159 if (GetUserMode(schannels->list[ii], uNode))
2161 if (ii < schannels->used)
2165 HANDLE_CLEAR_FLAG(hi, HELPING);
2168 if (after && !before) {
2169 /* Add user to current helper list. */
2170 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2171 userList_append(&curr_helpers, uNode);
2172 } else if (!after && before) {
2173 /* Remove user from current helper list. */
2174 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2175 userList_remove(&curr_helpers, uNode);
2182 set_list(struct userNode *user, struct handle_info *hi, int override)
2186 char *set_display[] = {
2187 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2188 "EMAIL", "MAXLOGINS", "LANGUAGE"
2191 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2193 /* Do this so options are presented in a consistent order. */
2194 for (i = 0; i < ArrayLength(set_display); ++i)
2195 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2196 opt(user, hi, override, 0, NULL);
2199 static NICKSERV_FUNC(cmd_set)
2201 struct handle_info *hi;
2204 hi = user->handle_info;
2206 set_list(user, hi, 0);
2209 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2210 reply("NSMSG_INVALID_OPTION", argv[1]);
2213 return opt(user, hi, 0, argc-1, argv+1);
2216 static NICKSERV_FUNC(cmd_oset)
2218 struct handle_info *hi;
2219 struct svccmd *subcmd;
2221 char cmdname[MAXLEN];
2223 NICKSERV_MIN_PARMS(2);
2225 if (!(hi = get_victim_oper(user, argv[1])))
2229 set_list(user, hi, 0);
2233 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2234 reply("NSMSG_INVALID_OPTION", argv[2]);
2238 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2239 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2240 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2243 return opt(user, hi, 1, argc-2, argv+2);
2246 static OPTION_FUNC(opt_info)
2250 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2252 hi->infoline = NULL;
2254 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2258 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2259 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2263 static OPTION_FUNC(opt_width)
2266 hi->screen_width = strtoul(argv[1], NULL, 0);
2268 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2269 hi->screen_width = MIN_LINE_SIZE;
2270 else if (hi->screen_width > MAX_LINE_SIZE)
2271 hi->screen_width = MAX_LINE_SIZE;
2273 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2277 static OPTION_FUNC(opt_tablewidth)
2280 hi->table_width = strtoul(argv[1], NULL, 0);
2282 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2283 hi->table_width = MIN_LINE_SIZE;
2284 else if (hi->screen_width > MAX_LINE_SIZE)
2285 hi->table_width = MAX_LINE_SIZE;
2287 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2291 static OPTION_FUNC(opt_color)
2294 if (enabled_string(argv[1]))
2295 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2296 else if (disabled_string(argv[1]))
2297 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2299 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2304 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2308 static OPTION_FUNC(opt_privmsg)
2311 if (enabled_string(argv[1]))
2312 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2313 else if (disabled_string(argv[1]))
2314 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2316 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2321 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2325 static OPTION_FUNC(opt_style)
2330 if (!irccasecmp(argv[1], "Zoot"))
2331 hi->userlist_style = HI_STYLE_ZOOT;
2332 else if (!irccasecmp(argv[1], "def"))
2333 hi->userlist_style = HI_STYLE_DEF;
2336 switch (hi->userlist_style) {
2345 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2349 static OPTION_FUNC(opt_password)
2352 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2357 cryptpass(argv[1], hi->passwd);
2359 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2363 static OPTION_FUNC(opt_flags)
2366 unsigned int ii, flen;
2369 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2374 nickserv_apply_flags(user, hi, argv[1]);
2376 for (ii = flen = 0; handle_flags[ii]; ii++)
2377 if (hi->flags & (1 << ii))
2378 flags[flen++] = handle_flags[ii];
2381 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2383 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2387 static OPTION_FUNC(opt_email)
2391 if (!is_valid_email_addr(argv[1])) {
2392 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2395 if ((str = mail_prohibited_address(argv[1]))) {
2396 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2399 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2400 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2402 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2404 nickserv_set_email_addr(hi, argv[1]);
2406 nickserv_eat_cookie(hi->cookie);
2407 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2410 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2414 static OPTION_FUNC(opt_maxlogins)
2416 unsigned char maxlogins;
2418 maxlogins = strtoul(argv[1], NULL, 0);
2419 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2420 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2423 hi->maxlogins = maxlogins;
2425 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2426 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2430 static OPTION_FUNC(opt_language)
2432 struct language *lang;
2434 lang = language_find(argv[1]);
2435 if (irccasecmp(lang->name, argv[1]))
2436 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2437 hi->language = lang;
2439 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2443 static OPTION_FUNC(opt_karma)
2446 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2451 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2452 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2453 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2454 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2456 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2460 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2465 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2466 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2468 if ((user->handle_info->opserv_level < target->opserv_level)
2469 || ((user->handle_info->opserv_level == target->opserv_level)
2470 && (user->handle_info->opserv_level < 1000))) {
2471 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2474 if ((user->handle_info->opserv_level < new_level)
2475 || ((user->handle_info->opserv_level == new_level)
2476 && (user->handle_info->opserv_level < 1000))) {
2477 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2480 if (user->handle_info == target) {
2481 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2484 if (target->opserv_level == new_level)
2486 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2487 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2488 target->opserv_level = new_level;
2492 static OPTION_FUNC(opt_level)
2497 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2501 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2502 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2506 static OPTION_FUNC(opt_epithet)
2509 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2513 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2514 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2517 if ((epithet[0] == '*') && !epithet[1])
2520 hi->epithet = strdup(epithet);
2524 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2526 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2530 static OPTION_FUNC(opt_title)
2535 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2539 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2541 if (strchr(title, '.')) {
2542 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2545 if ((strlen(user->handle_info->handle) + strlen(title) +
2546 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2547 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2552 if (!strcmp(title, "*")) {
2553 hi->fakehost = NULL;
2555 hi->fakehost = malloc(strlen(title)+2);
2556 hi->fakehost[0] = '.';
2557 strcpy(hi->fakehost+1, title);
2560 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2561 title = hi->fakehost + 1;
2565 title = user_find_message(user, "MSG_NONE");
2566 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2570 static OPTION_FUNC(opt_fakehost)
2575 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2579 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2581 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2582 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2586 if (!strcmp(fake, "*"))
2587 hi->fakehost = NULL;
2589 hi->fakehost = strdup(fake);
2590 fake = hi->fakehost;
2593 fake = generate_fakehost(hi);
2595 fake = user_find_message(user, "MSG_NONE");
2596 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2600 static NICKSERV_FUNC(cmd_reclaim)
2602 struct handle_info *hi;
2603 struct nick_info *ni;
2604 struct userNode *victim;
2606 NICKSERV_MIN_PARMS(2);
2607 hi = user->handle_info;
2608 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2610 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2613 if (ni->owner != user->handle_info) {
2614 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2617 victim = GetUserH(ni->nick);
2619 reply("MSG_NICK_UNKNOWN", ni->nick);
2622 if (victim == user) {
2623 reply("NSMSG_NICK_USER_YOU");
2626 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2627 switch (nickserv_conf.reclaim_action) {
2628 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2629 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2630 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2631 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2636 static NICKSERV_FUNC(cmd_unregnick)
2639 struct handle_info *hi;
2640 struct nick_info *ni;
2642 hi = user->handle_info;
2643 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2644 ni = dict_find(nickserv_nick_dict, nick, NULL);
2646 reply("NSMSG_UNKNOWN_NICK", nick);
2649 if (hi != ni->owner) {
2650 reply("NSMSG_NOT_YOUR_NICK", nick);
2653 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2658 static NICKSERV_FUNC(cmd_ounregnick)
2660 struct nick_info *ni;
2662 NICKSERV_MIN_PARMS(2);
2663 if (!(ni = get_nick_info(argv[1]))) {
2664 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2667 if (!oper_outranks(user, ni->owner))
2669 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2674 static NICKSERV_FUNC(cmd_unregister)
2676 struct handle_info *hi;
2679 NICKSERV_MIN_PARMS(2);
2680 hi = user->handle_info;
2683 if (checkpass(passwd, hi->passwd)) {
2684 nickserv_unregister_handle(hi, user);
2687 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2688 reply("NSMSG_PASSWORD_INVALID");
2693 static NICKSERV_FUNC(cmd_ounregister)
2695 struct handle_info *hi;
2696 char reason[MAXLEN];
2699 NICKSERV_MIN_PARMS(2);
2700 if (!(hi = get_victim_oper(user, argv[1])))
2703 if (HANDLE_FLAGGED(hi, NODELETE)) {
2704 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
2708 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
2710 ((hi->flags & nickserv_conf.ounregister_flags)
2712 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
2713 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
2717 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2718 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2719 nickserv_unregister_handle(hi, user);
2723 static NICKSERV_FUNC(cmd_status)
2725 if (nickserv_conf.disable_nicks) {
2726 reply("NSMSG_GLOBAL_STATS_NONICK",
2727 dict_size(nickserv_handle_dict));
2729 if (user->handle_info) {
2731 struct nick_info *ni;
2732 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2733 reply("NSMSG_HANDLE_STATS", cnt);
2735 reply("NSMSG_HANDLE_NONE");
2737 reply("NSMSG_GLOBAL_STATS",
2738 dict_size(nickserv_handle_dict),
2739 dict_size(nickserv_nick_dict));
2744 static NICKSERV_FUNC(cmd_ghost)
2746 struct userNode *target;
2747 char reason[MAXLEN];
2749 NICKSERV_MIN_PARMS(2);
2750 if (!(target = GetUserH(argv[1]))) {
2751 reply("MSG_NICK_UNKNOWN", argv[1]);
2754 if (target == user) {
2755 reply("NSMSG_CANNOT_GHOST_SELF");
2758 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2759 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2762 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2763 DelUser(target, nickserv, 1, reason);
2764 reply("NSMSG_GHOST_KILLED", argv[1]);
2768 static NICKSERV_FUNC(cmd_vacation)
2770 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2771 reply("NSMSG_ON_VACATION");
2775 static NICKSERV_FUNC(cmd_addnote)
2777 struct handle_info *hi;
2778 unsigned long duration;
2781 struct handle_note *prev;
2782 struct handle_note *note;
2784 /* Parse parameters and figure out values for note's fields. */
2785 NICKSERV_MIN_PARMS(4);
2786 hi = get_victim_oper(user, argv[1]);
2789 duration = ParseInterval(argv[2]);
2790 if (duration > 2*365*86400) {
2791 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2794 unsplit_string(argv + 3, argc - 3, text);
2795 WALK_NOTES(hi, prev, note) {}
2796 id = prev ? (prev->id + 1) : 1;
2798 /* Create the new note structure. */
2799 note = calloc(1, sizeof(*note) + strlen(text));
2801 note->expires = duration ? (now + duration) : 0;
2804 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2805 strcpy(note->note, text);
2810 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2814 static NICKSERV_FUNC(cmd_delnote)
2816 struct handle_info *hi;
2817 struct handle_note *prev;
2818 struct handle_note *note;
2821 NICKSERV_MIN_PARMS(3);
2822 hi = get_victim_oper(user, argv[1]);
2825 id = strtoul(argv[2], NULL, 10);
2826 WALK_NOTES(hi, prev, note) {
2827 if (id == note->id) {
2829 prev->next = note->next;
2831 hi->notes = note->next;
2833 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2837 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2842 nickserv_saxdb_write(struct saxdb_context *ctx) {
2844 struct handle_info *hi;
2847 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2849 #ifdef WITH_PROTOCOL_BAHAMUT
2852 saxdb_start_record(ctx, iter_key(it), 0);
2854 struct handle_cookie *cookie = hi->cookie;
2857 switch (cookie->type) {
2858 case ACTIVATION: type = KEY_ACTIVATION; break;
2859 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2860 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2861 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2862 default: type = NULL; break;
2865 saxdb_start_record(ctx, KEY_COOKIE, 0);
2866 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2867 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2869 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2870 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2871 saxdb_end_record(ctx);
2875 struct handle_note *prev, *note;
2876 saxdb_start_record(ctx, KEY_NOTES, 0);
2877 WALK_NOTES(hi, prev, note) {
2878 snprintf(flags, sizeof(flags), "%d", note->id);
2879 saxdb_start_record(ctx, flags, 0);
2881 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
2882 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
2883 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
2884 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
2885 saxdb_end_record(ctx);
2887 saxdb_end_record(ctx);
2890 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2892 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2894 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2898 for (ii=flen=0; handle_flags[ii]; ++ii)
2899 if (hi->flags & (1 << ii))
2900 flags[flen++] = handle_flags[ii];
2902 saxdb_write_string(ctx, KEY_FLAGS, flags);
2904 #ifdef WITH_PROTOCOL_BAHAMUT
2905 saxdb_write_int(ctx, KEY_ID, hi->id);
2908 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2909 if (hi->last_quit_host[0])
2910 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2911 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2913 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
2914 if (hi->masks->used)
2915 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2917 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2919 struct string_list *slist;
2920 struct nick_info *ni;
2922 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2923 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2924 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2928 if (hi->opserv_level)
2929 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2930 if (hi->language != lang_C)
2931 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2932 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2933 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2934 if (hi->screen_width)
2935 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2936 if (hi->table_width)
2937 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2938 flags[0] = hi->userlist_style;
2940 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2941 saxdb_end_record(ctx);
2946 static handle_merge_func_t *handle_merge_func_list;
2947 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2950 reg_handle_merge_func(handle_merge_func_t func)
2952 if (handle_merge_func_used == handle_merge_func_size) {
2953 if (handle_merge_func_size) {
2954 handle_merge_func_size <<= 1;
2955 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2957 handle_merge_func_size = 8;
2958 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2961 handle_merge_func_list[handle_merge_func_used++] = func;
2964 static NICKSERV_FUNC(cmd_merge)
2966 struct handle_info *hi_from, *hi_to;
2967 struct userNode *last_user;
2968 struct userData *cList, *cListNext;
2969 unsigned int ii, jj, n;
2970 char buffer[MAXLEN];
2972 NICKSERV_MIN_PARMS(3);
2974 if (!(hi_from = get_victim_oper(user, argv[1])))
2976 if (!(hi_to = get_victim_oper(user, argv[2])))
2978 if (hi_to == hi_from) {
2979 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2983 for (n=0; n<handle_merge_func_used; n++)
2984 handle_merge_func_list[n](user, hi_to, hi_from);
2986 /* Append "from" handle's nicks to "to" handle's nick list. */
2988 struct nick_info *last_ni;
2989 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2990 last_ni->next = hi_from->nicks;
2992 while (hi_from->nicks) {
2993 hi_from->nicks->owner = hi_to;
2994 hi_from->nicks = hi_from->nicks->next;
2997 /* Merge the hostmasks. */
2998 for (ii=0; ii<hi_from->masks->used; ii++) {
2999 char *mask = hi_from->masks->list[ii];
3000 for (jj=0; jj<hi_to->masks->used; jj++)
3001 if (match_ircglobs(hi_to->masks->list[jj], mask))
3003 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3004 string_list_append(hi_to->masks, strdup(mask));
3007 /* Merge the lists of authed users. */
3009 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3010 last_user->next_authed = hi_from->users;
3012 hi_to->users = hi_from->users;
3014 /* Repoint the old "from" handle's users. */
3015 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3016 last_user->handle_info = hi_to;
3018 hi_from->users = NULL;
3020 /* Merge channel userlists. */
3021 for (cList=hi_from->channels; cList; cList=cListNext) {
3022 struct userData *cList2;
3023 cListNext = cList->u_next;
3024 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3025 if (cList->channel == cList2->channel)
3027 if (cList2 && (cList2->access >= cList->access)) {
3028 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);
3029 /* keep cList2 in hi_to; remove cList from hi_from */
3030 del_channel_user(cList, 1);
3033 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);
3034 /* remove the lower-ranking cList2 from hi_to */
3035 del_channel_user(cList2, 1);
3037 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3039 /* cList needs to be moved from hi_from to hi_to */
3040 cList->handle = hi_to;
3041 /* Remove from linked list for hi_from */
3042 assert(!cList->u_prev);
3043 hi_from->channels = cList->u_next;
3045 cList->u_next->u_prev = cList->u_prev;
3046 /* Add to linked list for hi_to */
3047 cList->u_prev = NULL;
3048 cList->u_next = hi_to->channels;
3049 if (hi_to->channels)
3050 hi_to->channels->u_prev = cList;
3051 hi_to->channels = cList;
3055 /* Do they get an OpServ level promotion? */
3056 if (hi_from->opserv_level > hi_to->opserv_level)
3057 hi_to->opserv_level = hi_from->opserv_level;
3059 /* What about last seen time? */
3060 if (hi_from->lastseen > hi_to->lastseen)
3061 hi_to->lastseen = hi_from->lastseen;
3063 /* New karma is the sum of the two original karmas. */
3064 hi_to->karma += hi_from->karma;
3066 /* Does a fakehost carry over? (This intentionally doesn't set it
3067 * for users previously attached to hi_to. They'll just have to
3070 if (hi_from->fakehost && !hi_to->fakehost)
3071 hi_to->fakehost = strdup(hi_from->fakehost);
3073 /* Notify of success. */
3074 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3075 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3076 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3078 /* Unregister the "from" handle. */
3079 nickserv_unregister_handle(hi_from, NULL);
3084 struct nickserv_discrim {
3085 unsigned long flags_on, flags_off;
3086 time_t min_registered, max_registered;
3089 int min_level, max_level;
3090 int min_karma, max_karma;
3091 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3092 const char *nickmask;
3093 const char *hostmask;
3094 const char *fakehostmask;
3095 const char *handlemask;
3096 const char *emailmask;
3099 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3101 struct discrim_apply_info {
3102 struct nickserv_discrim *discrim;
3103 discrim_search_func func;
3104 struct userNode *source;
3105 unsigned int matched;
3108 static struct nickserv_discrim *
3109 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3112 struct nickserv_discrim *discrim;
3114 discrim = malloc(sizeof(*discrim));
3115 memset(discrim, 0, sizeof(*discrim));
3116 discrim->min_level = 0;
3117 discrim->max_level = INT_MAX;
3118 discrim->limit = 50;
3119 discrim->min_registered = 0;
3120 discrim->max_registered = INT_MAX;
3121 discrim->lastseen = LONG_MAX;
3122 discrim->min_karma = INT_MIN;
3123 discrim->max_karma = INT_MAX;
3125 for (i=0; i<argc; i++) {
3126 if (i == argc - 1) {
3127 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3130 if (!irccasecmp(argv[i], "limit")) {
3131 discrim->limit = strtoul(argv[++i], NULL, 0);
3132 } else if (!irccasecmp(argv[i], "flags")) {
3133 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3134 } else if (!irccasecmp(argv[i], "registered")) {
3135 const char *cmp = argv[++i];
3136 if (cmp[0] == '<') {
3137 if (cmp[1] == '=') {
3138 discrim->min_registered = now - ParseInterval(cmp+2);
3140 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3142 } else if (cmp[0] == '=') {
3143 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3144 } else if (cmp[0] == '>') {
3145 if (cmp[1] == '=') {
3146 discrim->max_registered = now - ParseInterval(cmp+2);
3148 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3151 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3153 } else if (!irccasecmp(argv[i], "seen")) {
3154 discrim->lastseen = now - ParseInterval(argv[++i]);
3155 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3156 discrim->nickmask = argv[++i];
3157 } else if (!irccasecmp(argv[i], "hostmask")) {
3159 if (!irccasecmp(argv[i], "exact")) {
3160 if (i == argc - 1) {
3161 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3164 discrim->hostmask_type = EXACT;
3165 } else if (!irccasecmp(argv[i], "subset")) {
3166 if (i == argc - 1) {
3167 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3170 discrim->hostmask_type = SUBSET;
3171 } else if (!irccasecmp(argv[i], "superset")) {
3172 if (i == argc - 1) {
3173 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3176 discrim->hostmask_type = SUPERSET;
3177 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3178 if (i == argc - 1) {
3179 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3182 discrim->hostmask_type = LASTQUIT;
3185 discrim->hostmask_type = SUPERSET;
3187 discrim->hostmask = argv[++i];
3188 } else if (!irccasecmp(argv[i], "fakehost")) {
3189 if (!irccasecmp(argv[++i], "*")) {
3190 discrim->fakehostmask = 0;
3192 discrim->fakehostmask = argv[i];
3194 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3195 if (!irccasecmp(argv[++i], "*")) {
3196 discrim->handlemask = 0;
3198 discrim->handlemask = argv[i];
3200 } else if (!irccasecmp(argv[i], "email")) {
3201 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3202 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3204 } else if (!irccasecmp(argv[++i], "*")) {
3205 discrim->emailmask = 0;
3207 discrim->emailmask = argv[i];
3209 } else if (!irccasecmp(argv[i], "access")) {
3210 const char *cmp = argv[++i];
3211 if (cmp[0] == '<') {
3212 if (discrim->min_level == 0) discrim->min_level = 1;
3213 if (cmp[1] == '=') {
3214 discrim->max_level = strtoul(cmp+2, NULL, 0);
3216 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3218 } else if (cmp[0] == '=') {
3219 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3220 } else if (cmp[0] == '>') {
3221 if (cmp[1] == '=') {
3222 discrim->min_level = strtoul(cmp+2, NULL, 0);
3224 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3227 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3229 } else if (!irccasecmp(argv[i], "karma")) {
3230 const char *cmp = argv[++i];
3231 if (cmp[0] == '<') {
3232 if (cmp[1] == '=') {
3233 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3235 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3237 } else if (cmp[0] == '=') {
3238 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3239 } else if (cmp[0] == '>') {
3240 if (cmp[1] == '=') {
3241 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3243 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3246 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3249 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3260 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3262 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3263 || (discrim->flags_off & hi->flags)
3264 || (discrim->min_registered > hi->registered)
3265 || (discrim->max_registered < hi->registered)
3266 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3267 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3268 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3269 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3270 || (discrim->min_level > hi->opserv_level)
3271 || (discrim->max_level < hi->opserv_level)
3272 || (discrim->min_karma > hi->karma)
3273 || (discrim->max_karma < hi->karma)
3277 if (discrim->hostmask) {
3279 for (i=0; i<hi->masks->used; i++) {
3280 const char *mask = hi->masks->list[i];
3281 if ((discrim->hostmask_type == SUBSET)
3282 && (match_ircglobs(discrim->hostmask, mask))) break;
3283 else if ((discrim->hostmask_type == EXACT)
3284 && !irccasecmp(discrim->hostmask, mask)) break;
3285 else if ((discrim->hostmask_type == SUPERSET)
3286 && (match_ircglobs(mask, discrim->hostmask))) break;
3287 else if ((discrim->hostmask_type == LASTQUIT)
3288 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3290 if (i==hi->masks->used) return 0;
3292 if (discrim->nickmask) {
3293 struct nick_info *nick = hi->nicks;
3295 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3298 if (!nick) return 0;
3304 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3306 dict_iterator_t it, next;
3307 unsigned int matched;
3309 for (it = dict_first(nickserv_handle_dict), matched = 0;
3310 it && (matched < discrim->limit);
3312 next = iter_next(it);
3313 if (nickserv_discrim_match(discrim, iter_data(it))) {
3314 dsf(source, iter_data(it));
3322 search_print_func(struct userNode *source, struct handle_info *match)
3324 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3328 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3333 search_unregister_func (struct userNode *source, struct handle_info *match)
3335 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3336 nickserv_unregister_handle(match, source);
3340 nickserv_sort_accounts_by_access(const void *a, const void *b)
3342 const struct handle_info *hi_a = *(const struct handle_info**)a;
3343 const struct handle_info *hi_b = *(const struct handle_info**)b;
3344 if (hi_a->opserv_level != hi_b->opserv_level)
3345 return hi_b->opserv_level - hi_a->opserv_level;
3346 return irccasecmp(hi_a->handle, hi_b->handle);
3350 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3352 struct handle_info_list hil;
3353 struct helpfile_table tbl;
3358 memset(&hil, 0, sizeof(hil));
3359 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3360 struct handle_info *hi = iter_data(it);
3361 if (hi->opserv_level)
3362 handle_info_list_append(&hil, hi);
3364 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3365 tbl.length = hil.used + 1;
3367 tbl.flags = TABLE_NO_FREE;
3368 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3369 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3372 for (ii = 0; ii < hil.used; ) {
3373 ary = malloc(tbl.width * sizeof(ary[0]));
3374 ary[0] = hil.list[ii]->handle;
3375 ary[1] = strtab(hil.list[ii]->opserv_level);
3376 tbl.contents[++ii] = ary;
3378 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3379 reply("MSG_MATCH_COUNT", hil.used);
3380 for (ii = 0; ii < hil.used; ii++)
3381 free(tbl.contents[ii]);
3386 static NICKSERV_FUNC(cmd_search)
3388 struct nickserv_discrim *discrim;
3389 discrim_search_func action;
3390 struct svccmd *subcmd;
3391 unsigned int matches;
3394 NICKSERV_MIN_PARMS(3);
3395 sprintf(buf, "search %s", argv[1]);
3396 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3397 if (!irccasecmp(argv[1], "print"))
3398 action = search_print_func;
3399 else if (!irccasecmp(argv[1], "count"))
3400 action = search_count_func;
3401 else if (!irccasecmp(argv[1], "unregister"))
3402 action = search_unregister_func;
3404 reply("NSMSG_INVALID_ACTION", argv[1]);
3408 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3411 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3415 if (action == search_print_func)
3416 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3417 else if (action == search_count_func)
3418 discrim->limit = INT_MAX;
3420 matches = nickserv_discrim_search(discrim, action, user);
3423 reply("MSG_MATCH_COUNT", matches);
3425 reply("MSG_NO_MATCHES");
3431 static MODCMD_FUNC(cmd_checkpass)
3433 struct handle_info *hi;
3435 NICKSERV_MIN_PARMS(3);
3436 if (!(hi = get_handle_info(argv[1]))) {
3437 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3440 if (checkpass(argv[2], hi->passwd))
3441 reply("CHECKPASS_YES");
3443 reply("CHECKPASS_NO");
3449 nickserv_db_read_handle(const char *handle, dict_t obj)
3452 struct string_list *masks, *slist;
3453 struct handle_info *hi;
3454 struct userNode *authed_users;
3455 struct userData *channels;
3456 unsigned long int id;
3460 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3461 id = str ? strtoul(str, NULL, 0) : 0;
3462 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3464 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3467 if ((hi = get_handle_info(handle))) {
3468 authed_users = hi->users;
3469 channels = hi->channels;
3471 hi->channels = NULL;
3472 dict_remove(nickserv_handle_dict, hi->handle);
3474 authed_users = NULL;
3477 hi = register_handle(handle, str, id);
3479 hi->users = authed_users;
3480 while (authed_users) {
3481 authed_users->handle_info = hi;
3482 authed_users = authed_users->next_authed;
3485 hi->channels = channels;
3486 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3487 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3488 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3489 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3490 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3491 hi->language = language_find(str ? str : "C");
3492 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3493 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3494 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3496 hi->infoline = strdup(str);
3497 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3498 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3499 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3500 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3501 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3502 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3503 /* We want to read the nicks even if disable_nicks is set. This is so
3504 * that we don't lose the nick data entirely. */
3505 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3507 for (ii=0; ii<slist->used; ii++)
3508 register_nick(slist->list[ii], hi);
3510 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3512 for (ii=0; str[ii]; ii++)
3513 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3515 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3516 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3517 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3518 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3519 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3520 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3521 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3523 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3525 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3526 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3528 nickserv_set_email_addr(hi, str);
3529 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3531 hi->epithet = strdup(str);
3532 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3534 hi->fakehost = strdup(str);
3535 /* Read the "cookie" sub-database (if it exists). */
3536 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3538 const char *data, *type, *expires, *cookie_str;
3539 struct handle_cookie *cookie;
3541 cookie = calloc(1, sizeof(*cookie));
3542 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3543 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3544 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3545 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3546 if (!type || !expires || !cookie_str) {
3547 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3550 if (!irccasecmp(type, KEY_ACTIVATION))
3551 cookie->type = ACTIVATION;
3552 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3553 cookie->type = PASSWORD_CHANGE;
3554 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3555 cookie->type = EMAIL_CHANGE;
3556 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3557 cookie->type = ALLOWAUTH;
3559 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3562 cookie->expires = strtoul(expires, NULL, 0);
3563 if (cookie->expires < now)
3566 cookie->data = strdup(data);
3567 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3571 nickserv_bake_cookie(cookie);
3573 nickserv_free_cookie(cookie);
3575 /* Read the "notes" sub-database (if it exists). */
3576 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3579 struct handle_note *last_note;
3580 struct handle_note *note;
3583 for (it = dict_first(subdb); it; it = iter_next(it)) {
3584 const char *expires;
3592 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3594 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", id, hi->handle);
3597 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3598 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3599 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3600 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3601 if (!setter || !text || !set) {
3602 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", id, hi->handle);
3605 note = calloc(1, sizeof(*note) + strlen(text));
3607 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3608 note->set = strtoul(set, NULL, 10);
3609 note->id = strtoul(id, NULL, 10);
3610 safestrncpy(note->setter, setter, sizeof(note->setter));
3611 strcpy(note->note, text);
3613 last_note->next = note;
3622 nickserv_saxdb_read(dict_t db) {
3624 struct record_data *rd;
3626 for (it=dict_first(db); it; it=iter_next(it)) {
3628 nickserv_db_read_handle(iter_key(it), rd->d.object);
3633 static NICKSERV_FUNC(cmd_mergedb)
3635 struct timeval start, stop;
3638 NICKSERV_MIN_PARMS(2);
3639 gettimeofday(&start, NULL);
3640 if (!(db = parse_database(argv[1]))) {
3641 reply("NSMSG_DB_UNREADABLE", argv[1]);
3644 nickserv_saxdb_read(db);
3646 gettimeofday(&stop, NULL);
3647 stop.tv_sec -= start.tv_sec;
3648 stop.tv_usec -= start.tv_usec;
3649 if (stop.tv_usec < 0) {
3651 stop.tv_usec += 1000000;
3653 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3658 expire_handles(UNUSED_ARG(void *data))
3660 dict_iterator_t it, next;
3662 struct handle_info *hi;
3664 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3665 next = iter_next(it);
3667 if ((hi->opserv_level > 0)
3669 || HANDLE_FLAGGED(hi, FROZEN)
3670 || HANDLE_FLAGGED(hi, NODELETE)) {
3673 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3674 if ((now - hi->lastseen) > expiry) {
3675 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3676 nickserv_unregister_handle(hi, NULL);
3680 if (nickserv_conf.handle_expire_frequency)
3681 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3685 nickserv_load_dict(const char *fname)
3689 if (!(file = fopen(fname, "r"))) {
3690 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3693 while (!feof(file)) {
3694 fgets(line, sizeof(line), file);
3697 if (line[strlen(line)-1] == '\n')
3698 line[strlen(line)-1] = 0;
3699 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3702 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3705 static enum reclaim_action
3706 reclaim_action_from_string(const char *str) {
3708 return RECLAIM_NONE;
3709 else if (!irccasecmp(str, "warn"))
3710 return RECLAIM_WARN;
3711 else if (!irccasecmp(str, "svsnick"))
3712 return RECLAIM_SVSNICK;
3713 else if (!irccasecmp(str, "kill"))
3714 return RECLAIM_KILL;
3716 return RECLAIM_NONE;
3720 nickserv_conf_read(void)
3722 dict_t conf_node, child;
3726 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3727 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3730 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3732 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3733 if (nickserv_conf.valid_handle_regex_set)
3734 regfree(&nickserv_conf.valid_handle_regex);
3736 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3737 nickserv_conf.valid_handle_regex_set = !err;
3738 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3740 nickserv_conf.valid_handle_regex_set = 0;
3742 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3743 if (nickserv_conf.valid_nick_regex_set)
3744 regfree(&nickserv_conf.valid_nick_regex);
3746 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3747 nickserv_conf.valid_nick_regex_set = !err;
3748 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3750 nickserv_conf.valid_nick_regex_set = 0;
3752 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3754 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3755 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3756 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3757 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3758 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3759 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3760 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3761 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3762 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3763 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3764 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3765 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3766 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3767 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3768 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3769 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3770 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3771 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3772 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3773 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3774 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3775 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3776 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3777 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3778 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3780 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3781 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3782 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3784 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3785 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3786 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3788 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3789 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3790 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3791 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3792 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3793 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3794 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3795 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3796 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
3797 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
3798 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
3801 nickserv_conf.ounregister_flags = 0;
3803 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
3806 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
3808 if (!nickserv_conf.disable_nicks) {
3809 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3810 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3811 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3812 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3813 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3814 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3815 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3816 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3818 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3819 for (it=dict_first(child); it; it=iter_next(it)) {
3820 const char *key = iter_key(it), *value;
3824 if (!strncasecmp(key, "uc_", 3))
3825 flag = toupper(key[3]);
3826 else if (!strncasecmp(key, "lc_", 3))
3827 flag = tolower(key[3]);
3831 if ((pos = handle_inverse_flags[flag])) {
3832 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3833 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3836 if (nickserv_conf.weak_password_dict)
3837 dict_delete(nickserv_conf.weak_password_dict);
3838 nickserv_conf.weak_password_dict = dict_new();
3839 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3840 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3841 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3842 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3844 nickserv_load_dict(str);
3845 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3846 if (nickserv && str)
3847 NickChange(nickserv, str, 0);
3848 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3849 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3850 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3851 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3852 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3853 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3854 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3855 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3856 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3857 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3858 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3859 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3860 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3861 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3862 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3863 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3864 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3865 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3866 str = conf_get_data("server/network", RECDB_QSTRING);
3867 nickserv_conf.network_name = str ? str : "some IRC network";
3868 if (!nickserv_conf.auth_policer_params) {
3869 nickserv_conf.auth_policer_params = policer_params_new();
3870 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3871 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3873 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3874 for (it=dict_first(child); it; it=iter_next(it))
3875 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3879 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3881 char newnick[NICKLEN+1];
3890 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3892 case RECLAIM_SVSNICK:
3894 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3895 } while (GetUserH(newnick));
3896 irc_svsnick(nickserv, user, newnick);
3899 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3900 DelUser(user, nickserv, 1, msg);
3906 nickserv_reclaim_p(void *data) {
3907 struct userNode *user = data;
3908 struct nick_info *ni = get_nick_info(user->nick);
3910 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3914 check_user_nick(struct userNode *user) {
3915 struct nick_info *ni;
3916 user->modes &= ~FLAGS_REGNICK;
3917 if (!(ni = get_nick_info(user->nick)))
3919 if (user->handle_info == ni->owner) {
3920 user->modes |= FLAGS_REGNICK;
3924 if (nickserv_conf.warn_nick_owned)
3925 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3926 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3928 if (nickserv_conf.auto_reclaim_delay)
3929 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3931 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3936 handle_new_user(struct userNode *user)
3938 return check_user_nick(user);
3942 handle_account(struct userNode *user, const char *stamp)
3944 struct handle_info *hi;
3946 #ifdef WITH_PROTOCOL_P10
3947 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3949 hi = dict_find(nickserv_id_dict, stamp, NULL);
3953 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3956 set_user_handle_info(user, hi, 0);
3958 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3963 handle_nick_change(struct userNode *user, const char *old_nick)
3965 struct handle_info *hi;
3967 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3968 dict_remove(nickserv_allow_auth_dict, old_nick);
3969 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3971 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3972 check_user_nick(user);
3976 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3978 dict_remove(nickserv_allow_auth_dict, user->nick);
3979 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3980 set_user_handle_info(user, NULL, 0);
3983 static struct modcmd *
3984 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3986 if (min_level > 0) {
3988 sprintf(buf, "%u", min_level);
3989 if (must_be_qualified) {
3990 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3992 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3994 } else if (min_level == 0) {
3995 if (must_be_qualified) {
3996 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3998 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4001 if (must_be_qualified) {
4002 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4004 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4010 nickserv_db_cleanup(void)
4012 unreg_del_user_func(nickserv_remove_user);
4013 userList_clean(&curr_helpers);
4014 policer_params_delete(nickserv_conf.auth_policer_params);
4015 dict_delete(nickserv_handle_dict);
4016 dict_delete(nickserv_nick_dict);
4017 dict_delete(nickserv_opt_dict);
4018 dict_delete(nickserv_allow_auth_dict);
4019 dict_delete(nickserv_email_dict);
4020 dict_delete(nickserv_id_dict);
4021 dict_delete(nickserv_conf.weak_password_dict);
4022 free(auth_func_list);
4023 free(unreg_func_list);
4025 free(allowauth_func_list);
4026 free(handle_merge_func_list);
4027 free(failpw_func_list);
4028 if (nickserv_conf.valid_handle_regex_set)
4029 regfree(&nickserv_conf.valid_handle_regex);
4030 if (nickserv_conf.valid_nick_regex_set)
4031 regfree(&nickserv_conf.valid_nick_regex);
4035 init_nickserv(const char *nick)
4038 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4039 reg_new_user_func(handle_new_user);
4040 reg_nick_change_func(handle_nick_change);
4041 reg_del_user_func(nickserv_remove_user);
4042 reg_account_func(handle_account);
4044 /* set up handle_inverse_flags */
4045 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4046 for (i=0; handle_flags[i]; i++) {
4047 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4048 flag_access_levels[i] = 0;
4051 conf_register_reload(nickserv_conf_read);
4052 nickserv_opt_dict = dict_new();
4053 nickserv_email_dict = dict_new();
4054 dict_set_free_keys(nickserv_email_dict, free);
4055 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4057 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4058 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4059 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4060 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4061 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4062 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4063 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4064 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4065 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4066 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4067 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4068 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4069 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4070 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4071 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4072 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4073 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4074 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4075 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4076 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4077 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4078 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4079 if (!nickserv_conf.disable_nicks) {
4080 /* nick management commands */
4081 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4082 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4083 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4084 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4085 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4086 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4088 if (nickserv_conf.email_enabled) {
4089 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4090 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4091 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4092 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4093 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4095 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4096 /* miscellaneous commands */
4097 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4098 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4099 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4100 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4101 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4103 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4104 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4105 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4106 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4107 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4108 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4109 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4110 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4111 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4112 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4113 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4114 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4115 if (nickserv_conf.titlehost_suffix) {
4116 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4117 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4119 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4120 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4121 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4122 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4124 nickserv_handle_dict = dict_new();
4125 dict_set_free_keys(nickserv_handle_dict, free);
4126 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4128 nickserv_id_dict = dict_new();
4129 dict_set_free_keys(nickserv_id_dict, free);
4131 nickserv_nick_dict = dict_new();
4132 dict_set_free_data(nickserv_nick_dict, free);
4134 nickserv_allow_auth_dict = dict_new();
4136 userList_init(&curr_helpers);
4139 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4140 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4141 nickserv_service = service_register(nickserv);
4143 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4144 reg_exit_func(nickserv_db_cleanup);
4145 if(nickserv_conf.handle_expire_frequency)
4146 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4147 message_register_table(msgtab);