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 if(!strcmp(argv[2], "0"))
2791 else if(!(duration = ParseInterval(argv[2])))
2793 reply("MSG_INVALID_DURATION", argv[2]);
2796 if (duration > 2*365*86400) {
2797 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2800 unsplit_string(argv + 3, argc - 3, text);
2801 WALK_NOTES(hi, prev, note) {}
2802 id = prev ? (prev->id + 1) : 1;
2804 /* Create the new note structure. */
2805 note = calloc(1, sizeof(*note) + strlen(text));
2807 note->expires = duration ? (now + duration) : 0;
2810 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2811 strcpy(note->note, text);
2816 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2820 static NICKSERV_FUNC(cmd_delnote)
2822 struct handle_info *hi;
2823 struct handle_note *prev;
2824 struct handle_note *note;
2827 NICKSERV_MIN_PARMS(3);
2828 hi = get_victim_oper(user, argv[1]);
2831 id = strtoul(argv[2], NULL, 10);
2832 WALK_NOTES(hi, prev, note) {
2833 if (id == note->id) {
2835 prev->next = note->next;
2837 hi->notes = note->next;
2839 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2843 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2848 nickserv_saxdb_write(struct saxdb_context *ctx) {
2850 struct handle_info *hi;
2853 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2855 #ifdef WITH_PROTOCOL_BAHAMUT
2858 saxdb_start_record(ctx, iter_key(it), 0);
2860 struct handle_cookie *cookie = hi->cookie;
2863 switch (cookie->type) {
2864 case ACTIVATION: type = KEY_ACTIVATION; break;
2865 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2866 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2867 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2868 default: type = NULL; break;
2871 saxdb_start_record(ctx, KEY_COOKIE, 0);
2872 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2873 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2875 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2876 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2877 saxdb_end_record(ctx);
2881 struct handle_note *prev, *note;
2882 saxdb_start_record(ctx, KEY_NOTES, 0);
2883 WALK_NOTES(hi, prev, note) {
2884 snprintf(flags, sizeof(flags), "%d", note->id);
2885 saxdb_start_record(ctx, flags, 0);
2887 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
2888 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
2889 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
2890 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
2891 saxdb_end_record(ctx);
2893 saxdb_end_record(ctx);
2896 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2898 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2900 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2904 for (ii=flen=0; handle_flags[ii]; ++ii)
2905 if (hi->flags & (1 << ii))
2906 flags[flen++] = handle_flags[ii];
2908 saxdb_write_string(ctx, KEY_FLAGS, flags);
2910 #ifdef WITH_PROTOCOL_BAHAMUT
2911 saxdb_write_int(ctx, KEY_ID, hi->id);
2914 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2915 if (hi->last_quit_host[0])
2916 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2917 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2919 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
2920 if (hi->masks->used)
2921 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2923 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2925 struct string_list *slist;
2926 struct nick_info *ni;
2928 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2929 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2930 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2934 if (hi->opserv_level)
2935 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2936 if (hi->language != lang_C)
2937 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2938 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2939 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2940 if (hi->screen_width)
2941 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2942 if (hi->table_width)
2943 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2944 flags[0] = hi->userlist_style;
2946 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2947 saxdb_end_record(ctx);
2952 static handle_merge_func_t *handle_merge_func_list;
2953 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2956 reg_handle_merge_func(handle_merge_func_t func)
2958 if (handle_merge_func_used == handle_merge_func_size) {
2959 if (handle_merge_func_size) {
2960 handle_merge_func_size <<= 1;
2961 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2963 handle_merge_func_size = 8;
2964 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2967 handle_merge_func_list[handle_merge_func_used++] = func;
2970 static NICKSERV_FUNC(cmd_merge)
2972 struct handle_info *hi_from, *hi_to;
2973 struct userNode *last_user;
2974 struct userData *cList, *cListNext;
2975 unsigned int ii, jj, n;
2976 char buffer[MAXLEN];
2978 NICKSERV_MIN_PARMS(3);
2980 if (!(hi_from = get_victim_oper(user, argv[1])))
2982 if (!(hi_to = get_victim_oper(user, argv[2])))
2984 if (hi_to == hi_from) {
2985 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2989 for (n=0; n<handle_merge_func_used; n++)
2990 handle_merge_func_list[n](user, hi_to, hi_from);
2992 /* Append "from" handle's nicks to "to" handle's nick list. */
2994 struct nick_info *last_ni;
2995 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2996 last_ni->next = hi_from->nicks;
2998 while (hi_from->nicks) {
2999 hi_from->nicks->owner = hi_to;
3000 hi_from->nicks = hi_from->nicks->next;
3003 /* Merge the hostmasks. */
3004 for (ii=0; ii<hi_from->masks->used; ii++) {
3005 char *mask = hi_from->masks->list[ii];
3006 for (jj=0; jj<hi_to->masks->used; jj++)
3007 if (match_ircglobs(hi_to->masks->list[jj], mask))
3009 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3010 string_list_append(hi_to->masks, strdup(mask));
3013 /* Merge the lists of authed users. */
3015 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3016 last_user->next_authed = hi_from->users;
3018 hi_to->users = hi_from->users;
3020 /* Repoint the old "from" handle's users. */
3021 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3022 last_user->handle_info = hi_to;
3024 hi_from->users = NULL;
3026 /* Merge channel userlists. */
3027 for (cList=hi_from->channels; cList; cList=cListNext) {
3028 struct userData *cList2;
3029 cListNext = cList->u_next;
3030 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3031 if (cList->channel == cList2->channel)
3033 if (cList2 && (cList2->access >= cList->access)) {
3034 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);
3035 /* keep cList2 in hi_to; remove cList from hi_from */
3036 del_channel_user(cList, 1);
3039 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);
3040 /* remove the lower-ranking cList2 from hi_to */
3041 del_channel_user(cList2, 1);
3043 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3045 /* cList needs to be moved from hi_from to hi_to */
3046 cList->handle = hi_to;
3047 /* Remove from linked list for hi_from */
3048 assert(!cList->u_prev);
3049 hi_from->channels = cList->u_next;
3051 cList->u_next->u_prev = cList->u_prev;
3052 /* Add to linked list for hi_to */
3053 cList->u_prev = NULL;
3054 cList->u_next = hi_to->channels;
3055 if (hi_to->channels)
3056 hi_to->channels->u_prev = cList;
3057 hi_to->channels = cList;
3061 /* Do they get an OpServ level promotion? */
3062 if (hi_from->opserv_level > hi_to->opserv_level)
3063 hi_to->opserv_level = hi_from->opserv_level;
3065 /* What about last seen time? */
3066 if (hi_from->lastseen > hi_to->lastseen)
3067 hi_to->lastseen = hi_from->lastseen;
3069 /* New karma is the sum of the two original karmas. */
3070 hi_to->karma += hi_from->karma;
3072 /* Does a fakehost carry over? (This intentionally doesn't set it
3073 * for users previously attached to hi_to. They'll just have to
3076 if (hi_from->fakehost && !hi_to->fakehost)
3077 hi_to->fakehost = strdup(hi_from->fakehost);
3079 /* Notify of success. */
3080 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3081 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3082 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3084 /* Unregister the "from" handle. */
3085 nickserv_unregister_handle(hi_from, NULL);
3090 struct nickserv_discrim {
3091 unsigned long flags_on, flags_off;
3092 time_t min_registered, max_registered;
3095 int min_level, max_level;
3096 int min_karma, max_karma;
3097 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3098 const char *nickmask;
3099 const char *hostmask;
3100 const char *fakehostmask;
3101 const char *handlemask;
3102 const char *emailmask;
3105 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3107 struct discrim_apply_info {
3108 struct nickserv_discrim *discrim;
3109 discrim_search_func func;
3110 struct userNode *source;
3111 unsigned int matched;
3114 static struct nickserv_discrim *
3115 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3118 struct nickserv_discrim *discrim;
3120 discrim = malloc(sizeof(*discrim));
3121 memset(discrim, 0, sizeof(*discrim));
3122 discrim->min_level = 0;
3123 discrim->max_level = INT_MAX;
3124 discrim->limit = 50;
3125 discrim->min_registered = 0;
3126 discrim->max_registered = INT_MAX;
3127 discrim->lastseen = LONG_MAX;
3128 discrim->min_karma = INT_MIN;
3129 discrim->max_karma = INT_MAX;
3131 for (i=0; i<argc; i++) {
3132 if (i == argc - 1) {
3133 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3136 if (!irccasecmp(argv[i], "limit")) {
3137 discrim->limit = strtoul(argv[++i], NULL, 0);
3138 } else if (!irccasecmp(argv[i], "flags")) {
3139 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3140 } else if (!irccasecmp(argv[i], "registered")) {
3141 const char *cmp = argv[++i];
3142 if (cmp[0] == '<') {
3143 if (cmp[1] == '=') {
3144 discrim->min_registered = now - ParseInterval(cmp+2);
3146 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3148 } else if (cmp[0] == '=') {
3149 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3150 } else if (cmp[0] == '>') {
3151 if (cmp[1] == '=') {
3152 discrim->max_registered = now - ParseInterval(cmp+2);
3154 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3157 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3159 } else if (!irccasecmp(argv[i], "seen")) {
3160 discrim->lastseen = now - ParseInterval(argv[++i]);
3161 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3162 discrim->nickmask = argv[++i];
3163 } else if (!irccasecmp(argv[i], "hostmask")) {
3165 if (!irccasecmp(argv[i], "exact")) {
3166 if (i == argc - 1) {
3167 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3170 discrim->hostmask_type = EXACT;
3171 } else if (!irccasecmp(argv[i], "subset")) {
3172 if (i == argc - 1) {
3173 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3176 discrim->hostmask_type = SUBSET;
3177 } else if (!irccasecmp(argv[i], "superset")) {
3178 if (i == argc - 1) {
3179 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3182 discrim->hostmask_type = SUPERSET;
3183 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3184 if (i == argc - 1) {
3185 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3188 discrim->hostmask_type = LASTQUIT;
3191 discrim->hostmask_type = SUPERSET;
3193 discrim->hostmask = argv[++i];
3194 } else if (!irccasecmp(argv[i], "fakehost")) {
3195 if (!irccasecmp(argv[++i], "*")) {
3196 discrim->fakehostmask = 0;
3198 discrim->fakehostmask = argv[i];
3200 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3201 if (!irccasecmp(argv[++i], "*")) {
3202 discrim->handlemask = 0;
3204 discrim->handlemask = argv[i];
3206 } else if (!irccasecmp(argv[i], "email")) {
3207 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3208 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3210 } else if (!irccasecmp(argv[++i], "*")) {
3211 discrim->emailmask = 0;
3213 discrim->emailmask = argv[i];
3215 } else if (!irccasecmp(argv[i], "access")) {
3216 const char *cmp = argv[++i];
3217 if (cmp[0] == '<') {
3218 if (discrim->min_level == 0) discrim->min_level = 1;
3219 if (cmp[1] == '=') {
3220 discrim->max_level = strtoul(cmp+2, NULL, 0);
3222 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3224 } else if (cmp[0] == '=') {
3225 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3226 } else if (cmp[0] == '>') {
3227 if (cmp[1] == '=') {
3228 discrim->min_level = strtoul(cmp+2, NULL, 0);
3230 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3233 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3235 } else if (!irccasecmp(argv[i], "karma")) {
3236 const char *cmp = argv[++i];
3237 if (cmp[0] == '<') {
3238 if (cmp[1] == '=') {
3239 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3241 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3243 } else if (cmp[0] == '=') {
3244 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3245 } else if (cmp[0] == '>') {
3246 if (cmp[1] == '=') {
3247 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3249 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3252 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3255 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3266 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3268 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3269 || (discrim->flags_off & hi->flags)
3270 || (discrim->min_registered > hi->registered)
3271 || (discrim->max_registered < hi->registered)
3272 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3273 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3274 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3275 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3276 || (discrim->min_level > hi->opserv_level)
3277 || (discrim->max_level < hi->opserv_level)
3278 || (discrim->min_karma > hi->karma)
3279 || (discrim->max_karma < hi->karma)
3283 if (discrim->hostmask) {
3285 for (i=0; i<hi->masks->used; i++) {
3286 const char *mask = hi->masks->list[i];
3287 if ((discrim->hostmask_type == SUBSET)
3288 && (match_ircglobs(discrim->hostmask, mask))) break;
3289 else if ((discrim->hostmask_type == EXACT)
3290 && !irccasecmp(discrim->hostmask, mask)) break;
3291 else if ((discrim->hostmask_type == SUPERSET)
3292 && (match_ircglobs(mask, discrim->hostmask))) break;
3293 else if ((discrim->hostmask_type == LASTQUIT)
3294 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3296 if (i==hi->masks->used) return 0;
3298 if (discrim->nickmask) {
3299 struct nick_info *nick = hi->nicks;
3301 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3304 if (!nick) return 0;
3310 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3312 dict_iterator_t it, next;
3313 unsigned int matched;
3315 for (it = dict_first(nickserv_handle_dict), matched = 0;
3316 it && (matched < discrim->limit);
3318 next = iter_next(it);
3319 if (nickserv_discrim_match(discrim, iter_data(it))) {
3320 dsf(source, iter_data(it));
3328 search_print_func(struct userNode *source, struct handle_info *match)
3330 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3334 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3339 search_unregister_func (struct userNode *source, struct handle_info *match)
3341 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3342 nickserv_unregister_handle(match, source);
3346 nickserv_sort_accounts_by_access(const void *a, const void *b)
3348 const struct handle_info *hi_a = *(const struct handle_info**)a;
3349 const struct handle_info *hi_b = *(const struct handle_info**)b;
3350 if (hi_a->opserv_level != hi_b->opserv_level)
3351 return hi_b->opserv_level - hi_a->opserv_level;
3352 return irccasecmp(hi_a->handle, hi_b->handle);
3356 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3358 struct handle_info_list hil;
3359 struct helpfile_table tbl;
3364 memset(&hil, 0, sizeof(hil));
3365 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3366 struct handle_info *hi = iter_data(it);
3367 if (hi->opserv_level)
3368 handle_info_list_append(&hil, hi);
3370 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3371 tbl.length = hil.used + 1;
3373 tbl.flags = TABLE_NO_FREE;
3374 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3375 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3378 for (ii = 0; ii < hil.used; ) {
3379 ary = malloc(tbl.width * sizeof(ary[0]));
3380 ary[0] = hil.list[ii]->handle;
3381 ary[1] = strtab(hil.list[ii]->opserv_level);
3382 tbl.contents[++ii] = ary;
3384 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3385 reply("MSG_MATCH_COUNT", hil.used);
3386 for (ii = 0; ii < hil.used; ii++)
3387 free(tbl.contents[ii]);
3392 static NICKSERV_FUNC(cmd_search)
3394 struct nickserv_discrim *discrim;
3395 discrim_search_func action;
3396 struct svccmd *subcmd;
3397 unsigned int matches;
3400 NICKSERV_MIN_PARMS(3);
3401 sprintf(buf, "search %s", argv[1]);
3402 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3403 if (!irccasecmp(argv[1], "print"))
3404 action = search_print_func;
3405 else if (!irccasecmp(argv[1], "count"))
3406 action = search_count_func;
3407 else if (!irccasecmp(argv[1], "unregister"))
3408 action = search_unregister_func;
3410 reply("NSMSG_INVALID_ACTION", argv[1]);
3414 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3417 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3421 if (action == search_print_func)
3422 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3423 else if (action == search_count_func)
3424 discrim->limit = INT_MAX;
3426 matches = nickserv_discrim_search(discrim, action, user);
3429 reply("MSG_MATCH_COUNT", matches);
3431 reply("MSG_NO_MATCHES");
3437 static MODCMD_FUNC(cmd_checkpass)
3439 struct handle_info *hi;
3441 NICKSERV_MIN_PARMS(3);
3442 if (!(hi = get_handle_info(argv[1]))) {
3443 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3446 if (checkpass(argv[2], hi->passwd))
3447 reply("CHECKPASS_YES");
3449 reply("CHECKPASS_NO");
3455 nickserv_db_read_handle(const char *handle, dict_t obj)
3458 struct string_list *masks, *slist;
3459 struct handle_info *hi;
3460 struct userNode *authed_users;
3461 struct userData *channels;
3462 unsigned long int id;
3466 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3467 id = str ? strtoul(str, NULL, 0) : 0;
3468 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3470 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3473 if ((hi = get_handle_info(handle))) {
3474 authed_users = hi->users;
3475 channels = hi->channels;
3477 hi->channels = NULL;
3478 dict_remove(nickserv_handle_dict, hi->handle);
3480 authed_users = NULL;
3483 hi = register_handle(handle, str, id);
3485 hi->users = authed_users;
3486 while (authed_users) {
3487 authed_users->handle_info = hi;
3488 authed_users = authed_users->next_authed;
3491 hi->channels = channels;
3492 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3493 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3494 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3495 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3496 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3497 hi->language = language_find(str ? str : "C");
3498 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3499 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3500 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3502 hi->infoline = strdup(str);
3503 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3504 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3505 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3506 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3507 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3508 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3509 /* We want to read the nicks even if disable_nicks is set. This is so
3510 * that we don't lose the nick data entirely. */
3511 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3513 for (ii=0; ii<slist->used; ii++)
3514 register_nick(slist->list[ii], hi);
3516 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3518 for (ii=0; str[ii]; ii++)
3519 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3521 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3522 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3523 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3524 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3525 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3526 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3527 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3529 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3531 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3532 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3534 nickserv_set_email_addr(hi, str);
3535 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3537 hi->epithet = strdup(str);
3538 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3540 hi->fakehost = strdup(str);
3541 /* Read the "cookie" sub-database (if it exists). */
3542 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3544 const char *data, *type, *expires, *cookie_str;
3545 struct handle_cookie *cookie;
3547 cookie = calloc(1, sizeof(*cookie));
3548 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3549 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3550 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3551 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3552 if (!type || !expires || !cookie_str) {
3553 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3556 if (!irccasecmp(type, KEY_ACTIVATION))
3557 cookie->type = ACTIVATION;
3558 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3559 cookie->type = PASSWORD_CHANGE;
3560 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3561 cookie->type = EMAIL_CHANGE;
3562 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3563 cookie->type = ALLOWAUTH;
3565 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3568 cookie->expires = strtoul(expires, NULL, 0);
3569 if (cookie->expires < now)
3572 cookie->data = strdup(data);
3573 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3577 nickserv_bake_cookie(cookie);
3579 nickserv_free_cookie(cookie);
3581 /* Read the "notes" sub-database (if it exists). */
3582 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3585 struct handle_note *last_note;
3586 struct handle_note *note;
3589 for (it = dict_first(subdb); it; it = iter_next(it)) {
3590 const char *expires;
3598 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3600 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", id, hi->handle);
3603 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3604 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3605 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3606 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3607 if (!setter || !text || !set) {
3608 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", id, hi->handle);
3611 note = calloc(1, sizeof(*note) + strlen(text));
3613 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3614 note->set = strtoul(set, NULL, 10);
3615 note->id = strtoul(id, NULL, 10);
3616 safestrncpy(note->setter, setter, sizeof(note->setter));
3617 strcpy(note->note, text);
3619 last_note->next = note;
3628 nickserv_saxdb_read(dict_t db) {
3630 struct record_data *rd;
3632 for (it=dict_first(db); it; it=iter_next(it)) {
3634 nickserv_db_read_handle(iter_key(it), rd->d.object);
3639 static NICKSERV_FUNC(cmd_mergedb)
3641 struct timeval start, stop;
3644 NICKSERV_MIN_PARMS(2);
3645 gettimeofday(&start, NULL);
3646 if (!(db = parse_database(argv[1]))) {
3647 reply("NSMSG_DB_UNREADABLE", argv[1]);
3650 nickserv_saxdb_read(db);
3652 gettimeofday(&stop, NULL);
3653 stop.tv_sec -= start.tv_sec;
3654 stop.tv_usec -= start.tv_usec;
3655 if (stop.tv_usec < 0) {
3657 stop.tv_usec += 1000000;
3659 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3664 expire_handles(UNUSED_ARG(void *data))
3666 dict_iterator_t it, next;
3668 struct handle_info *hi;
3670 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3671 next = iter_next(it);
3673 if ((hi->opserv_level > 0)
3675 || HANDLE_FLAGGED(hi, FROZEN)
3676 || HANDLE_FLAGGED(hi, NODELETE)) {
3679 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3680 if ((now - hi->lastseen) > expiry) {
3681 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3682 nickserv_unregister_handle(hi, NULL);
3686 if (nickserv_conf.handle_expire_frequency)
3687 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3691 nickserv_load_dict(const char *fname)
3695 if (!(file = fopen(fname, "r"))) {
3696 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3699 while (!feof(file)) {
3700 fgets(line, sizeof(line), file);
3703 if (line[strlen(line)-1] == '\n')
3704 line[strlen(line)-1] = 0;
3705 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3708 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3711 static enum reclaim_action
3712 reclaim_action_from_string(const char *str) {
3714 return RECLAIM_NONE;
3715 else if (!irccasecmp(str, "warn"))
3716 return RECLAIM_WARN;
3717 else if (!irccasecmp(str, "svsnick"))
3718 return RECLAIM_SVSNICK;
3719 else if (!irccasecmp(str, "kill"))
3720 return RECLAIM_KILL;
3722 return RECLAIM_NONE;
3726 nickserv_conf_read(void)
3728 dict_t conf_node, child;
3732 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3733 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3736 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3738 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3739 if (nickserv_conf.valid_handle_regex_set)
3740 regfree(&nickserv_conf.valid_handle_regex);
3742 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3743 nickserv_conf.valid_handle_regex_set = !err;
3744 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3746 nickserv_conf.valid_handle_regex_set = 0;
3748 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3749 if (nickserv_conf.valid_nick_regex_set)
3750 regfree(&nickserv_conf.valid_nick_regex);
3752 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3753 nickserv_conf.valid_nick_regex_set = !err;
3754 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3756 nickserv_conf.valid_nick_regex_set = 0;
3758 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3760 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3761 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3762 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3763 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3764 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3765 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3766 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3767 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3768 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3769 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3770 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3771 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3772 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3773 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3774 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3775 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3776 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3777 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3778 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3779 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3780 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3781 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3782 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3783 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3784 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3786 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3787 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3788 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3790 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3791 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3792 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3794 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3795 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3796 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3797 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3798 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3799 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3800 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3801 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3802 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
3803 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
3804 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
3807 nickserv_conf.ounregister_flags = 0;
3809 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
3812 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
3814 if (!nickserv_conf.disable_nicks) {
3815 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3816 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3817 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3818 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3819 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3820 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3821 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3822 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3824 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3825 for (it=dict_first(child); it; it=iter_next(it)) {
3826 const char *key = iter_key(it), *value;
3830 if (!strncasecmp(key, "uc_", 3))
3831 flag = toupper(key[3]);
3832 else if (!strncasecmp(key, "lc_", 3))
3833 flag = tolower(key[3]);
3837 if ((pos = handle_inverse_flags[flag])) {
3838 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3839 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3842 if (nickserv_conf.weak_password_dict)
3843 dict_delete(nickserv_conf.weak_password_dict);
3844 nickserv_conf.weak_password_dict = dict_new();
3845 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3846 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3847 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3848 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3850 nickserv_load_dict(str);
3851 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3852 if (nickserv && str)
3853 NickChange(nickserv, str, 0);
3854 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3855 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3856 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3857 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3858 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3859 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3860 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3861 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3862 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3863 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3864 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3865 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3866 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3867 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3868 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3869 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3870 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3871 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3872 str = conf_get_data("server/network", RECDB_QSTRING);
3873 nickserv_conf.network_name = str ? str : "some IRC network";
3874 if (!nickserv_conf.auth_policer_params) {
3875 nickserv_conf.auth_policer_params = policer_params_new();
3876 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3877 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3879 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3880 for (it=dict_first(child); it; it=iter_next(it))
3881 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3885 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3887 char newnick[NICKLEN+1];
3896 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3898 case RECLAIM_SVSNICK:
3900 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3901 } while (GetUserH(newnick));
3902 irc_svsnick(nickserv, user, newnick);
3905 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3906 DelUser(user, nickserv, 1, msg);
3912 nickserv_reclaim_p(void *data) {
3913 struct userNode *user = data;
3914 struct nick_info *ni = get_nick_info(user->nick);
3916 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3920 check_user_nick(struct userNode *user) {
3921 struct nick_info *ni;
3922 user->modes &= ~FLAGS_REGNICK;
3923 if (!(ni = get_nick_info(user->nick)))
3925 if (user->handle_info == ni->owner) {
3926 user->modes |= FLAGS_REGNICK;
3930 if (nickserv_conf.warn_nick_owned)
3931 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3932 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3934 if (nickserv_conf.auto_reclaim_delay)
3935 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3937 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3942 handle_new_user(struct userNode *user)
3944 return check_user_nick(user);
3948 handle_account(struct userNode *user, const char *stamp)
3950 struct handle_info *hi;
3952 #ifdef WITH_PROTOCOL_P10
3953 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3955 hi = dict_find(nickserv_id_dict, stamp, NULL);
3959 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3962 set_user_handle_info(user, hi, 0);
3964 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3969 handle_nick_change(struct userNode *user, const char *old_nick)
3971 struct handle_info *hi;
3973 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3974 dict_remove(nickserv_allow_auth_dict, old_nick);
3975 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3977 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3978 check_user_nick(user);
3982 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3984 dict_remove(nickserv_allow_auth_dict, user->nick);
3985 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3986 set_user_handle_info(user, NULL, 0);
3989 static struct modcmd *
3990 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3992 if (min_level > 0) {
3994 sprintf(buf, "%u", min_level);
3995 if (must_be_qualified) {
3996 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3998 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4000 } else if (min_level == 0) {
4001 if (must_be_qualified) {
4002 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4004 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4007 if (must_be_qualified) {
4008 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4010 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4016 nickserv_db_cleanup(void)
4018 unreg_del_user_func(nickserv_remove_user);
4019 userList_clean(&curr_helpers);
4020 policer_params_delete(nickserv_conf.auth_policer_params);
4021 dict_delete(nickserv_handle_dict);
4022 dict_delete(nickserv_nick_dict);
4023 dict_delete(nickserv_opt_dict);
4024 dict_delete(nickserv_allow_auth_dict);
4025 dict_delete(nickserv_email_dict);
4026 dict_delete(nickserv_id_dict);
4027 dict_delete(nickserv_conf.weak_password_dict);
4028 free(auth_func_list);
4029 free(unreg_func_list);
4031 free(allowauth_func_list);
4032 free(handle_merge_func_list);
4033 free(failpw_func_list);
4034 if (nickserv_conf.valid_handle_regex_set)
4035 regfree(&nickserv_conf.valid_handle_regex);
4036 if (nickserv_conf.valid_nick_regex_set)
4037 regfree(&nickserv_conf.valid_nick_regex);
4041 init_nickserv(const char *nick)
4044 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4045 reg_new_user_func(handle_new_user);
4046 reg_nick_change_func(handle_nick_change);
4047 reg_del_user_func(nickserv_remove_user);
4048 reg_account_func(handle_account);
4050 /* set up handle_inverse_flags */
4051 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4052 for (i=0; handle_flags[i]; i++) {
4053 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4054 flag_access_levels[i] = 0;
4057 conf_register_reload(nickserv_conf_read);
4058 nickserv_opt_dict = dict_new();
4059 nickserv_email_dict = dict_new();
4060 dict_set_free_keys(nickserv_email_dict, free);
4061 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4063 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4064 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4065 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4066 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4067 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4068 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4069 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4070 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4071 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4072 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4073 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4074 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4075 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4076 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4077 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4078 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4079 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4080 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4081 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4082 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4083 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4084 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4085 if (!nickserv_conf.disable_nicks) {
4086 /* nick management commands */
4087 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4088 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4089 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4090 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4091 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4092 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4094 if (nickserv_conf.email_enabled) {
4095 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4096 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4097 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4098 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4099 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4101 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4102 /* miscellaneous commands */
4103 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4104 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4105 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4106 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4107 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4109 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4110 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4111 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4112 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4113 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4114 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4115 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4116 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4117 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4118 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4119 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4120 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4121 if (nickserv_conf.titlehost_suffix) {
4122 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4123 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4125 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4126 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4127 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4128 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4130 nickserv_handle_dict = dict_new();
4131 dict_set_free_keys(nickserv_handle_dict, free);
4132 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4134 nickserv_id_dict = dict_new();
4135 dict_set_free_keys(nickserv_id_dict, free);
4137 nickserv_nick_dict = dict_new();
4138 dict_set_free_data(nickserv_nick_dict, free);
4140 nickserv_allow_auth_dict = dict_new();
4142 userList_init(&curr_helpers);
4145 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4146 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4147 nickserv_service = service_register(nickserv);
4149 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4150 reg_exit_func(nickserv_db_cleanup);
4151 if(nickserv_conf.handle_expire_frequency)
4152 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4153 message_register_table(msgtab);