1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2006 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for gag_create(), opserv_bad_channel() */
33 # include "rx/rxposix.h"
36 #define NICKSERV_CONF_NAME "services/nickserv"
38 #define KEY_DISABLE_NICKS "disable_nicks"
39 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
40 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
41 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
42 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
43 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
44 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
45 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
46 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
47 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
48 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
49 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
50 #define KEY_MODOPER_LEVEL "modoper_level"
51 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
52 #define KEY_SET_TITLE_LEVEL "set_title_level"
53 #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
54 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
55 #define KEY_FLAG_LEVELS "flag_levels"
56 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
57 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
58 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
59 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
60 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
61 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
62 #define KEY_DICT_FILE "dict_file"
63 #define KEY_NICK "nick"
64 #define KEY_LANGUAGE "language"
65 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
66 #define KEY_AUTOGAG_DURATION "autogag_duration"
67 #define KEY_AUTH_POLICER "auth_policer"
68 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
69 #define KEY_EMAIL_ENABLED "email_enabled"
70 #define KEY_EMAIL_REQUIRED "email_required"
71 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
72 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
73 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
76 #define KEY_PASSWD "passwd"
77 #define KEY_NICKS "nicks"
78 #define KEY_MASKS "masks"
79 #define KEY_OPSERV_LEVEL "opserv_level"
80 #define KEY_FLAGS "flags"
81 #define KEY_REGISTER_ON "register"
82 #define KEY_LAST_SEEN "lastseen"
83 #define KEY_INFO "info"
84 #define KEY_USERLIST_STYLE "user_style"
85 #define KEY_SCREEN_WIDTH "screen_width"
86 #define KEY_LAST_AUTHED_HOST "last_authed_host"
87 #define KEY_LAST_QUIT_HOST "last_quit_host"
88 #define KEY_EMAIL_ADDR "email_addr"
89 #define KEY_COOKIE "cookie"
90 #define KEY_COOKIE_DATA "data"
91 #define KEY_COOKIE_TYPE "type"
92 #define KEY_COOKIE_EXPIRES "expires"
93 #define KEY_ACTIVATION "activation"
94 #define KEY_PASSWORD_CHANGE "password change"
95 #define KEY_EMAIL_CHANGE "email change"
96 #define KEY_ALLOWAUTH "allowauth"
97 #define KEY_EPITHET "epithet"
98 #define KEY_TABLE_WIDTH "table_width"
99 #define KEY_ANNOUNCEMENTS "announcements"
100 #define KEY_MAXLOGINS "maxlogins"
101 #define KEY_FAKEHOST "fakehost"
102 #define KEY_NOTES "notes"
103 #define KEY_NOTE_EXPIRES "expires"
104 #define KEY_NOTE_SET "set"
105 #define KEY_NOTE_SETTER "setter"
106 #define KEY_NOTE_NOTE "note"
107 #define KEY_KARMA "karma"
109 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
111 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
112 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
113 typedef OPTION_FUNC(option_func_t);
115 DEFINE_LIST(handle_info_list, struct handle_info*);
117 #define NICKSERV_MIN_PARMS(N) do { \
119 reply("MSG_MISSING_PARAMS", argv[0]); \
120 svccmd_send_help(user, nickserv, cmd); \
124 struct userNode *nickserv;
125 struct userList curr_helpers;
126 const char *handle_flags = HANDLE_FLAGS;
128 static struct module *nickserv_module;
129 static struct service *nickserv_service;
130 static struct log_type *NS_LOG;
131 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
132 static dict_t nickserv_id_dict; /* contains struct handle_info* */
133 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
134 static dict_t nickserv_opt_dict; /* contains option_func_t* */
135 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
136 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
137 static char handle_inverse_flags[256];
138 static unsigned int flag_access_levels[32];
139 static const struct message_entry msgtab[] = {
140 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
141 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
142 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
143 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
144 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
145 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
146 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
147 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
148 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
149 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
150 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
151 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
152 { "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." },
153 { "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." },
154 { "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." },
155 { "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." },
156 { "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." },
157 { "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." },
158 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
159 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
160 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
161 { "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." },
162 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
163 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
164 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
165 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
166 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
167 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
168 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
169 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
170 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
171 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
172 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
173 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
174 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
175 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
176 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
177 { "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)" },
178 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
179 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
180 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
181 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
182 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
183 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
184 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
185 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
186 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
187 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
188 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
189 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
190 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
191 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
192 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
193 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
194 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
195 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
196 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
197 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
198 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
199 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
200 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
201 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
202 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
203 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
204 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
205 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
206 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
207 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
208 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
209 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
210 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
211 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
212 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
213 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
214 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
215 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
216 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
217 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
218 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
219 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
220 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
221 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
222 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
223 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
224 { "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)." },
225 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
226 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
227 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
228 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
229 { "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." },
230 { "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." },
231 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
232 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
233 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
234 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
235 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
236 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
237 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
238 { "NSMSG_PASS_SUCCESS", "Password changed." },
239 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
240 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
241 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
242 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
243 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
244 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
245 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
246 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
247 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
248 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
249 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
250 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
251 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
252 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
253 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
254 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
255 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
256 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
257 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
258 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
259 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
260 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
261 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
262 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
263 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
264 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
265 { "NSMSG_NO_ACCESS", "Access denied." },
266 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
267 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
268 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
269 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
270 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
271 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
272 { "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." },
273 { "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." },
274 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
275 { "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." },
276 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
277 { "NSMSG_SEARCH_MATCH", "Match: %s" },
278 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
279 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
280 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
281 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
282 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
283 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
284 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
285 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
286 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
287 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
288 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
289 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
290 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
291 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
292 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
293 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
294 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
295 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
296 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
297 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
298 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
299 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
300 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
301 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
302 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
303 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
304 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
305 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
306 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
307 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
308 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
309 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
310 { "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." },
311 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
312 { "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." },
313 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
314 { "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." },
315 { "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." },
316 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
317 { "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." },
318 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
319 { "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." },
320 { "CHECKPASS_YES", "Yes." },
321 { "CHECKPASS_NO", "No." },
325 enum reclaim_action {
331 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
332 static void nickserv_reclaim_p(void *data);
333 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
336 unsigned int disable_nicks : 1;
337 unsigned int valid_handle_regex_set : 1;
338 unsigned int valid_nick_regex_set : 1;
339 unsigned int autogag_enabled : 1;
340 unsigned int email_enabled : 1;
341 unsigned int email_required : 1;
342 unsigned int default_hostmask : 1;
343 unsigned int warn_nick_owned : 1;
344 unsigned int warn_clone_auth : 1;
345 unsigned long nicks_per_handle;
346 unsigned long password_min_length;
347 unsigned long password_min_digits;
348 unsigned long password_min_upper;
349 unsigned long password_min_lower;
350 unsigned long db_backup_frequency;
351 unsigned long handle_expire_frequency;
352 unsigned long autogag_duration;
353 unsigned long email_visible_level;
354 unsigned long cookie_timeout;
355 unsigned long handle_expire_delay;
356 unsigned long nochan_handle_expire_delay;
357 unsigned long modoper_level;
358 unsigned long set_epithet_level;
359 unsigned long set_title_level;
360 unsigned long set_fakehost_level;
361 unsigned long handles_per_email;
362 unsigned long email_search_level;
363 const char *network_name;
364 const char *titlehost_suffix;
365 regex_t valid_handle_regex;
366 regex_t valid_nick_regex;
367 dict_t weak_password_dict;
368 struct policer_params *auth_policer_params;
369 enum reclaim_action reclaim_action;
370 enum reclaim_action auto_reclaim_action;
371 unsigned long auto_reclaim_delay;
372 unsigned char default_maxlogins;
373 unsigned char hard_maxlogins;
376 /* We have 2^32 unique account IDs to use. */
377 unsigned long int highest_id = 0;
379 #define WALK_NOTES(HANDLE, PREV, NOTE) \
380 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
381 if (NOTE->expires && NOTE->expires < now) { \
382 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
384 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
388 canonicalize_hostmask(char *mask)
390 char *out = mask, *temp;
391 if ((temp = strchr(mask, '!'))) {
393 while (*temp) *out++ = *temp++;
399 static struct handle_info *
400 register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
402 struct handle_info *hi;
404 #ifdef WITH_PROTOCOL_BAHAMUT
405 char id_base64[IDLEN + 1];
408 /* Assign a unique account ID to the account; note that 0 is
409 an invalid account ID. 1 is therefore the first account ID. */
411 id = 1 + highest_id++;
413 /* Note: highest_id is and must always be the highest ID. */
414 if (id > highest_id) {
418 inttobase64(id_base64, id, IDLEN);
420 /* Make sure an account with the same ID doesn't exist. If a
421 duplicate is found, log some details and assign a new one.
422 This should be impossible, but it never hurts to expect it. */
423 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
424 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
430 hi = calloc(1, sizeof(*hi));
431 hi->userlist_style = HI_DEFAULT_STYLE;
432 hi->announcements = '?';
433 hi->handle = strdup(handle);
434 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
436 dict_insert(nickserv_handle_dict, hi->handle, hi);
438 #ifdef WITH_PROTOCOL_BAHAMUT
440 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
447 register_nick(const char *nick, struct handle_info *owner)
449 struct nick_info *ni;
450 ni = malloc(sizeof(struct nick_info));
451 safestrncpy(ni->nick, nick, sizeof(ni->nick));
453 ni->next = owner->nicks;
455 dict_insert(nickserv_nick_dict, ni->nick, ni);
459 delete_nick(struct nick_info *ni)
461 struct nick_info *last, *next;
462 struct userNode *user;
463 /* Check to see if we should mark a user as unregistered. */
464 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
465 user->modes &= ~FLAGS_REGNICK;
468 /* Remove ni from the nick_info linked list. */
469 if (ni == ni->owner->nicks) {
470 ni->owner->nicks = ni->next;
472 last = ni->owner->nicks;
478 last->next = next->next;
480 dict_remove(nickserv_nick_dict, ni->nick);
483 static unreg_func_t *unreg_func_list;
484 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
487 reg_unreg_func(unreg_func_t func)
489 if (unreg_func_used == unreg_func_size) {
490 if (unreg_func_size) {
491 unreg_func_size <<= 1;
492 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
495 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
498 unreg_func_list[unreg_func_used++] = func;
502 nickserv_free_cookie(void *data)
504 struct handle_cookie *cookie = data;
505 if (cookie->hi) cookie->hi->cookie = NULL;
506 if (cookie->data) free(cookie->data);
511 free_handle_info(void *vhi)
513 struct handle_info *hi = vhi;
515 #ifdef WITH_PROTOCOL_BAHAMUT
518 inttobase64(id, hi->id, IDLEN);
519 dict_remove(nickserv_id_dict, id);
522 free_string_list(hi->masks);
526 delete_nick(hi->nicks);
531 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
532 nickserv_free_cookie(hi->cookie);
535 struct handle_note *note = hi->notes;
536 hi->notes = note->next;
539 if (hi->email_addr) {
540 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
541 handle_info_list_remove(hil, hi);
543 dict_remove(nickserv_email_dict, hi->email_addr);
548 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
551 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
555 for (n=0; n<unreg_func_used; n++)
556 unreg_func_list[n](notify, hi);
558 set_user_handle_info(hi->users, NULL, 0);
560 if (nickserv_conf.disable_nicks)
561 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
563 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
565 dict_remove(nickserv_handle_dict, hi->handle);
569 get_handle_info(const char *handle)
571 return dict_find(nickserv_handle_dict, handle, 0);
575 get_nick_info(const char *nick)
577 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
581 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
586 for (nn=0; nn<channel->members.used; ++nn) {
587 mn = channel->members.list[nn];
588 if ((mn->user != except) && (mn->user->handle_info == handle))
595 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
596 if (!user->handle_info) {
598 send_message(user, bot, "MSG_AUTHENTICATE");
602 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
604 send_message(user, bot, "NSMSG_NO_ACCESS");
608 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
610 send_message(user, bot, "MSG_OPER_SUSPENDED");
614 if (user->handle_info->opserv_level < min_level) {
616 send_message(user, bot, "NSMSG_NO_ACCESS");
624 is_valid_handle(const char *handle)
626 struct userNode *user;
627 /* cant register a juped nick/service nick as handle, to prevent confusion */
628 user = GetUserH(handle);
629 if (user && IsLocal(user))
631 /* check against maximum length */
632 if (strlen(handle) > NICKSERV_HANDLE_LEN)
634 /* for consistency, only allow account names that could be nicks */
635 if (!is_valid_nick(handle))
637 /* disallow account names that look like bad words */
638 if (opserv_bad_channel(handle))
640 /* test either regex or containing all valid chars */
641 if (nickserv_conf.valid_handle_regex_set) {
642 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
645 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
646 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
650 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
655 is_registerable_nick(const char *nick)
657 /* make sure it could be used as an account name */
658 if (!is_valid_handle(nick))
661 if (strlen(nick) > NICKLEN)
663 /* test either regex or as valid handle */
664 if (nickserv_conf.valid_nick_regex_set) {
665 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
668 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
669 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
677 is_valid_email_addr(const char *email)
679 return strchr(email, '@') != NULL;
683 visible_email_addr(struct userNode *user, struct handle_info *hi)
685 if (hi->email_addr) {
686 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
687 return hi->email_addr;
697 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
699 struct handle_info *hi;
700 struct userNode *target;
704 if (!(hi = get_handle_info(++name))) {
705 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
710 if (!(target = GetUserH(name))) {
711 send_message(user, service, "MSG_NICK_UNKNOWN", name);
714 if (IsLocal(target)) {
715 if (IsService(target))
716 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
718 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
721 if (!(hi = target->handle_info)) {
722 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
730 oper_outranks(struct userNode *user, struct handle_info *hi) {
731 if (user->handle_info->opserv_level > hi->opserv_level)
733 if (user->handle_info->opserv_level == hi->opserv_level) {
734 if ((user->handle_info->opserv_level == 1000)
735 || (user->handle_info == hi)
736 || ((user->handle_info->opserv_level == 0)
737 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
738 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
742 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
746 static struct handle_info *
747 get_victim_oper(struct userNode *user, const char *target)
749 struct handle_info *hi;
750 if (!(hi = smart_get_handle_info(nickserv, user, target)))
752 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
753 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
756 return oper_outranks(user, hi) ? hi : NULL;
760 valid_user_for(struct userNode *user, struct handle_info *hi)
764 /* If no hostmasks on the account, allow it. */
765 if (!hi->masks->used)
767 /* If any hostmask matches, allow it. */
768 for (ii=0; ii<hi->masks->used; ii++)
769 if (user_matches_glob(user, hi->masks->list[ii], 0))
771 /* If they are allowauthed to this account, allow it (removing the aa). */
772 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
773 dict_remove(nickserv_allow_auth_dict, user->nick);
776 /* The user is not allowed to use this account. */
781 is_secure_password(const char *handle, const char *pass, struct userNode *user)
784 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
788 if (len < nickserv_conf.password_min_length) {
790 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
793 if (!irccasecmp(pass, handle)) {
795 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
798 dict_find(nickserv_conf.weak_password_dict, pass, &p);
801 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
804 for (i=0; i<len; i++) {
805 if (isdigit(pass[i]))
807 if (isupper(pass[i]))
809 if (islower(pass[i]))
812 if ((cnt_lower < nickserv_conf.password_min_lower)
813 || (cnt_upper < nickserv_conf.password_min_upper)
814 || (cnt_digits < nickserv_conf.password_min_digits)) {
816 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
822 static auth_func_t *auth_func_list;
823 static unsigned int auth_func_size = 0, auth_func_used = 0;
826 reg_auth_func(auth_func_t func)
828 if (auth_func_used == auth_func_size) {
829 if (auth_func_size) {
830 auth_func_size <<= 1;
831 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
834 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
837 auth_func_list[auth_func_used++] = func;
840 static handle_rename_func_t *rf_list;
841 static unsigned int rf_list_size, rf_list_used;
844 reg_handle_rename_func(handle_rename_func_t func)
846 if (rf_list_used == rf_list_size) {
849 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
852 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
855 rf_list[rf_list_used++] = func;
859 generate_fakehost(struct handle_info *handle)
861 extern const char *hidden_host_suffix;
862 static char buffer[HOSTLEN+1];
864 if (!handle->fakehost) {
865 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
867 } else if (handle->fakehost[0] == '.') {
868 /* A leading dot indicates the stored value is actually a title. */
869 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
872 return handle->fakehost;
876 apply_fakehost(struct handle_info *handle)
878 struct userNode *target;
883 fake = generate_fakehost(handle);
884 for (target = handle->users; target; target = target->next_authed)
885 assign_fakehost(target, fake, 1);
889 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
892 struct handle_info *old_info;
894 /* This can happen if somebody uses COOKIE while authed, or if
895 * they re-auth to their current handle (which is silly, but users
897 if (user->handle_info == hi)
900 if (user->handle_info) {
901 struct userNode *other;
904 userList_remove(&curr_helpers, user);
906 /* remove from next_authed linked list */
907 if (user->handle_info->users == user) {
908 user->handle_info->users = user->next_authed;
910 for (other = user->handle_info->users;
911 other->next_authed != user;
912 other = other->next_authed) ;
913 other->next_authed = user->next_authed;
915 /* if nobody left on old handle, and they're not an oper, remove !god */
916 if (!user->handle_info->users && !user->handle_info->opserv_level)
917 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
918 /* record them as being last seen at this time */
919 user->handle_info->lastseen = now;
920 /* and record their hostmask */
921 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
923 old_info = user->handle_info;
924 user->handle_info = hi;
925 if (hi && !hi->users && !hi->opserv_level)
926 HANDLE_CLEAR_FLAG(hi, HELPING);
927 for (n=0; n<auth_func_used; n++)
928 auth_func_list[n](user, old_info);
930 struct nick_info *ni;
932 HANDLE_CLEAR_FLAG(hi, FROZEN);
933 if (nickserv_conf.warn_clone_auth) {
934 struct userNode *other;
935 for (other = hi->users; other; other = other->next_authed)
936 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
938 user->next_authed = hi->users;
942 userList_append(&curr_helpers, user);
944 if (hi->fakehost || old_info)
948 #ifdef WITH_PROTOCOL_BAHAMUT
949 /* Stamp users with their account ID. */
951 inttobase64(id, hi->id, IDLEN);
952 #elif WITH_PROTOCOL_P10
953 /* Stamp users with their account name. */
954 char *id = hi->handle;
956 const char *id = "???";
958 if (!nickserv_conf.disable_nicks) {
959 struct nick_info *ni;
960 for (ni = hi->nicks; ni; ni = ni->next) {
961 if (!irccasecmp(user->nick, ni->nick)) {
962 user->modes |= FLAGS_REGNICK;
970 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
971 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
973 /* We cannot clear the user's account ID, unfortunately. */
974 user->next_authed = NULL;
978 static struct handle_info*
979 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
981 struct handle_info *hi;
982 struct nick_info *ni;
983 char crypted[MD5_CRYPT_LENGTH];
985 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
986 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
990 if (!is_secure_password(handle, passwd, user))
993 cryptpass(passwd, crypted);
994 hi = register_handle(handle, crypted, 0);
995 hi->masks = alloc_string_list(1);
997 hi->language = lang_C;
998 hi->registered = now;
1000 hi->flags = HI_DEFAULT_FLAGS;
1001 if (settee && !no_auth)
1002 set_user_handle_info(settee, hi, 1);
1005 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1006 else if (nickserv_conf.disable_nicks)
1007 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1008 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1009 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1011 register_nick(user->nick, hi);
1012 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1014 if (settee && (user != settee))
1015 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1020 nickserv_bake_cookie(struct handle_cookie *cookie)
1022 cookie->hi->cookie = cookie;
1023 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1027 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1029 struct handle_cookie *cookie;
1030 char subject[128], body[4096], *misc;
1031 const char *netname, *fmt;
1035 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1039 cookie = calloc(1, sizeof(*cookie));
1041 cookie->type = type;
1042 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1043 cookie->expires = now + nickserv_conf.cookie_timeout;
1044 inttobase64(cookie->cookie, rand(), 5);
1045 inttobase64(cookie->cookie+5, rand(), 5);
1047 netname = nickserv_conf.network_name;
1050 switch (cookie->type) {
1052 hi->passwd[0] = 0; /* invalidate password */
1053 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1054 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1055 snprintf(subject, sizeof(subject), fmt, netname);
1056 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1057 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1060 case PASSWORD_CHANGE:
1061 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1062 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1063 snprintf(subject, sizeof(subject), fmt, netname);
1064 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1065 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1068 misc = hi->email_addr;
1069 hi->email_addr = cookie->data;
1071 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1072 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1073 snprintf(subject, sizeof(subject), fmt, netname);
1074 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1075 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1076 mail_send(nickserv, hi, subject, body, 1);
1077 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1078 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1080 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1081 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1082 snprintf(subject, sizeof(subject), fmt, netname);
1083 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1084 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1085 mail_send(nickserv, hi, subject, body, 1);
1088 hi->email_addr = misc;
1091 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1092 snprintf(subject, sizeof(subject), fmt, netname);
1093 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1094 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1095 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1098 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1102 mail_send(nickserv, hi, subject, body, first_time);
1103 nickserv_bake_cookie(cookie);
1107 nickserv_eat_cookie(struct handle_cookie *cookie)
1109 cookie->hi->cookie = NULL;
1110 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1111 nickserv_free_cookie(cookie);
1115 nickserv_free_email_addr(void *data)
1117 handle_info_list_clean(data);
1122 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1124 struct handle_info_list *hil;
1125 /* Remove from old handle_info_list ... */
1126 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1127 handle_info_list_remove(hil, hi);
1128 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1129 hi->email_addr = NULL;
1131 /* Add to the new list.. */
1132 if (new_email_addr) {
1133 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1134 hil = calloc(1, sizeof(*hil));
1135 hil->tag = strdup(new_email_addr);
1136 handle_info_list_init(hil);
1137 dict_insert(nickserv_email_dict, hil->tag, hil);
1139 handle_info_list_append(hil, hi);
1140 hi->email_addr = hil->tag;
1144 static NICKSERV_FUNC(cmd_register)
1147 struct handle_info *hi;
1148 const char *email_addr, *password;
1151 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1152 /* Require the first handle registered to belong to someone +o. */
1153 reply("NSMSG_REQUIRE_OPER");
1157 if (user->handle_info) {
1158 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1162 if (IsRegistering(user)) {
1163 reply("NSMSG_ALREADY_REGISTERING");
1167 if (IsStamped(user)) {
1168 /* Unauthenticated users might still have been stamped
1169 previously and could therefore have a hidden host;
1170 do not allow them to register a new account. */
1171 reply("NSMSG_STAMPED_REGISTER");
1175 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1177 if (!is_valid_handle(argv[1])) {
1178 reply("NSMSG_BAD_HANDLE", argv[1]);
1182 if ((argc >= 4) && nickserv_conf.email_enabled) {
1183 struct handle_info_list *hil;
1186 /* Remember email address. */
1187 email_addr = argv[3];
1189 /* Check that the email address looks valid.. */
1190 if (!is_valid_email_addr(email_addr)) {
1191 reply("NSMSG_BAD_EMAIL_ADDR");
1195 /* .. and that we are allowed to send to it. */
1196 if ((str = mail_prohibited_address(email_addr))) {
1197 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1201 /* If we do email verify, make sure we don't spam the address. */
1202 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1204 for (nn=0; nn<hil->used; nn++) {
1205 if (hil->list[nn]->cookie) {
1206 reply("NSMSG_EMAIL_UNACTIVATED");
1210 if (hil->used >= nickserv_conf.handles_per_email) {
1211 reply("NSMSG_EMAIL_OVERUSED");
1224 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1226 /* Add any masks they should get. */
1227 if (nickserv_conf.default_hostmask) {
1228 string_list_append(hi->masks, strdup("*@*"));
1230 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1231 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1232 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1235 /* If they're the first to register, give them level 1000. */
1236 if (dict_size(nickserv_handle_dict) == 1) {
1237 hi->opserv_level = 1000;
1238 reply("NSMSG_ROOT_HANDLE", argv[1]);
1241 /* Set their email address. */
1243 nickserv_set_email_addr(hi, email_addr);
1245 /* If they need to do email verification, tell them. */
1247 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1249 /* Set registering flag.. */
1250 user->modes |= FLAGS_REGISTERING;
1255 static NICKSERV_FUNC(cmd_oregister)
1258 struct userNode *settee;
1259 struct handle_info *hi;
1261 NICKSERV_MIN_PARMS(3);
1263 if (!is_valid_handle(argv[1])) {
1264 reply("NSMSG_BAD_HANDLE", argv[1]);
1271 } else if (strchr(argv[3], '@')) {
1272 mask = canonicalize_hostmask(strdup(argv[3]));
1274 settee = GetUserH(argv[4]);
1276 reply("MSG_NICK_UNKNOWN", argv[4]);
1283 } else if ((settee = GetUserH(argv[3]))) {
1284 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1286 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1289 if (settee && settee->handle_info) {
1290 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1294 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1299 string_list_append(hi->masks, mask);
1303 static NICKSERV_FUNC(cmd_handleinfo)
1306 unsigned int i, pos=0, herelen;
1307 struct userNode *target, *next_un;
1308 struct handle_info *hi;
1309 const char *nsmsg_none;
1312 if (!(hi = user->handle_info)) {
1313 reply("NSMSG_MUST_AUTH");
1316 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1320 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1321 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1322 #ifdef WITH_PROTOCOL_BAHAMUT
1323 reply("NSMSG_HANDLEINFO_ID", hi->id);
1325 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1328 intervalString(buff, now - hi->lastseen, user->handle_info);
1329 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1331 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1334 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1335 if (HANDLE_FLAGGED(hi, FROZEN))
1336 reply("NSMSG_HANDLEINFO_VACATION");
1338 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1339 struct do_not_register *dnr;
1340 if ((dnr = chanserv_is_dnr(NULL, hi)))
1341 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1342 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1344 } else if (hi != user->handle_info)
1348 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1350 if (nickserv_conf.email_enabled)
1351 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1355 switch (hi->cookie->type) {
1356 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1357 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1358 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1359 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1360 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1366 reply("NSMSG_HANDLEINFO_NO_NOTES");
1368 struct handle_note *prev, *note;
1370 WALK_NOTES(hi, prev, note) {
1371 char set_time[INTERVALLEN];
1372 intervalString(set_time, now - note->set, user->handle_info);
1373 if (note->expires) {
1374 char exp_time[INTERVALLEN];
1375 intervalString(exp_time, note->expires - now, user->handle_info);
1376 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1378 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1384 unsigned long flen = 1;
1385 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1387 for (i=0, flen=1; handle_flags[i]; i++)
1388 if (hi->flags & 1 << i)
1389 flags[flen++] = handle_flags[i];
1391 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1393 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1396 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1397 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1398 || (hi->opserv_level > 0)) {
1399 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1403 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1405 if (hi->last_quit_host[0])
1406 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1408 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1410 if (nickserv_conf.disable_nicks) {
1411 /* nicks disabled; don't show anything about registered nicks */
1412 } else if (hi->nicks) {
1413 struct nick_info *ni, *next_ni;
1414 for (ni = hi->nicks; ni; ni = next_ni) {
1415 herelen = strlen(ni->nick);
1416 if (pos + herelen + 1 > ArrayLength(buff)) {
1418 goto print_nicks_buff;
1422 memcpy(buff+pos, ni->nick, herelen);
1423 pos += herelen; buff[pos++] = ' ';
1427 reply("NSMSG_HANDLEINFO_NICKS", buff);
1432 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1435 if (hi->masks->used) {
1436 for (i=0; i < hi->masks->used; i++) {
1437 herelen = strlen(hi->masks->list[i]);
1438 if (pos + herelen + 1 > ArrayLength(buff)) {
1440 goto print_mask_buff;
1442 memcpy(buff+pos, hi->masks->list[i], herelen);
1443 pos += herelen; buff[pos++] = ' ';
1444 if (i+1 == hi->masks->used) {
1447 reply("NSMSG_HANDLEINFO_MASKS", buff);
1452 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1456 struct userData *channel, *next;
1459 for (channel = hi->channels; channel; channel = next) {
1460 next = channel->u_next;
1461 name = channel->channel->channel->name;
1462 herelen = strlen(name);
1463 if (pos + herelen + 7 > ArrayLength(buff)) {
1465 goto print_chans_buff;
1467 if (IsUserSuspended(channel))
1469 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1473 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1478 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1481 for (target = hi->users; target; target = next_un) {
1482 herelen = strlen(target->nick);
1483 if (pos + herelen + 1 > ArrayLength(buff)) {
1485 goto print_cnick_buff;
1487 next_un = target->next_authed;
1489 memcpy(buff+pos, target->nick, herelen);
1490 pos += herelen; buff[pos++] = ' ';
1494 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1502 static NICKSERV_FUNC(cmd_userinfo)
1504 struct userNode *target;
1506 NICKSERV_MIN_PARMS(2);
1507 if (!(target = GetUserH(argv[1]))) {
1508 reply("MSG_NICK_UNKNOWN", argv[1]);
1511 if (target->handle_info)
1512 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1514 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1518 static NICKSERV_FUNC(cmd_nickinfo)
1520 struct nick_info *ni;
1522 NICKSERV_MIN_PARMS(2);
1523 if (!(ni = get_nick_info(argv[1]))) {
1524 reply("MSG_NICK_UNKNOWN", argv[1]);
1527 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1531 static NICKSERV_FUNC(cmd_rename_handle)
1533 struct handle_info *hi;
1534 char msgbuf[MAXLEN], *old_handle;
1537 NICKSERV_MIN_PARMS(3);
1538 if (!(hi = get_victim_oper(user, argv[1])))
1540 if (!is_valid_handle(argv[2])) {
1541 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1544 if (get_handle_info(argv[2])) {
1545 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1549 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1550 hi->handle = strdup(argv[2]);
1551 dict_insert(nickserv_handle_dict, hi->handle, hi);
1552 for (nn=0; nn<rf_list_used; nn++)
1553 rf_list[nn](hi, old_handle);
1554 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1555 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1556 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1561 static failpw_func_t *failpw_func_list;
1562 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1565 reg_failpw_func(failpw_func_t func)
1567 if (failpw_func_used == failpw_func_size) {
1568 if (failpw_func_size) {
1569 failpw_func_size <<= 1;
1570 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1572 failpw_func_size = 8;
1573 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1576 failpw_func_list[failpw_func_used++] = func;
1579 static NICKSERV_FUNC(cmd_auth)
1581 int pw_arg, used, maxlogins;
1582 struct handle_info *hi;
1584 struct userNode *other;
1586 if (user->handle_info) {
1587 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1590 if (IsStamped(user)) {
1591 /* Unauthenticated users might still have been stamped
1592 previously and could therefore have a hidden host;
1593 do not allow them to authenticate. */
1594 reply("NSMSG_STAMPED_AUTH");
1598 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1600 } else if (argc == 2) {
1601 if (nickserv_conf.disable_nicks) {
1602 if (!(hi = get_handle_info(user->nick))) {
1603 reply("NSMSG_HANDLE_NOT_FOUND");
1607 /* try to look up their handle from their nick */
1608 struct nick_info *ni;
1609 ni = get_nick_info(user->nick);
1611 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1618 reply("MSG_MISSING_PARAMS", argv[0]);
1619 svccmd_send_help(user, nickserv, cmd);
1623 reply("NSMSG_HANDLE_NOT_FOUND");
1626 /* Responses from here on look up the language used by the handle they asked about. */
1627 passwd = argv[pw_arg];
1628 if (!valid_user_for(user, hi)) {
1629 if (hi->email_addr && nickserv_conf.email_enabled)
1630 send_message_type(4, user, cmd->parent->bot,
1631 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1634 send_message_type(4, user, cmd->parent->bot,
1635 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1637 argv[pw_arg] = "BADMASK";
1640 if (!checkpass(passwd, hi->passwd)) {
1642 send_message_type(4, user, cmd->parent->bot,
1643 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1644 argv[pw_arg] = "BADPASS";
1645 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1646 if (nickserv_conf.autogag_enabled) {
1647 if (!user->auth_policer.params) {
1648 user->auth_policer.last_req = now;
1649 user->auth_policer.params = nickserv_conf.auth_policer_params;
1651 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1653 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1654 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1655 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1657 argv[pw_arg] = "GAGGED";
1662 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1663 send_message_type(4, user, cmd->parent->bot,
1664 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1665 argv[pw_arg] = "SUSPENDED";
1668 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1669 for (used = 0, other = hi->users; other; other = other->next_authed) {
1670 if (++used >= maxlogins) {
1671 send_message_type(4, user, cmd->parent->bot,
1672 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1674 argv[pw_arg] = "MAXLOGINS";
1679 set_user_handle_info(user, hi, 1);
1680 if (nickserv_conf.email_required && !hi->email_addr)
1681 reply("NSMSG_PLEASE_SET_EMAIL");
1682 if (!is_secure_password(hi->handle, passwd, NULL))
1683 reply("NSMSG_WEAK_PASSWORD");
1684 if (hi->passwd[0] != '$')
1685 cryptpass(passwd, hi->passwd);
1686 if (!hi->masks->used) {
1688 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1689 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1690 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1692 argv[pw_arg] = "****";
1693 reply("NSMSG_AUTH_SUCCESS");
1697 static allowauth_func_t *allowauth_func_list;
1698 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1701 reg_allowauth_func(allowauth_func_t func)
1703 if (allowauth_func_used == allowauth_func_size) {
1704 if (allowauth_func_size) {
1705 allowauth_func_size <<= 1;
1706 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1708 allowauth_func_size = 8;
1709 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1712 allowauth_func_list[allowauth_func_used++] = func;
1715 static NICKSERV_FUNC(cmd_allowauth)
1717 struct userNode *target;
1718 struct handle_info *hi;
1721 NICKSERV_MIN_PARMS(2);
1722 if (!(target = GetUserH(argv[1]))) {
1723 reply("MSG_NICK_UNKNOWN", argv[1]);
1726 if (target->handle_info) {
1727 reply("NSMSG_USER_PREV_AUTH", target->nick);
1730 if (IsStamped(target)) {
1731 /* Unauthenticated users might still have been stamped
1732 previously and could therefore have a hidden host;
1733 do not allow them to authenticate to an account. */
1734 reply("NSMSG_USER_PREV_STAMP", target->nick);
1739 else if (!(hi = get_handle_info(argv[2]))) {
1740 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1744 if (hi->opserv_level > user->handle_info->opserv_level) {
1745 reply("MSG_USER_OUTRANKED", hi->handle);
1748 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1749 || (hi->opserv_level > 0))
1750 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1751 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1754 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1755 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1756 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1757 if (nickserv_conf.email_enabled)
1758 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1760 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1761 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1763 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1765 for (n=0; n<allowauth_func_used; n++)
1766 allowauth_func_list[n](user, target, hi);
1770 static NICKSERV_FUNC(cmd_authcookie)
1772 struct handle_info *hi;
1774 NICKSERV_MIN_PARMS(2);
1775 if (user->handle_info) {
1776 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1779 if (IsStamped(user)) {
1780 /* Unauthenticated users might still have been stamped
1781 previously and could therefore have a hidden host;
1782 do not allow them to authenticate to an account. */
1783 reply("NSMSG_STAMPED_AUTHCOOKIE");
1786 if (!(hi = get_handle_info(argv[1]))) {
1787 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1790 if (!hi->email_addr) {
1791 reply("MSG_SET_EMAIL_ADDR");
1794 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1798 static NICKSERV_FUNC(cmd_delcookie)
1800 struct handle_info *hi;
1802 hi = user->handle_info;
1804 reply("NSMSG_NO_COOKIE");
1807 switch (hi->cookie->type) {
1810 reply("NSMSG_MUST_TIME_OUT");
1813 nickserv_eat_cookie(hi->cookie);
1814 reply("NSMSG_ATE_COOKIE");
1820 static NICKSERV_FUNC(cmd_resetpass)
1822 struct handle_info *hi;
1823 char crypted[MD5_CRYPT_LENGTH];
1825 NICKSERV_MIN_PARMS(3);
1826 if (user->handle_info) {
1827 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1830 if (IsStamped(user)) {
1831 /* Unauthenticated users might still have been stamped
1832 previously and could therefore have a hidden host;
1833 do not allow them to activate an account. */
1834 reply("NSMSG_STAMPED_RESETPASS");
1837 if (!(hi = get_handle_info(argv[1]))) {
1838 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1841 if (!hi->email_addr) {
1842 reply("MSG_SET_EMAIL_ADDR");
1845 cryptpass(argv[2], crypted);
1847 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1851 static NICKSERV_FUNC(cmd_cookie)
1853 struct handle_info *hi;
1856 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1859 NICKSERV_MIN_PARMS(3);
1860 if (!(hi = get_handle_info(argv[1]))) {
1861 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1867 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1868 reply("NSMSG_HANDLE_SUSPENDED");
1873 reply("NSMSG_NO_COOKIE");
1877 /* Check validity of operation before comparing cookie to
1878 * prohibit guessing by authed users. */
1879 if (user->handle_info
1880 && (hi->cookie->type != EMAIL_CHANGE)
1881 && (hi->cookie->type != PASSWORD_CHANGE)) {
1882 reply("NSMSG_CANNOT_COOKIE");
1886 if (strcmp(cookie, hi->cookie->cookie)) {
1887 reply("NSMSG_BAD_COOKIE");
1891 switch (hi->cookie->type) {
1893 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1894 set_user_handle_info(user, hi, 1);
1895 reply("NSMSG_HANDLE_ACTIVATED");
1897 case PASSWORD_CHANGE:
1898 set_user_handle_info(user, hi, 1);
1899 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1900 reply("NSMSG_PASSWORD_CHANGED");
1903 nickserv_set_email_addr(hi, hi->cookie->data);
1904 reply("NSMSG_EMAIL_CHANGED");
1907 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1908 set_user_handle_info(user, hi, 1);
1909 nickserv_addmask(user, hi, mask);
1910 reply("NSMSG_AUTH_SUCCESS");
1915 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1916 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1920 nickserv_eat_cookie(hi->cookie);
1925 static NICKSERV_FUNC(cmd_oregnick) {
1927 struct handle_info *target;
1928 struct nick_info *ni;
1930 NICKSERV_MIN_PARMS(3);
1931 if (!(target = modcmd_get_handle_info(user, argv[1])))
1934 if (!is_registerable_nick(nick)) {
1935 reply("NSMSG_BAD_NICK", nick);
1938 ni = dict_find(nickserv_nick_dict, nick, NULL);
1940 reply("NSMSG_NICK_EXISTS", nick);
1943 register_nick(nick, target);
1944 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1948 static NICKSERV_FUNC(cmd_regnick) {
1950 struct nick_info *ni;
1952 if (!is_registerable_nick(user->nick)) {
1953 reply("NSMSG_BAD_NICK", user->nick);
1956 /* count their nicks, see if it's too many */
1957 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1958 if (n >= nickserv_conf.nicks_per_handle) {
1959 reply("NSMSG_TOO_MANY_NICKS");
1962 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1964 reply("NSMSG_NICK_EXISTS", user->nick);
1967 register_nick(user->nick, user->handle_info);
1968 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1972 static NICKSERV_FUNC(cmd_pass)
1974 struct handle_info *hi;
1975 const char *old_pass, *new_pass;
1977 NICKSERV_MIN_PARMS(3);
1978 hi = user->handle_info;
1982 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1983 if (!checkpass(old_pass, hi->passwd)) {
1984 argv[1] = "BADPASS";
1985 reply("NSMSG_PASSWORD_INVALID");
1988 cryptpass(new_pass, hi->passwd);
1990 reply("NSMSG_PASS_SUCCESS");
1995 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1998 char *new_mask = canonicalize_hostmask(strdup(mask));
1999 for (i=0; i<hi->masks->used; i++) {
2000 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2001 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2006 string_list_append(hi->masks, new_mask);
2007 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2011 static NICKSERV_FUNC(cmd_addmask)
2014 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2015 int res = nickserv_addmask(user, user->handle_info, mask);
2019 if (!is_gline(argv[1])) {
2020 reply("NSMSG_MASK_INVALID", argv[1]);
2023 return nickserv_addmask(user, user->handle_info, argv[1]);
2027 static NICKSERV_FUNC(cmd_oaddmask)
2029 struct handle_info *hi;
2031 NICKSERV_MIN_PARMS(3);
2032 if (!(hi = get_victim_oper(user, argv[1])))
2034 return nickserv_addmask(user, hi, argv[2]);
2038 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2041 for (i=0; i<hi->masks->used; i++) {
2042 if (!strcmp(del_mask, hi->masks->list[i])) {
2043 char *old_mask = hi->masks->list[i];
2044 if (hi->masks->used == 1 && !force) {
2045 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2048 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2049 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2054 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2058 static NICKSERV_FUNC(cmd_delmask)
2060 NICKSERV_MIN_PARMS(2);
2061 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2064 static NICKSERV_FUNC(cmd_odelmask)
2066 struct handle_info *hi;
2067 NICKSERV_MIN_PARMS(3);
2068 if (!(hi = get_victim_oper(user, argv[1])))
2070 return nickserv_delmask(user, hi, argv[2], 1);
2074 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2075 unsigned int nn, add = 1, pos;
2076 unsigned long added, removed, flag;
2078 for (added=removed=nn=0; str[nn]; nn++) {
2080 case '+': add = 1; break;
2081 case '-': add = 0; break;
2083 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2084 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2087 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2088 /* cheesy avoidance of looking up the flag name.. */
2089 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2092 flag = 1 << (pos - 1);
2094 added |= flag, removed &= ~flag;
2096 removed |= flag, added &= ~flag;
2101 *premoved = removed;
2106 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2108 unsigned long before, after, added, removed;
2109 struct userNode *uNode;
2111 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2112 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2114 hi->flags = (hi->flags | added) & ~removed;
2115 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2117 /* Strip helping flag if they're only a support helper and not
2118 * currently in #support. */
2119 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2120 struct channelList *schannels;
2122 schannels = chanserv_support_channels();
2123 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2124 for (ii = 0; ii < schannels->used; ++ii)
2125 if (GetUserMode(schannels->list[ii], uNode))
2127 if (ii < schannels->used)
2131 HANDLE_CLEAR_FLAG(hi, HELPING);
2134 if (after && !before) {
2135 /* Add user to current helper list. */
2136 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2137 userList_append(&curr_helpers, uNode);
2138 } else if (!after && before) {
2139 /* Remove user from current helper list. */
2140 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2141 userList_remove(&curr_helpers, uNode);
2148 set_list(struct userNode *user, struct handle_info *hi, int override)
2152 char *set_display[] = {
2153 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2154 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2157 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2159 /* Do this so options are presented in a consistent order. */
2160 for (i = 0; i < ArrayLength(set_display); ++i)
2161 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2162 opt(user, hi, override, 0, NULL);
2165 static NICKSERV_FUNC(cmd_set)
2167 struct handle_info *hi;
2170 hi = user->handle_info;
2172 set_list(user, hi, 0);
2175 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2176 reply("NSMSG_INVALID_OPTION", argv[1]);
2179 return opt(user, hi, 0, argc-1, argv+1);
2182 static NICKSERV_FUNC(cmd_oset)
2184 struct handle_info *hi;
2187 NICKSERV_MIN_PARMS(2);
2189 if (!(hi = get_victim_oper(user, argv[1])))
2193 set_list(user, hi, 0);
2197 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2198 reply("NSMSG_INVALID_OPTION", argv[2]);
2202 return opt(user, hi, 1, argc-2, argv+2);
2205 static OPTION_FUNC(opt_info)
2209 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2211 hi->infoline = NULL;
2213 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2217 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2218 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2222 static OPTION_FUNC(opt_width)
2225 hi->screen_width = strtoul(argv[1], NULL, 0);
2227 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2228 hi->screen_width = MIN_LINE_SIZE;
2229 else if (hi->screen_width > MAX_LINE_SIZE)
2230 hi->screen_width = MAX_LINE_SIZE;
2232 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2236 static OPTION_FUNC(opt_tablewidth)
2239 hi->table_width = strtoul(argv[1], NULL, 0);
2241 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2242 hi->table_width = MIN_LINE_SIZE;
2243 else if (hi->screen_width > MAX_LINE_SIZE)
2244 hi->table_width = MAX_LINE_SIZE;
2246 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2250 static OPTION_FUNC(opt_color)
2253 if (enabled_string(argv[1]))
2254 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2255 else if (disabled_string(argv[1]))
2256 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2258 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2263 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2267 static OPTION_FUNC(opt_privmsg)
2270 if (enabled_string(argv[1]))
2271 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2272 else if (disabled_string(argv[1]))
2273 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2275 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2280 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2284 static OPTION_FUNC(opt_style)
2289 if (!irccasecmp(argv[1], "Zoot"))
2290 hi->userlist_style = HI_STYLE_ZOOT;
2291 else if (!irccasecmp(argv[1], "def"))
2292 hi->userlist_style = HI_STYLE_DEF;
2295 switch (hi->userlist_style) {
2304 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2308 static OPTION_FUNC(opt_announcements)
2313 if (enabled_string(argv[1]))
2314 hi->announcements = 'y';
2315 else if (disabled_string(argv[1]))
2316 hi->announcements = 'n';
2317 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2318 hi->announcements = '?';
2320 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2325 switch (hi->announcements) {
2326 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2327 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2328 case '?': choice = "default"; break;
2329 default: choice = "unknown"; break;
2331 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2335 static OPTION_FUNC(opt_password)
2338 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2343 cryptpass(argv[1], hi->passwd);
2345 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2349 static OPTION_FUNC(opt_flags)
2352 unsigned int ii, flen;
2355 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2360 nickserv_apply_flags(user, hi, argv[1]);
2362 for (ii = flen = 0; handle_flags[ii]; ii++)
2363 if (hi->flags & (1 << ii))
2364 flags[flen++] = handle_flags[ii];
2367 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2369 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2373 static OPTION_FUNC(opt_email)
2377 if (!is_valid_email_addr(argv[1])) {
2378 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2381 if ((str = mail_prohibited_address(argv[1]))) {
2382 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2385 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2386 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2388 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2390 nickserv_set_email_addr(hi, argv[1]);
2392 nickserv_eat_cookie(hi->cookie);
2393 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2396 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2400 static OPTION_FUNC(opt_maxlogins)
2402 unsigned char maxlogins;
2404 maxlogins = strtoul(argv[1], NULL, 0);
2405 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2406 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2409 hi->maxlogins = maxlogins;
2411 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2412 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2416 static OPTION_FUNC(opt_language)
2418 struct language *lang;
2420 lang = language_find(argv[1]);
2421 if (irccasecmp(lang->name, argv[1]))
2422 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2423 hi->language = lang;
2425 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2429 static OPTION_FUNC(opt_karma)
2432 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2437 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2438 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2439 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2440 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2442 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2446 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2451 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2452 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2454 if ((user->handle_info->opserv_level < target->opserv_level)
2455 || ((user->handle_info->opserv_level == target->opserv_level)
2456 && (user->handle_info->opserv_level < 1000))) {
2457 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2460 if ((user->handle_info->opserv_level < new_level)
2461 || ((user->handle_info->opserv_level == new_level)
2462 && (user->handle_info->opserv_level < 1000))) {
2463 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2466 if (user->handle_info == target) {
2467 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2470 if (target->opserv_level == new_level)
2472 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2473 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2474 target->opserv_level = new_level;
2478 static OPTION_FUNC(opt_level)
2483 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2487 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2488 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2492 static OPTION_FUNC(opt_epithet)
2495 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2499 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2500 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2503 if ((epithet[0] == '*') && !epithet[1])
2506 hi->epithet = strdup(epithet);
2510 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2512 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2516 static OPTION_FUNC(opt_title)
2521 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2525 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2527 if (strchr(title, '.')) {
2528 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2531 if ((strlen(user->handle_info->handle) + strlen(title) +
2532 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2533 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2538 if (!strcmp(title, "*")) {
2539 hi->fakehost = NULL;
2541 hi->fakehost = malloc(strlen(title)+2);
2542 hi->fakehost[0] = '.';
2543 strcpy(hi->fakehost+1, title);
2546 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2547 title = hi->fakehost + 1;
2551 title = user_find_message(user, "MSG_NONE");
2552 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2556 static OPTION_FUNC(opt_fakehost)
2561 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2565 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2567 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2568 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2572 if (!strcmp(fake, "*"))
2573 hi->fakehost = NULL;
2575 hi->fakehost = strdup(fake);
2576 fake = hi->fakehost;
2579 fake = generate_fakehost(hi);
2581 fake = user_find_message(user, "MSG_NONE");
2582 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2586 static NICKSERV_FUNC(cmd_reclaim)
2588 struct handle_info *hi;
2589 struct nick_info *ni;
2590 struct userNode *victim;
2592 NICKSERV_MIN_PARMS(2);
2593 hi = user->handle_info;
2594 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2596 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2599 if (ni->owner != user->handle_info) {
2600 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2603 victim = GetUserH(ni->nick);
2605 reply("MSG_NICK_UNKNOWN", ni->nick);
2608 if (victim == user) {
2609 reply("NSMSG_NICK_USER_YOU");
2612 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2613 switch (nickserv_conf.reclaim_action) {
2614 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2615 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2616 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2617 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2622 static NICKSERV_FUNC(cmd_unregnick)
2625 struct handle_info *hi;
2626 struct nick_info *ni;
2628 hi = user->handle_info;
2629 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2630 ni = dict_find(nickserv_nick_dict, nick, NULL);
2632 reply("NSMSG_UNKNOWN_NICK", nick);
2635 if (hi != ni->owner) {
2636 reply("NSMSG_NOT_YOUR_NICK", nick);
2639 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2644 static NICKSERV_FUNC(cmd_ounregnick)
2646 struct nick_info *ni;
2648 NICKSERV_MIN_PARMS(2);
2649 if (!(ni = get_nick_info(argv[1]))) {
2650 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2653 if (!oper_outranks(user, ni->owner))
2655 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2660 static NICKSERV_FUNC(cmd_unregister)
2662 struct handle_info *hi;
2665 NICKSERV_MIN_PARMS(2);
2666 hi = user->handle_info;
2669 if (checkpass(passwd, hi->passwd)) {
2670 nickserv_unregister_handle(hi, user);
2673 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2674 reply("NSMSG_PASSWORD_INVALID");
2679 static NICKSERV_FUNC(cmd_ounregister)
2681 struct handle_info *hi;
2682 char reason[MAXLEN];
2684 NICKSERV_MIN_PARMS(2);
2685 if (!(hi = get_victim_oper(user, argv[1])))
2687 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2688 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2689 nickserv_unregister_handle(hi, user);
2693 static NICKSERV_FUNC(cmd_status)
2695 if (nickserv_conf.disable_nicks) {
2696 reply("NSMSG_GLOBAL_STATS_NONICK",
2697 dict_size(nickserv_handle_dict));
2699 if (user->handle_info) {
2701 struct nick_info *ni;
2702 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2703 reply("NSMSG_HANDLE_STATS", cnt);
2705 reply("NSMSG_HANDLE_NONE");
2707 reply("NSMSG_GLOBAL_STATS",
2708 dict_size(nickserv_handle_dict),
2709 dict_size(nickserv_nick_dict));
2714 static NICKSERV_FUNC(cmd_ghost)
2716 struct userNode *target;
2717 char reason[MAXLEN];
2719 NICKSERV_MIN_PARMS(2);
2720 if (!(target = GetUserH(argv[1]))) {
2721 reply("MSG_NICK_UNKNOWN", argv[1]);
2724 if (target == user) {
2725 reply("NSMSG_CANNOT_GHOST_SELF");
2728 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2729 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2732 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2733 DelUser(target, nickserv, 1, reason);
2734 reply("NSMSG_GHOST_KILLED", argv[1]);
2738 static NICKSERV_FUNC(cmd_vacation)
2740 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2741 reply("NSMSG_ON_VACATION");
2745 static NICKSERV_FUNC(cmd_addnote)
2747 struct handle_info *hi;
2748 unsigned long duration;
2751 struct handle_note *prev;
2752 struct handle_note *note;
2754 /* Parse parameters and figure out values for note's fields. */
2755 NICKSERV_MIN_PARMS(4);
2756 hi = get_victim_oper(user, argv[1]);
2759 duration = ParseInterval(argv[2]);
2760 if (duration > 2*365*86400) {
2761 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2764 unsplit_string(argv + 3, argc - 3, text);
2765 WALK_NOTES(hi, prev, note) {}
2766 id = prev ? (prev->id + 1) : 1;
2768 /* Create the new note structure. */
2769 note = calloc(1, sizeof(*note) + strlen(text));
2771 note->expires = duration ? (now + duration) : 0;
2774 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2775 strcpy(note->note, text);
2780 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2784 static NICKSERV_FUNC(cmd_delnote)
2786 struct handle_info *hi;
2787 struct handle_note *prev;
2788 struct handle_note *note;
2791 NICKSERV_MIN_PARMS(3);
2792 hi = get_victim_oper(user, argv[1]);
2795 id = strtoul(argv[2], NULL, 10);
2796 WALK_NOTES(hi, prev, note) {
2797 if (id == note->id) {
2799 prev->next = note->next;
2801 hi->notes = note->next;
2803 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2807 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2812 nickserv_saxdb_write(struct saxdb_context *ctx) {
2814 struct handle_info *hi;
2817 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2819 #ifdef WITH_PROTOCOL_BAHAMUT
2822 saxdb_start_record(ctx, iter_key(it), 0);
2823 if (hi->announcements != '?') {
2824 flags[0] = hi->announcements;
2826 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2829 struct handle_cookie *cookie = hi->cookie;
2832 switch (cookie->type) {
2833 case ACTIVATION: type = KEY_ACTIVATION; break;
2834 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2835 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2836 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2837 default: type = NULL; break;
2840 saxdb_start_record(ctx, KEY_COOKIE, 0);
2841 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2842 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2844 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2845 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2846 saxdb_end_record(ctx);
2850 struct handle_note *prev, *note;
2851 saxdb_start_record(ctx, KEY_NOTES, 0);
2852 WALK_NOTES(hi, prev, note) {
2853 snprintf(flags, sizeof(flags), "%d", note->id);
2854 saxdb_start_record(ctx, flags, 0);
2856 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
2857 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
2858 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
2859 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
2860 saxdb_end_record(ctx);
2862 saxdb_end_record(ctx);
2865 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2867 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2869 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2873 for (ii=flen=0; handle_flags[ii]; ++ii)
2874 if (hi->flags & (1 << ii))
2875 flags[flen++] = handle_flags[ii];
2877 saxdb_write_string(ctx, KEY_FLAGS, flags);
2879 #ifdef WITH_PROTOCOL_BAHAMUT
2880 saxdb_write_int(ctx, KEY_ID, hi->id);
2883 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2884 if (hi->last_quit_host[0])
2885 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2886 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2888 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
2889 if (hi->masks->used)
2890 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2892 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2894 struct string_list *slist;
2895 struct nick_info *ni;
2897 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2898 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2899 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2903 if (hi->opserv_level)
2904 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2905 if (hi->language != lang_C)
2906 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2907 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2908 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2909 if (hi->screen_width)
2910 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2911 if (hi->table_width)
2912 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2913 flags[0] = hi->userlist_style;
2915 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2916 saxdb_end_record(ctx);
2921 static handle_merge_func_t *handle_merge_func_list;
2922 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2925 reg_handle_merge_func(handle_merge_func_t func)
2927 if (handle_merge_func_used == handle_merge_func_size) {
2928 if (handle_merge_func_size) {
2929 handle_merge_func_size <<= 1;
2930 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2932 handle_merge_func_size = 8;
2933 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2936 handle_merge_func_list[handle_merge_func_used++] = func;
2939 static NICKSERV_FUNC(cmd_merge)
2941 struct handle_info *hi_from, *hi_to;
2942 struct userNode *last_user;
2943 struct userData *cList, *cListNext;
2944 unsigned int ii, jj, n;
2945 char buffer[MAXLEN];
2947 NICKSERV_MIN_PARMS(3);
2949 if (!(hi_from = get_victim_oper(user, argv[1])))
2951 if (!(hi_to = get_victim_oper(user, argv[2])))
2953 if (hi_to == hi_from) {
2954 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2958 for (n=0; n<handle_merge_func_used; n++)
2959 handle_merge_func_list[n](user, hi_to, hi_from);
2961 /* Append "from" handle's nicks to "to" handle's nick list. */
2963 struct nick_info *last_ni;
2964 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2965 last_ni->next = hi_from->nicks;
2967 while (hi_from->nicks) {
2968 hi_from->nicks->owner = hi_to;
2969 hi_from->nicks = hi_from->nicks->next;
2972 /* Merge the hostmasks. */
2973 for (ii=0; ii<hi_from->masks->used; ii++) {
2974 char *mask = hi_from->masks->list[ii];
2975 for (jj=0; jj<hi_to->masks->used; jj++)
2976 if (match_ircglobs(hi_to->masks->list[jj], mask))
2978 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2979 string_list_append(hi_to->masks, strdup(mask));
2982 /* Merge the lists of authed users. */
2984 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2985 last_user->next_authed = hi_from->users;
2987 hi_to->users = hi_from->users;
2989 /* Repoint the old "from" handle's users. */
2990 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2991 last_user->handle_info = hi_to;
2993 hi_from->users = NULL;
2995 /* Merge channel userlists. */
2996 for (cList=hi_from->channels; cList; cList=cListNext) {
2997 struct userData *cList2;
2998 cListNext = cList->u_next;
2999 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3000 if (cList->channel == cList2->channel)
3002 if (cList2 && (cList2->access >= cList->access)) {
3003 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);
3004 /* keep cList2 in hi_to; remove cList from hi_from */
3005 del_channel_user(cList, 1);
3008 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);
3009 /* remove the lower-ranking cList2 from hi_to */
3010 del_channel_user(cList2, 1);
3012 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3014 /* cList needs to be moved from hi_from to hi_to */
3015 cList->handle = hi_to;
3016 /* Remove from linked list for hi_from */
3017 assert(!cList->u_prev);
3018 hi_from->channels = cList->u_next;
3020 cList->u_next->u_prev = cList->u_prev;
3021 /* Add to linked list for hi_to */
3022 cList->u_prev = NULL;
3023 cList->u_next = hi_to->channels;
3024 if (hi_to->channels)
3025 hi_to->channels->u_prev = cList;
3026 hi_to->channels = cList;
3030 /* Do they get an OpServ level promotion? */
3031 if (hi_from->opserv_level > hi_to->opserv_level)
3032 hi_to->opserv_level = hi_from->opserv_level;
3034 /* What about last seen time? */
3035 if (hi_from->lastseen > hi_to->lastseen)
3036 hi_to->lastseen = hi_from->lastseen;
3038 /* New karma is the sum of the two original karmas. */
3039 hi_to->karma += hi_from->karma;
3041 /* Does a fakehost carry over? (This intentionally doesn't set it
3042 * for users previously attached to hi_to. They'll just have to
3045 if (hi_from->fakehost && !hi_to->fakehost)
3046 hi_to->fakehost = strdup(hi_from->fakehost);
3048 /* Notify of success. */
3049 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3050 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3051 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3053 /* Unregister the "from" handle. */
3054 nickserv_unregister_handle(hi_from, NULL);
3059 struct nickserv_discrim {
3060 unsigned long flags_on, flags_off;
3061 time_t min_registered, max_registered;
3064 int min_level, max_level;
3065 int min_karma, max_karma;
3066 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3067 const char *nickmask;
3068 const char *hostmask;
3069 const char *handlemask;
3070 const char *emailmask;
3073 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3075 struct discrim_apply_info {
3076 struct nickserv_discrim *discrim;
3077 discrim_search_func func;
3078 struct userNode *source;
3079 unsigned int matched;
3082 static struct nickserv_discrim *
3083 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3086 struct nickserv_discrim *discrim;
3088 discrim = malloc(sizeof(*discrim));
3089 memset(discrim, 0, sizeof(*discrim));
3090 discrim->min_level = 0;
3091 discrim->max_level = ~0;
3092 discrim->limit = 50;
3093 discrim->min_registered = 0;
3094 discrim->max_registered = INT_MAX;
3095 discrim->lastseen = now;
3096 discrim->min_karma = INT_MIN;
3097 discrim->max_karma = INT_MAX;
3099 for (i=0; i<argc; i++) {
3100 if (i == argc - 1) {
3101 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3104 if (!irccasecmp(argv[i], "limit")) {
3105 discrim->limit = strtoul(argv[++i], NULL, 0);
3106 } else if (!irccasecmp(argv[i], "flags")) {
3107 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3108 } else if (!irccasecmp(argv[i], "registered")) {
3109 const char *cmp = argv[++i];
3110 if (cmp[0] == '<') {
3111 if (cmp[1] == '=') {
3112 discrim->min_registered = now - ParseInterval(cmp+2);
3114 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3116 } else if (cmp[0] == '=') {
3117 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3118 } else if (cmp[0] == '>') {
3119 if (cmp[1] == '=') {
3120 discrim->max_registered = now - ParseInterval(cmp+2);
3122 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3125 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3127 } else if (!irccasecmp(argv[i], "seen")) {
3128 discrim->lastseen = now - ParseInterval(argv[++i]);
3129 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3130 discrim->nickmask = argv[++i];
3131 } else if (!irccasecmp(argv[i], "hostmask")) {
3133 if (!irccasecmp(argv[i], "exact")) {
3134 if (i == argc - 1) {
3135 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3138 discrim->hostmask_type = EXACT;
3139 } else if (!irccasecmp(argv[i], "subset")) {
3140 if (i == argc - 1) {
3141 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3144 discrim->hostmask_type = SUBSET;
3145 } else if (!irccasecmp(argv[i], "superset")) {
3146 if (i == argc - 1) {
3147 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3150 discrim->hostmask_type = SUPERSET;
3151 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3152 if (i == argc - 1) {
3153 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3156 discrim->hostmask_type = LASTQUIT;
3159 discrim->hostmask_type = SUPERSET;
3161 discrim->hostmask = argv[++i];
3162 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3163 if (!irccasecmp(argv[++i], "*")) {
3164 discrim->handlemask = 0;
3166 discrim->handlemask = argv[i];
3168 } else if (!irccasecmp(argv[i], "email")) {
3169 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3170 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3172 } else if (!irccasecmp(argv[++i], "*")) {
3173 discrim->emailmask = 0;
3175 discrim->emailmask = argv[i];
3177 } else if (!irccasecmp(argv[i], "access")) {
3178 const char *cmp = argv[++i];
3179 if (cmp[0] == '<') {
3180 if (discrim->min_level == 0) discrim->min_level = 1;
3181 if (cmp[1] == '=') {
3182 discrim->max_level = strtoul(cmp+2, NULL, 0);
3184 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3186 } else if (cmp[0] == '=') {
3187 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3188 } else if (cmp[0] == '>') {
3189 if (cmp[1] == '=') {
3190 discrim->min_level = strtoul(cmp+2, NULL, 0);
3192 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3195 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3197 } else if (!irccasecmp(argv[i], "karma")) {
3198 const char *cmp = argv[++i];
3199 if (cmp[0] == '<') {
3200 if (cmp[1] == '=') {
3201 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3203 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3205 } else if (cmp[0] == '=') {
3206 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3207 } else if (cmp[0] == '>') {
3208 if (cmp[1] == '=') {
3209 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3211 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3214 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3217 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3228 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3230 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3231 || (discrim->flags_off & hi->flags)
3232 || (discrim->min_registered > hi->registered)
3233 || (discrim->max_registered < hi->registered)
3234 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3235 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3236 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3237 || (discrim->min_level > hi->opserv_level)
3238 || (discrim->max_level < hi->opserv_level)
3239 || (discrim->min_karma > hi->karma)
3240 || (discrim->max_karma < hi->karma)
3244 if (discrim->hostmask) {
3246 for (i=0; i<hi->masks->used; i++) {
3247 const char *mask = hi->masks->list[i];
3248 if ((discrim->hostmask_type == SUBSET)
3249 && (match_ircglobs(discrim->hostmask, mask))) break;
3250 else if ((discrim->hostmask_type == EXACT)
3251 && !irccasecmp(discrim->hostmask, mask)) break;
3252 else if ((discrim->hostmask_type == SUPERSET)
3253 && (match_ircglobs(mask, discrim->hostmask))) break;
3254 else if ((discrim->hostmask_type == LASTQUIT)
3255 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3257 if (i==hi->masks->used) return 0;
3259 if (discrim->nickmask) {
3260 struct nick_info *nick = hi->nicks;
3262 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3265 if (!nick) return 0;
3271 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3273 dict_iterator_t it, next;
3274 unsigned int matched;
3276 for (it = dict_first(nickserv_handle_dict), matched = 0;
3277 it && (matched < discrim->limit);
3279 next = iter_next(it);
3280 if (nickserv_discrim_match(discrim, iter_data(it))) {
3281 dsf(source, iter_data(it));
3289 search_print_func(struct userNode *source, struct handle_info *match)
3291 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3295 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3300 search_unregister_func (struct userNode *source, struct handle_info *match)
3302 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3303 nickserv_unregister_handle(match, source);
3307 nickserv_sort_accounts_by_access(const void *a, const void *b)
3309 const struct handle_info *hi_a = *(const struct handle_info**)a;
3310 const struct handle_info *hi_b = *(const struct handle_info**)b;
3311 if (hi_a->opserv_level != hi_b->opserv_level)
3312 return hi_b->opserv_level - hi_a->opserv_level;
3313 return irccasecmp(hi_a->handle, hi_b->handle);
3317 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3319 struct handle_info_list hil;
3320 struct helpfile_table tbl;
3325 memset(&hil, 0, sizeof(hil));
3326 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3327 struct handle_info *hi = iter_data(it);
3328 if (hi->opserv_level)
3329 handle_info_list_append(&hil, hi);
3331 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3332 tbl.length = hil.used + 1;
3334 tbl.flags = TABLE_NO_FREE;
3335 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3336 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3339 for (ii = 0; ii < hil.used; ) {
3340 ary = malloc(tbl.width * sizeof(ary[0]));
3341 ary[0] = hil.list[ii]->handle;
3342 ary[1] = strtab(hil.list[ii]->opserv_level);
3343 tbl.contents[++ii] = ary;
3345 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3346 reply("MSG_MATCH_COUNT", hil.used);
3347 for (ii = 0; ii < hil.used; ii++)
3348 free(tbl.contents[ii]);
3353 static NICKSERV_FUNC(cmd_search)
3355 struct nickserv_discrim *discrim;
3356 discrim_search_func action;
3357 struct svccmd *subcmd;
3358 unsigned int matches;
3361 NICKSERV_MIN_PARMS(3);
3362 sprintf(buf, "search %s", argv[1]);
3363 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3364 if (!irccasecmp(argv[1], "print"))
3365 action = search_print_func;
3366 else if (!irccasecmp(argv[1], "count"))
3367 action = search_count_func;
3368 else if (!irccasecmp(argv[1], "unregister"))
3369 action = search_unregister_func;
3371 reply("NSMSG_INVALID_ACTION", argv[1]);
3375 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3378 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3382 if (action == search_print_func)
3383 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3384 else if (action == search_count_func)
3385 discrim->limit = INT_MAX;
3387 matches = nickserv_discrim_search(discrim, action, user);
3390 reply("MSG_MATCH_COUNT", matches);
3392 reply("MSG_NO_MATCHES");
3398 static MODCMD_FUNC(cmd_checkpass)
3400 struct handle_info *hi;
3402 NICKSERV_MIN_PARMS(3);
3403 if (!(hi = get_handle_info(argv[1]))) {
3404 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3407 if (checkpass(argv[2], hi->passwd))
3408 reply("CHECKPASS_YES");
3410 reply("CHECKPASS_NO");
3416 nickserv_db_read_handle(const char *handle, dict_t obj)
3419 struct string_list *masks, *slist;
3420 struct handle_info *hi;
3421 struct userNode *authed_users;
3422 struct userData *channels;
3423 unsigned long int id;
3427 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3428 id = str ? strtoul(str, NULL, 0) : 0;
3429 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3431 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3434 if ((hi = get_handle_info(handle))) {
3435 authed_users = hi->users;
3436 channels = hi->channels;
3438 hi->channels = NULL;
3439 dict_remove(nickserv_handle_dict, hi->handle);
3441 authed_users = NULL;
3444 hi = register_handle(handle, str, id);
3446 hi->users = authed_users;
3447 while (authed_users) {
3448 authed_users->handle_info = hi;
3449 authed_users = authed_users->next_authed;
3452 hi->channels = channels;
3453 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3454 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3455 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3456 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3457 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3458 hi->language = language_find(str ? str : "C");
3459 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3460 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3461 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3463 hi->infoline = strdup(str);
3464 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3465 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3466 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3467 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3468 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3469 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3470 /* We want to read the nicks even if disable_nicks is set. This is so
3471 * that we don't lose the nick data entirely. */
3472 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3474 for (ii=0; ii<slist->used; ii++)
3475 register_nick(slist->list[ii], hi);
3477 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3479 for (ii=0; str[ii]; ii++)
3480 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3482 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3483 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3484 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3485 hi->announcements = str ? str[0] : '?';
3486 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3487 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3488 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3489 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3490 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3492 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3494 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3495 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3497 nickserv_set_email_addr(hi, str);
3498 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3500 hi->epithet = strdup(str);
3501 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3503 hi->fakehost = strdup(str);
3504 /* Read the "cookie" sub-database (if it exists). */
3505 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3507 const char *data, *type, *expires, *cookie_str;
3508 struct handle_cookie *cookie;
3510 cookie = calloc(1, sizeof(*cookie));
3511 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3512 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3513 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3514 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3515 if (!type || !expires || !cookie_str) {
3516 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3519 if (!irccasecmp(type, KEY_ACTIVATION))
3520 cookie->type = ACTIVATION;
3521 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3522 cookie->type = PASSWORD_CHANGE;
3523 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3524 cookie->type = EMAIL_CHANGE;
3525 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3526 cookie->type = ALLOWAUTH;
3528 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3531 cookie->expires = strtoul(expires, NULL, 0);
3532 if (cookie->expires < now)
3535 cookie->data = strdup(data);
3536 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3540 nickserv_bake_cookie(cookie);
3542 nickserv_free_cookie(cookie);
3544 /* Read the "notes" sub-database (if it exists). */
3545 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3548 struct handle_note *last_note;
3549 struct handle_note *note;
3552 for (it = dict_first(subdb); it; it = iter_next(it)) {
3553 const char *expires;
3561 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3563 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", id, hi->handle);
3566 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3567 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3568 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3569 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3570 if (!setter || !text || !set) {
3571 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", id, hi->handle);
3574 note = calloc(1, sizeof(*note) + strlen(text));
3576 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3577 note->set = strtoul(set, NULL, 10);
3578 note->id = strtoul(id, NULL, 10);
3579 safestrncpy(note->setter, setter, sizeof(note->setter));
3580 strcpy(note->note, text);
3582 last_note->next = note;
3591 nickserv_saxdb_read(dict_t db) {
3593 struct record_data *rd;
3595 for (it=dict_first(db); it; it=iter_next(it)) {
3597 nickserv_db_read_handle(iter_key(it), rd->d.object);
3602 static NICKSERV_FUNC(cmd_mergedb)
3604 struct timeval start, stop;
3607 NICKSERV_MIN_PARMS(2);
3608 gettimeofday(&start, NULL);
3609 if (!(db = parse_database(argv[1]))) {
3610 reply("NSMSG_DB_UNREADABLE", argv[1]);
3613 nickserv_saxdb_read(db);
3615 gettimeofday(&stop, NULL);
3616 stop.tv_sec -= start.tv_sec;
3617 stop.tv_usec -= start.tv_usec;
3618 if (stop.tv_usec < 0) {
3620 stop.tv_usec += 1000000;
3622 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3627 expire_handles(UNUSED_ARG(void *data))
3629 dict_iterator_t it, next;
3631 struct handle_info *hi;
3633 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3634 next = iter_next(it);
3636 if ((hi->opserv_level > 0)
3638 || HANDLE_FLAGGED(hi, FROZEN)
3639 || HANDLE_FLAGGED(hi, NODELETE)) {
3642 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3643 if ((now - hi->lastseen) > expiry) {
3644 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3645 nickserv_unregister_handle(hi, NULL);
3649 if (nickserv_conf.handle_expire_frequency)
3650 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3654 nickserv_load_dict(const char *fname)
3658 if (!(file = fopen(fname, "r"))) {
3659 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3662 while (!feof(file)) {
3663 fgets(line, sizeof(line), file);
3666 if (line[strlen(line)-1] == '\n')
3667 line[strlen(line)-1] = 0;
3668 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3671 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3674 static enum reclaim_action
3675 reclaim_action_from_string(const char *str) {
3677 return RECLAIM_NONE;
3678 else if (!irccasecmp(str, "warn"))
3679 return RECLAIM_WARN;
3680 else if (!irccasecmp(str, "svsnick"))
3681 return RECLAIM_SVSNICK;
3682 else if (!irccasecmp(str, "kill"))
3683 return RECLAIM_KILL;
3685 return RECLAIM_NONE;
3689 nickserv_conf_read(void)
3691 dict_t conf_node, child;
3695 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3696 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3699 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3701 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3702 if (nickserv_conf.valid_handle_regex_set)
3703 regfree(&nickserv_conf.valid_handle_regex);
3705 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3706 nickserv_conf.valid_handle_regex_set = !err;
3707 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3709 nickserv_conf.valid_handle_regex_set = 0;
3711 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3712 if (nickserv_conf.valid_nick_regex_set)
3713 regfree(&nickserv_conf.valid_nick_regex);
3715 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3716 nickserv_conf.valid_nick_regex_set = !err;
3717 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3719 nickserv_conf.valid_nick_regex_set = 0;
3721 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3723 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3724 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3725 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3726 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3727 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3728 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3729 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3730 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3731 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3732 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3733 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3734 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3735 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3736 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3737 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3738 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3739 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3740 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3741 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3742 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3743 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3744 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3745 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3746 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3747 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3749 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3750 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3751 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3753 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3754 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3755 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3757 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3758 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3759 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3760 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3761 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3762 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3763 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3764 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3765 if (!nickserv_conf.disable_nicks) {
3766 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3767 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3768 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3769 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3770 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3771 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3772 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3773 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3775 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3776 for (it=dict_first(child); it; it=iter_next(it)) {
3777 const char *key = iter_key(it), *value;
3781 if (!strncasecmp(key, "uc_", 3))
3782 flag = toupper(key[3]);
3783 else if (!strncasecmp(key, "lc_", 3))
3784 flag = tolower(key[3]);
3788 if ((pos = handle_inverse_flags[flag])) {
3789 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3790 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3793 if (nickserv_conf.weak_password_dict)
3794 dict_delete(nickserv_conf.weak_password_dict);
3795 nickserv_conf.weak_password_dict = dict_new();
3796 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3797 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3798 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3799 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3801 nickserv_load_dict(str);
3802 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3803 if (nickserv && str)
3804 NickChange(nickserv, str, 0);
3805 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3806 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3807 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3808 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3809 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3810 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3811 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3812 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3813 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3814 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3815 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3816 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3817 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3818 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3819 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3820 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3821 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3822 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3823 str = conf_get_data("server/network", RECDB_QSTRING);
3824 nickserv_conf.network_name = str ? str : "some IRC network";
3825 if (!nickserv_conf.auth_policer_params) {
3826 nickserv_conf.auth_policer_params = policer_params_new();
3827 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3828 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3830 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3831 for (it=dict_first(child); it; it=iter_next(it))
3832 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3836 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3838 char newnick[NICKLEN+1];
3847 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3849 case RECLAIM_SVSNICK:
3851 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3852 } while (GetUserH(newnick));
3853 irc_svsnick(nickserv, user, newnick);
3856 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3857 DelUser(user, nickserv, 1, msg);
3863 nickserv_reclaim_p(void *data) {
3864 struct userNode *user = data;
3865 struct nick_info *ni = get_nick_info(user->nick);
3867 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3871 check_user_nick(struct userNode *user) {
3872 struct nick_info *ni;
3873 user->modes &= ~FLAGS_REGNICK;
3874 if (!(ni = get_nick_info(user->nick)))
3876 if (user->handle_info == ni->owner) {
3877 user->modes |= FLAGS_REGNICK;
3881 if (nickserv_conf.warn_nick_owned)
3882 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3883 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3885 if (nickserv_conf.auto_reclaim_delay)
3886 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3888 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3893 handle_new_user(struct userNode *user)
3895 return check_user_nick(user);
3899 handle_account(struct userNode *user, const char *stamp)
3901 struct handle_info *hi;
3903 #ifdef WITH_PROTOCOL_P10
3904 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3906 hi = dict_find(nickserv_id_dict, stamp, NULL);
3910 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3913 set_user_handle_info(user, hi, 0);
3915 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3920 handle_nick_change(struct userNode *user, const char *old_nick)
3922 struct handle_info *hi;
3924 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3925 dict_remove(nickserv_allow_auth_dict, old_nick);
3926 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3928 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3929 check_user_nick(user);
3933 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3935 dict_remove(nickserv_allow_auth_dict, user->nick);
3936 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3937 set_user_handle_info(user, NULL, 0);
3940 static struct modcmd *
3941 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3943 if (min_level > 0) {
3945 sprintf(buf, "%u", min_level);
3946 if (must_be_qualified) {
3947 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3949 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3951 } else if (min_level == 0) {
3952 if (must_be_qualified) {
3953 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3955 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3958 if (must_be_qualified) {
3959 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3961 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3967 nickserv_db_cleanup(void)
3969 unreg_del_user_func(nickserv_remove_user);
3970 userList_clean(&curr_helpers);
3971 policer_params_delete(nickserv_conf.auth_policer_params);
3972 dict_delete(nickserv_handle_dict);
3973 dict_delete(nickserv_nick_dict);
3974 dict_delete(nickserv_opt_dict);
3975 dict_delete(nickserv_allow_auth_dict);
3976 dict_delete(nickserv_email_dict);
3977 dict_delete(nickserv_id_dict);
3978 dict_delete(nickserv_conf.weak_password_dict);
3979 free(auth_func_list);
3980 free(unreg_func_list);
3982 free(allowauth_func_list);
3983 free(handle_merge_func_list);
3984 free(failpw_func_list);
3985 if (nickserv_conf.valid_handle_regex_set)
3986 regfree(&nickserv_conf.valid_handle_regex);
3987 if (nickserv_conf.valid_nick_regex_set)
3988 regfree(&nickserv_conf.valid_nick_regex);
3992 init_nickserv(const char *nick)
3995 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3996 reg_new_user_func(handle_new_user);
3997 reg_nick_change_func(handle_nick_change);
3998 reg_del_user_func(nickserv_remove_user);
3999 reg_account_func(handle_account);
4001 /* set up handle_inverse_flags */
4002 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4003 for (i=0; handle_flags[i]; i++) {
4004 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4005 flag_access_levels[i] = 0;
4008 conf_register_reload(nickserv_conf_read);
4009 nickserv_opt_dict = dict_new();
4010 nickserv_email_dict = dict_new();
4011 dict_set_free_keys(nickserv_email_dict, free);
4012 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4014 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4015 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4016 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4017 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4018 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4019 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4020 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4021 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4022 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4023 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4024 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4025 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4026 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4027 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4028 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4029 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4030 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4031 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4032 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4033 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4034 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4035 if (!nickserv_conf.disable_nicks) {
4036 /* nick management commands */
4037 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4038 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4039 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4040 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4041 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4042 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4044 if (nickserv_conf.email_enabled) {
4045 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4046 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4047 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4048 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4049 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4051 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4052 /* miscellaneous commands */
4053 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4054 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4055 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4056 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4057 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4059 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4060 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4061 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4062 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4063 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4064 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4065 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4066 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4067 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4068 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4069 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4070 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4071 if (nickserv_conf.titlehost_suffix) {
4072 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4073 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4075 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
4076 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4077 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4078 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4080 nickserv_handle_dict = dict_new();
4081 dict_set_free_keys(nickserv_handle_dict, free);
4082 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4084 nickserv_id_dict = dict_new();
4085 dict_set_free_keys(nickserv_id_dict, free);
4087 nickserv_nick_dict = dict_new();
4088 dict_set_free_data(nickserv_nick_dict, free);
4090 nickserv_allow_auth_dict = dict_new();
4092 userList_init(&curr_helpers);
4095 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4096 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4097 nickserv_service = service_register(nickserv);
4099 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4100 reg_exit_func(nickserv_db_cleanup);
4101 if(nickserv_conf.handle_expire_frequency)
4102 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4103 message_register_table(msgtab);