1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2006 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for gag_create(), opserv_bad_channel() */
33 # include "rx/rxposix.h"
36 #define NICKSERV_CONF_NAME "services/nickserv"
38 #define KEY_DISABLE_NICKS "disable_nicks"
39 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
40 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
41 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
42 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
43 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
44 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
45 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
46 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
47 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
48 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
49 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
50 #define KEY_MODOPER_LEVEL "modoper_level"
51 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
52 #define KEY_SET_TITLE_LEVEL "set_title_level"
53 #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
54 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
55 #define KEY_FLAG_LEVELS "flag_levels"
56 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
57 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
58 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
59 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
60 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
61 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
62 #define KEY_DICT_FILE "dict_file"
63 #define KEY_NICK "nick"
64 #define KEY_LANGUAGE "language"
65 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
66 #define KEY_AUTOGAG_DURATION "autogag_duration"
67 #define KEY_AUTH_POLICER "auth_policer"
68 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
69 #define KEY_EMAIL_ENABLED "email_enabled"
70 #define KEY_EMAIL_REQUIRED "email_required"
71 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
72 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
73 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
76 #define KEY_PASSWD "passwd"
77 #define KEY_NICKS "nicks"
78 #define KEY_MASKS "masks"
79 #define KEY_OPSERV_LEVEL "opserv_level"
80 #define KEY_FLAGS "flags"
81 #define KEY_REGISTER_ON "register"
82 #define KEY_LAST_SEEN "lastseen"
83 #define KEY_INFO "info"
84 #define KEY_USERLIST_STYLE "user_style"
85 #define KEY_SCREEN_WIDTH "screen_width"
86 #define KEY_LAST_AUTHED_HOST "last_authed_host"
87 #define KEY_LAST_QUIT_HOST "last_quit_host"
88 #define KEY_EMAIL_ADDR "email_addr"
89 #define KEY_COOKIE "cookie"
90 #define KEY_COOKIE_DATA "data"
91 #define KEY_COOKIE_TYPE "type"
92 #define KEY_COOKIE_EXPIRES "expires"
93 #define KEY_ACTIVATION "activation"
94 #define KEY_PASSWORD_CHANGE "password change"
95 #define KEY_EMAIL_CHANGE "email change"
96 #define KEY_ALLOWAUTH "allowauth"
97 #define KEY_EPITHET "epithet"
98 #define KEY_TABLE_WIDTH "table_width"
99 #define KEY_MAXLOGINS "maxlogins"
100 #define KEY_FAKEHOST "fakehost"
101 #define KEY_NOTES "notes"
102 #define KEY_NOTE_EXPIRES "expires"
103 #define KEY_NOTE_SET "set"
104 #define KEY_NOTE_SETTER "setter"
105 #define KEY_NOTE_NOTE "note"
106 #define KEY_KARMA "karma"
108 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
110 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
111 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
112 typedef OPTION_FUNC(option_func_t);
114 DEFINE_LIST(handle_info_list, struct handle_info*);
116 #define NICKSERV_MIN_PARMS(N) do { \
118 reply("MSG_MISSING_PARAMS", argv[0]); \
119 svccmd_send_help(user, nickserv, cmd); \
123 struct userNode *nickserv;
124 struct userList curr_helpers;
125 const char *handle_flags = HANDLE_FLAGS;
127 static struct module *nickserv_module;
128 static struct service *nickserv_service;
129 static struct log_type *NS_LOG;
130 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
131 static dict_t nickserv_id_dict; /* contains struct handle_info* */
132 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
133 static dict_t nickserv_opt_dict; /* contains option_func_t* */
134 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
135 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
136 static char handle_inverse_flags[256];
137 static unsigned int flag_access_levels[32];
138 static const struct message_entry msgtab[] = {
139 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
140 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
141 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
142 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
143 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
144 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
145 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
146 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
147 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
148 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
149 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
150 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
151 { "NSMSG_USE_COOKIE_REGISTER", "To activate your account, you must check your email for the \"cookie\" that has been mailed to it. When you have it, use the $bcookie$b command to complete registration." },
152 { "NSMSG_USE_COOKIE_RESETPASS", "A cookie has been mailed to your account's email address. You must check your email and use the $bcookie$b command to confirm the password change." },
153 { "NSMSG_USE_COOKIE_EMAIL_1", "A cookie has been mailed to the new address you requested. To finish setting your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
154 { "NSMSG_USE_COOKIE_EMAIL_2", "A cookie has been generated, and half mailed to each your old and new addresses. To finish changing your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
155 { "NSMSG_USE_COOKIE_AUTH", "A cookie has been generated and sent to your email address. Once you have checked your email and received the cookie, auth using the $bcookie$b command." },
156 { "NSMSG_COOKIE_LIVE", "Account $b%s$b already has a cookie active. Please either finish using that cookie, wait for it to expire, or auth to the account and use the $bdelcookie$b command." },
157 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
158 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
159 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
160 { "NSMSG_BAD_COOKIE", "That cookie is not the right one. Please make sure you are copying it EXACTLY from the email; it is case-sensitive, so $bABC$b is different from $babc$b." },
161 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
162 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
163 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
164 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
165 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
166 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
167 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
168 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
169 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
170 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
171 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
172 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
173 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
174 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
175 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
176 { "NSMSG_USE_AUTHCOOKIE", "Your hostmask is not valid for account $b%1$s$b. Please use the $bauthcookie$b command to grant yourself access. (/msg $S authcookie %1$s)" },
177 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
178 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
179 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
180 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
181 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
182 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
183 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
184 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
185 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
186 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
187 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
188 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
189 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
190 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
191 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
192 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
193 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
194 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
195 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
196 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
197 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
198 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
199 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
200 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
201 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
202 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
203 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
204 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
205 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
206 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
207 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
208 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
209 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
210 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
211 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
212 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
213 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
214 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
215 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
216 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
217 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
218 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
219 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
220 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
221 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
222 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
223 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
224 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
225 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
226 { "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)." },
227 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
228 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
229 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
230 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
231 { "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." },
232 { "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." },
233 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
234 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
235 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
236 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
237 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
238 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
239 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
240 { "NSMSG_PASS_SUCCESS", "Password changed." },
241 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
242 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
243 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
244 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
245 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
246 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
247 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
248 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
249 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
250 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
251 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
252 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
253 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
254 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
255 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
256 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
257 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
258 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
259 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
260 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
261 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
262 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
263 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
264 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
265 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
266 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
267 { "NSMSG_NO_ACCESS", "Access denied." },
268 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
269 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
270 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
271 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
272 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
273 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
274 { "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." },
275 { "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." },
276 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
277 { "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." },
278 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
279 { "NSMSG_SEARCH_MATCH", "Match: %s" },
280 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
281 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
282 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
283 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
284 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
285 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
286 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
287 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
288 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
289 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
290 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
291 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
292 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
293 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
294 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
295 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
296 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
297 { "NSMSG_SET_STYLE", "$bSTYLE: $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->handle = strdup(handle);
433 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
435 dict_insert(nickserv_handle_dict, hi->handle, hi);
437 #ifdef WITH_PROTOCOL_BAHAMUT
439 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
446 register_nick(const char *nick, struct handle_info *owner)
448 struct nick_info *ni;
449 ni = malloc(sizeof(struct nick_info));
450 safestrncpy(ni->nick, nick, sizeof(ni->nick));
452 ni->next = owner->nicks;
454 dict_insert(nickserv_nick_dict, ni->nick, ni);
458 delete_nick(struct nick_info *ni)
460 struct nick_info *last, *next;
461 struct userNode *user;
462 /* Check to see if we should mark a user as unregistered. */
463 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
464 user->modes &= ~FLAGS_REGNICK;
467 /* Remove ni from the nick_info linked list. */
468 if (ni == ni->owner->nicks) {
469 ni->owner->nicks = ni->next;
471 last = ni->owner->nicks;
477 last->next = next->next;
479 dict_remove(nickserv_nick_dict, ni->nick);
482 static unreg_func_t *unreg_func_list;
483 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
486 reg_unreg_func(unreg_func_t func)
488 if (unreg_func_used == unreg_func_size) {
489 if (unreg_func_size) {
490 unreg_func_size <<= 1;
491 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
494 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
497 unreg_func_list[unreg_func_used++] = func;
501 nickserv_free_cookie(void *data)
503 struct handle_cookie *cookie = data;
504 if (cookie->hi) cookie->hi->cookie = NULL;
505 if (cookie->data) free(cookie->data);
510 free_handle_info(void *vhi)
512 struct handle_info *hi = vhi;
514 #ifdef WITH_PROTOCOL_BAHAMUT
517 inttobase64(id, hi->id, IDLEN);
518 dict_remove(nickserv_id_dict, id);
521 free_string_list(hi->masks);
525 delete_nick(hi->nicks);
530 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
531 nickserv_free_cookie(hi->cookie);
534 struct handle_note *note = hi->notes;
535 hi->notes = note->next;
538 if (hi->email_addr) {
539 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
540 handle_info_list_remove(hil, hi);
542 dict_remove(nickserv_email_dict, hi->email_addr);
547 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
550 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
554 for (n=0; n<unreg_func_used; n++)
555 unreg_func_list[n](notify, hi);
557 set_user_handle_info(hi->users, NULL, 0);
559 if (nickserv_conf.disable_nicks)
560 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
562 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
564 dict_remove(nickserv_handle_dict, hi->handle);
568 get_handle_info(const char *handle)
570 return dict_find(nickserv_handle_dict, handle, 0);
574 get_nick_info(const char *nick)
576 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
580 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
585 for (nn=0; nn<channel->members.used; ++nn) {
586 mn = channel->members.list[nn];
587 if ((mn->user != except) && (mn->user->handle_info == handle))
594 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
595 if (!user->handle_info) {
597 send_message(user, bot, "MSG_AUTHENTICATE");
601 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
603 send_message(user, bot, "NSMSG_NO_ACCESS");
607 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
609 send_message(user, bot, "MSG_OPER_SUSPENDED");
613 if (user->handle_info->opserv_level < min_level) {
615 send_message(user, bot, "NSMSG_NO_ACCESS");
623 is_valid_handle(const char *handle)
625 struct userNode *user;
626 /* cant register a juped nick/service nick as handle, to prevent confusion */
627 user = GetUserH(handle);
628 if (user && IsLocal(user))
630 /* check against maximum length */
631 if (strlen(handle) > NICKSERV_HANDLE_LEN)
633 /* for consistency, only allow account names that could be nicks */
634 if (!is_valid_nick(handle))
636 /* disallow account names that look like bad words */
637 if (opserv_bad_channel(handle))
639 /* test either regex or containing all valid chars */
640 if (nickserv_conf.valid_handle_regex_set) {
641 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
644 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
645 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
649 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
654 is_registerable_nick(const char *nick)
656 /* make sure it could be used as an account name */
657 if (!is_valid_handle(nick))
660 if (strlen(nick) > NICKLEN)
662 /* test either regex or as valid handle */
663 if (nickserv_conf.valid_nick_regex_set) {
664 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
667 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
668 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
676 is_valid_email_addr(const char *email)
678 return strchr(email, '@') != NULL;
682 visible_email_addr(struct userNode *user, struct handle_info *hi)
684 if (hi->email_addr) {
685 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
686 return hi->email_addr;
696 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
698 struct handle_info *hi;
699 struct userNode *target;
703 if (!(hi = get_handle_info(++name))) {
704 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
709 if (!(target = GetUserH(name))) {
710 send_message(user, service, "MSG_NICK_UNKNOWN", name);
713 if (IsLocal(target)) {
714 if (IsService(target))
715 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
717 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
720 if (!(hi = target->handle_info)) {
721 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
729 oper_outranks(struct userNode *user, struct handle_info *hi) {
730 if (user->handle_info->opserv_level > hi->opserv_level)
732 if (user->handle_info->opserv_level == hi->opserv_level) {
733 if ((user->handle_info->opserv_level == 1000)
734 || (user->handle_info == hi)
735 || ((user->handle_info->opserv_level == 0)
736 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
737 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
741 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
745 static struct handle_info *
746 get_victim_oper(struct userNode *user, const char *target)
748 struct handle_info *hi;
749 if (!(hi = smart_get_handle_info(nickserv, user, target)))
751 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
752 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
755 return oper_outranks(user, hi) ? hi : NULL;
759 valid_user_for(struct userNode *user, struct handle_info *hi)
763 /* If no hostmasks on the account, allow it. */
764 if (!hi->masks->used)
766 /* If any hostmask matches, allow it. */
767 for (ii=0; ii<hi->masks->used; ii++)
768 if (user_matches_glob(user, hi->masks->list[ii], 0))
770 /* If they are allowauthed to this account, allow it (removing the aa). */
771 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
772 dict_remove(nickserv_allow_auth_dict, user->nick);
775 /* The user is not allowed to use this account. */
780 is_secure_password(const char *handle, const char *pass, struct userNode *user)
783 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
787 if (len < nickserv_conf.password_min_length) {
789 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
792 if (!irccasecmp(pass, handle)) {
794 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
797 dict_find(nickserv_conf.weak_password_dict, pass, &p);
800 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
803 for (i=0; i<len; i++) {
804 if (isdigit(pass[i]))
806 if (isupper(pass[i]))
808 if (islower(pass[i]))
811 if ((cnt_lower < nickserv_conf.password_min_lower)
812 || (cnt_upper < nickserv_conf.password_min_upper)
813 || (cnt_digits < nickserv_conf.password_min_digits)) {
815 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
821 static auth_func_t *auth_func_list;
822 static unsigned int auth_func_size = 0, auth_func_used = 0;
825 reg_auth_func(auth_func_t func)
827 if (auth_func_used == auth_func_size) {
828 if (auth_func_size) {
829 auth_func_size <<= 1;
830 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
833 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
836 auth_func_list[auth_func_used++] = func;
839 static handle_rename_func_t *rf_list;
840 static unsigned int rf_list_size, rf_list_used;
843 reg_handle_rename_func(handle_rename_func_t func)
845 if (rf_list_used == rf_list_size) {
848 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
851 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
854 rf_list[rf_list_used++] = func;
858 generate_fakehost(struct handle_info *handle)
860 extern const char *hidden_host_suffix;
861 static char buffer[HOSTLEN+1];
863 if (!handle->fakehost) {
864 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
866 } else if (handle->fakehost[0] == '.') {
867 /* A leading dot indicates the stored value is actually a title. */
868 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
871 return handle->fakehost;
875 apply_fakehost(struct handle_info *handle)
877 struct userNode *target;
882 fake = generate_fakehost(handle);
883 for (target = handle->users; target; target = target->next_authed)
884 assign_fakehost(target, fake, 1);
888 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
891 struct handle_info *old_info;
893 /* This can happen if somebody uses COOKIE while authed, or if
894 * they re-auth to their current handle (which is silly, but users
896 if (user->handle_info == hi)
899 if (user->handle_info) {
900 struct userNode *other;
903 userList_remove(&curr_helpers, user);
905 /* remove from next_authed linked list */
906 if (user->handle_info->users == user) {
907 user->handle_info->users = user->next_authed;
909 for (other = user->handle_info->users;
910 other->next_authed != user;
911 other = other->next_authed) ;
912 other->next_authed = user->next_authed;
914 /* if nobody left on old handle, and they're not an oper, remove !god */
915 if (!user->handle_info->users && !user->handle_info->opserv_level)
916 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
917 /* record them as being last seen at this time */
918 user->handle_info->lastseen = now;
919 /* and record their hostmask */
920 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
922 old_info = user->handle_info;
923 user->handle_info = hi;
924 if (hi && !hi->users && !hi->opserv_level)
925 HANDLE_CLEAR_FLAG(hi, HELPING);
926 for (n=0; n<auth_func_used; n++)
927 auth_func_list[n](user, old_info);
929 struct nick_info *ni;
931 HANDLE_CLEAR_FLAG(hi, FROZEN);
932 if (nickserv_conf.warn_clone_auth) {
933 struct userNode *other;
934 for (other = hi->users; other; other = other->next_authed)
935 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
937 user->next_authed = hi->users;
941 userList_append(&curr_helpers, user);
943 if (hi->fakehost || old_info)
947 #ifdef WITH_PROTOCOL_BAHAMUT
948 /* Stamp users with their account ID. */
950 inttobase64(id, hi->id, IDLEN);
951 #elif WITH_PROTOCOL_P10
952 /* Stamp users with their account name. */
953 char *id = hi->handle;
955 const char *id = "???";
957 if (!nickserv_conf.disable_nicks) {
958 struct nick_info *ni;
959 for (ni = hi->nicks; ni; ni = ni->next) {
960 if (!irccasecmp(user->nick, ni->nick)) {
961 user->modes |= FLAGS_REGNICK;
969 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
970 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
972 /* We cannot clear the user's account ID, unfortunately. */
973 user->next_authed = NULL;
977 static struct handle_info*
978 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
980 struct handle_info *hi;
981 struct nick_info *ni;
982 char crypted[MD5_CRYPT_LENGTH];
984 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
985 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
989 if (!is_secure_password(handle, passwd, user))
992 cryptpass(passwd, crypted);
993 hi = register_handle(handle, crypted, 0);
994 hi->masks = alloc_string_list(1);
996 hi->language = lang_C;
997 hi->registered = now;
999 hi->flags = HI_DEFAULT_FLAGS;
1000 if (settee && !no_auth)
1001 set_user_handle_info(settee, hi, 1);
1004 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1005 else if (nickserv_conf.disable_nicks)
1006 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1007 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1008 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1010 register_nick(user->nick, hi);
1011 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1013 if (settee && (user != settee))
1014 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1019 nickserv_bake_cookie(struct handle_cookie *cookie)
1021 cookie->hi->cookie = cookie;
1022 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1026 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1028 struct handle_cookie *cookie;
1029 char subject[128], body[4096], *misc;
1030 const char *netname, *fmt;
1034 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1038 cookie = calloc(1, sizeof(*cookie));
1040 cookie->type = type;
1041 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1042 cookie->expires = now + nickserv_conf.cookie_timeout;
1043 inttobase64(cookie->cookie, rand(), 5);
1044 inttobase64(cookie->cookie+5, rand(), 5);
1046 netname = nickserv_conf.network_name;
1049 switch (cookie->type) {
1051 hi->passwd[0] = 0; /* invalidate password */
1052 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1053 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1054 snprintf(subject, sizeof(subject), fmt, netname);
1055 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1056 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1059 case PASSWORD_CHANGE:
1060 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1061 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1062 snprintf(subject, sizeof(subject), fmt, netname);
1063 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1064 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1067 misc = hi->email_addr;
1068 hi->email_addr = cookie->data;
1070 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1071 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1072 snprintf(subject, sizeof(subject), fmt, netname);
1073 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1074 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1075 mail_send(nickserv, hi, subject, body, 1);
1076 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1077 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1079 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1080 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1081 snprintf(subject, sizeof(subject), fmt, netname);
1082 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1083 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1084 mail_send(nickserv, hi, subject, body, 1);
1087 hi->email_addr = misc;
1090 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1091 snprintf(subject, sizeof(subject), fmt, netname);
1092 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1093 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1094 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1097 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1101 mail_send(nickserv, hi, subject, body, first_time);
1102 nickserv_bake_cookie(cookie);
1106 nickserv_eat_cookie(struct handle_cookie *cookie)
1108 cookie->hi->cookie = NULL;
1109 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1110 nickserv_free_cookie(cookie);
1114 nickserv_free_email_addr(void *data)
1116 handle_info_list_clean(data);
1121 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1123 struct handle_info_list *hil;
1124 /* Remove from old handle_info_list ... */
1125 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1126 handle_info_list_remove(hil, hi);
1127 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1128 hi->email_addr = NULL;
1130 /* Add to the new list.. */
1131 if (new_email_addr) {
1132 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1133 hil = calloc(1, sizeof(*hil));
1134 hil->tag = strdup(new_email_addr);
1135 handle_info_list_init(hil);
1136 dict_insert(nickserv_email_dict, hil->tag, hil);
1138 handle_info_list_append(hil, hi);
1139 hi->email_addr = hil->tag;
1143 static NICKSERV_FUNC(cmd_register)
1146 struct handle_info *hi;
1147 const char *email_addr, *password;
1150 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1151 /* Require the first handle registered to belong to someone +o. */
1152 reply("NSMSG_REQUIRE_OPER");
1156 if (user->handle_info) {
1157 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1161 if (IsRegistering(user)) {
1162 reply("NSMSG_ALREADY_REGISTERING");
1166 if (IsStamped(user)) {
1167 /* Unauthenticated users might still have been stamped
1168 previously and could therefore have a hidden host;
1169 do not allow them to register a new account. */
1170 reply("NSMSG_STAMPED_REGISTER");
1174 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1176 if (!is_valid_handle(argv[1])) {
1177 reply("NSMSG_BAD_HANDLE", argv[1]);
1181 if ((argc >= 4) && nickserv_conf.email_enabled) {
1182 struct handle_info_list *hil;
1185 /* Remember email address. */
1186 email_addr = argv[3];
1188 /* Check that the email address looks valid.. */
1189 if (!is_valid_email_addr(email_addr)) {
1190 reply("NSMSG_BAD_EMAIL_ADDR");
1194 /* .. and that we are allowed to send to it. */
1195 if ((str = mail_prohibited_address(email_addr))) {
1196 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1200 /* If we do email verify, make sure we don't spam the address. */
1201 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1203 for (nn=0; nn<hil->used; nn++) {
1204 if (hil->list[nn]->cookie) {
1205 reply("NSMSG_EMAIL_UNACTIVATED");
1209 if (hil->used >= nickserv_conf.handles_per_email) {
1210 reply("NSMSG_EMAIL_OVERUSED");
1223 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1225 /* Add any masks they should get. */
1226 if (nickserv_conf.default_hostmask) {
1227 string_list_append(hi->masks, strdup("*@*"));
1229 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1230 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1231 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1234 /* If they're the first to register, give them level 1000. */
1235 if (dict_size(nickserv_handle_dict) == 1) {
1236 hi->opserv_level = 1000;
1237 reply("NSMSG_ROOT_HANDLE", argv[1]);
1240 /* Set their email address. */
1242 nickserv_set_email_addr(hi, email_addr);
1244 /* If they need to do email verification, tell them. */
1246 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1248 /* Set registering flag.. */
1249 user->modes |= FLAGS_REGISTERING;
1254 static NICKSERV_FUNC(cmd_oregister)
1257 struct userNode *settee;
1258 struct handle_info *hi;
1260 NICKSERV_MIN_PARMS(3);
1262 if (!is_valid_handle(argv[1])) {
1263 reply("NSMSG_BAD_HANDLE", argv[1]);
1270 } else if (strchr(argv[3], '@')) {
1271 mask = canonicalize_hostmask(strdup(argv[3]));
1273 settee = GetUserH(argv[4]);
1275 reply("MSG_NICK_UNKNOWN", argv[4]);
1282 } else if ((settee = GetUserH(argv[3]))) {
1283 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1285 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1288 if (settee && settee->handle_info) {
1289 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1293 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1298 string_list_append(hi->masks, mask);
1302 static NICKSERV_FUNC(cmd_handleinfo)
1305 unsigned int i, pos=0, herelen;
1306 struct userNode *target, *next_un;
1307 struct handle_info *hi;
1308 const char *nsmsg_none;
1311 if (!(hi = user->handle_info)) {
1312 reply("NSMSG_MUST_AUTH");
1315 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1319 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1320 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1321 #ifdef WITH_PROTOCOL_BAHAMUT
1322 reply("NSMSG_HANDLEINFO_ID", hi->id);
1324 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1327 intervalString(buff, now - hi->lastseen, user->handle_info);
1328 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1330 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1333 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1334 if (HANDLE_FLAGGED(hi, FROZEN))
1335 reply("NSMSG_HANDLEINFO_VACATION");
1337 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1338 struct do_not_register *dnr;
1339 if ((dnr = chanserv_is_dnr(NULL, hi)))
1340 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1341 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1343 } else if (hi != user->handle_info)
1347 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1349 if (nickserv_conf.email_enabled)
1350 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1354 switch (hi->cookie->type) {
1355 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1356 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1357 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1358 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1359 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1365 reply("NSMSG_HANDLEINFO_NO_NOTES");
1367 struct handle_note *prev, *note;
1369 WALK_NOTES(hi, prev, note) {
1370 char set_time[INTERVALLEN];
1371 intervalString(set_time, now - note->set, user->handle_info);
1372 if (note->expires) {
1373 char exp_time[INTERVALLEN];
1374 intervalString(exp_time, note->expires - now, user->handle_info);
1375 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1377 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1383 unsigned long flen = 1;
1384 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1386 for (i=0, flen=1; handle_flags[i]; i++)
1387 if (hi->flags & 1 << i)
1388 flags[flen++] = handle_flags[i];
1390 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1392 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1395 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1396 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1397 || (hi->opserv_level > 0)) {
1398 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1402 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1404 if (hi->last_quit_host[0])
1405 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1407 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1409 if (nickserv_conf.disable_nicks) {
1410 /* nicks disabled; don't show anything about registered nicks */
1411 } else if (hi->nicks) {
1412 struct nick_info *ni, *next_ni;
1413 for (ni = hi->nicks; ni; ni = next_ni) {
1414 herelen = strlen(ni->nick);
1415 if (pos + herelen + 1 > ArrayLength(buff)) {
1417 goto print_nicks_buff;
1421 memcpy(buff+pos, ni->nick, herelen);
1422 pos += herelen; buff[pos++] = ' ';
1426 reply("NSMSG_HANDLEINFO_NICKS", buff);
1431 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1434 if (hi->masks->used) {
1435 for (i=0; i < hi->masks->used; i++) {
1436 herelen = strlen(hi->masks->list[i]);
1437 if (pos + herelen + 1 > ArrayLength(buff)) {
1439 goto print_mask_buff;
1441 memcpy(buff+pos, hi->masks->list[i], herelen);
1442 pos += herelen; buff[pos++] = ' ';
1443 if (i+1 == hi->masks->used) {
1446 reply("NSMSG_HANDLEINFO_MASKS", buff);
1451 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1455 struct userData *channel, *next;
1458 for (channel = hi->channels; channel; channel = next) {
1459 next = channel->u_next;
1460 name = channel->channel->channel->name;
1461 herelen = strlen(name);
1462 if (pos + herelen + 7 > ArrayLength(buff)) {
1464 goto print_chans_buff;
1466 if (IsUserSuspended(channel))
1468 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1472 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1477 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1480 for (target = hi->users; target; target = next_un) {
1481 herelen = strlen(target->nick);
1482 if (pos + herelen + 1 > ArrayLength(buff)) {
1484 goto print_cnick_buff;
1486 next_un = target->next_authed;
1488 memcpy(buff+pos, target->nick, herelen);
1489 pos += herelen; buff[pos++] = ' ';
1493 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1501 static NICKSERV_FUNC(cmd_userinfo)
1503 struct userNode *target;
1505 NICKSERV_MIN_PARMS(2);
1506 if (!(target = GetUserH(argv[1]))) {
1507 reply("MSG_NICK_UNKNOWN", argv[1]);
1510 if (target->handle_info)
1511 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1513 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1517 static NICKSERV_FUNC(cmd_nickinfo)
1519 struct nick_info *ni;
1521 NICKSERV_MIN_PARMS(2);
1522 if (!(ni = get_nick_info(argv[1]))) {
1523 reply("MSG_NICK_UNKNOWN", argv[1]);
1526 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1530 static NICKSERV_FUNC(cmd_notes)
1532 struct handle_info *hi;
1533 struct handle_note *prev, *note;
1536 NICKSERV_MIN_PARMS(2);
1537 if (!(hi = get_victim_oper(user, argv[1])))
1540 WALK_NOTES(hi, prev, note) {
1541 char set_time[INTERVALLEN];
1542 intervalString(set_time, now - note->set, user->handle_info);
1543 if (note->expires) {
1544 char exp_time[INTERVALLEN];
1545 intervalString(exp_time, note->expires - now, user->handle_info);
1546 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1548 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1552 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1556 static NICKSERV_FUNC(cmd_rename_handle)
1558 struct handle_info *hi;
1559 char msgbuf[MAXLEN], *old_handle;
1562 NICKSERV_MIN_PARMS(3);
1563 if (!(hi = get_victim_oper(user, argv[1])))
1565 if (!is_valid_handle(argv[2])) {
1566 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1569 if (get_handle_info(argv[2])) {
1570 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1574 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1575 hi->handle = strdup(argv[2]);
1576 dict_insert(nickserv_handle_dict, hi->handle, hi);
1577 for (nn=0; nn<rf_list_used; nn++)
1578 rf_list[nn](hi, old_handle);
1579 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1580 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1581 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1586 static failpw_func_t *failpw_func_list;
1587 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1590 reg_failpw_func(failpw_func_t func)
1592 if (failpw_func_used == failpw_func_size) {
1593 if (failpw_func_size) {
1594 failpw_func_size <<= 1;
1595 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1597 failpw_func_size = 8;
1598 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1601 failpw_func_list[failpw_func_used++] = func;
1604 static NICKSERV_FUNC(cmd_auth)
1606 int pw_arg, used, maxlogins;
1607 struct handle_info *hi;
1609 struct userNode *other;
1611 if (user->handle_info) {
1612 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1615 if (IsStamped(user)) {
1616 /* Unauthenticated users might still have been stamped
1617 previously and could therefore have a hidden host;
1618 do not allow them to authenticate. */
1619 reply("NSMSG_STAMPED_AUTH");
1623 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1625 } else if (argc == 2) {
1626 if (nickserv_conf.disable_nicks) {
1627 if (!(hi = get_handle_info(user->nick))) {
1628 reply("NSMSG_HANDLE_NOT_FOUND");
1632 /* try to look up their handle from their nick */
1633 struct nick_info *ni;
1634 ni = get_nick_info(user->nick);
1636 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1643 reply("MSG_MISSING_PARAMS", argv[0]);
1644 svccmd_send_help(user, nickserv, cmd);
1648 reply("NSMSG_HANDLE_NOT_FOUND");
1651 /* Responses from here on look up the language used by the handle they asked about. */
1652 passwd = argv[pw_arg];
1653 if (!valid_user_for(user, hi)) {
1654 if (hi->email_addr && nickserv_conf.email_enabled)
1655 send_message_type(4, user, cmd->parent->bot,
1656 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1659 send_message_type(4, user, cmd->parent->bot,
1660 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1662 argv[pw_arg] = "BADMASK";
1665 if (!checkpass(passwd, hi->passwd)) {
1667 send_message_type(4, user, cmd->parent->bot,
1668 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1669 argv[pw_arg] = "BADPASS";
1670 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1671 if (nickserv_conf.autogag_enabled) {
1672 if (!user->auth_policer.params) {
1673 user->auth_policer.last_req = now;
1674 user->auth_policer.params = nickserv_conf.auth_policer_params;
1676 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1678 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1679 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1680 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1682 argv[pw_arg] = "GAGGED";
1687 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1688 send_message_type(4, user, cmd->parent->bot,
1689 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1690 argv[pw_arg] = "SUSPENDED";
1693 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1694 for (used = 0, other = hi->users; other; other = other->next_authed) {
1695 if (++used >= maxlogins) {
1696 send_message_type(4, user, cmd->parent->bot,
1697 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1699 argv[pw_arg] = "MAXLOGINS";
1704 set_user_handle_info(user, hi, 1);
1705 if (nickserv_conf.email_required && !hi->email_addr)
1706 reply("NSMSG_PLEASE_SET_EMAIL");
1707 if (!is_secure_password(hi->handle, passwd, NULL))
1708 reply("NSMSG_WEAK_PASSWORD");
1709 if (hi->passwd[0] != '$')
1710 cryptpass(passwd, hi->passwd);
1711 if (!hi->masks->used) {
1713 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1714 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1715 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1717 argv[pw_arg] = "****";
1718 reply("NSMSG_AUTH_SUCCESS");
1722 static allowauth_func_t *allowauth_func_list;
1723 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1726 reg_allowauth_func(allowauth_func_t func)
1728 if (allowauth_func_used == allowauth_func_size) {
1729 if (allowauth_func_size) {
1730 allowauth_func_size <<= 1;
1731 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1733 allowauth_func_size = 8;
1734 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1737 allowauth_func_list[allowauth_func_used++] = func;
1740 static NICKSERV_FUNC(cmd_allowauth)
1742 struct userNode *target;
1743 struct handle_info *hi;
1746 NICKSERV_MIN_PARMS(2);
1747 if (!(target = GetUserH(argv[1]))) {
1748 reply("MSG_NICK_UNKNOWN", argv[1]);
1751 if (target->handle_info) {
1752 reply("NSMSG_USER_PREV_AUTH", target->nick);
1755 if (IsStamped(target)) {
1756 /* Unauthenticated users might still have been stamped
1757 previously and could therefore have a hidden host;
1758 do not allow them to authenticate to an account. */
1759 reply("NSMSG_USER_PREV_STAMP", target->nick);
1764 else if (!(hi = get_handle_info(argv[2]))) {
1765 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1769 if (hi->opserv_level > user->handle_info->opserv_level) {
1770 reply("MSG_USER_OUTRANKED", hi->handle);
1773 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1774 || (hi->opserv_level > 0))
1775 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1776 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1779 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1780 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1781 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1782 if (nickserv_conf.email_enabled)
1783 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1785 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1786 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1788 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1790 for (n=0; n<allowauth_func_used; n++)
1791 allowauth_func_list[n](user, target, hi);
1795 static NICKSERV_FUNC(cmd_authcookie)
1797 struct handle_info *hi;
1799 NICKSERV_MIN_PARMS(2);
1800 if (user->handle_info) {
1801 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1804 if (IsStamped(user)) {
1805 /* Unauthenticated users might still have been stamped
1806 previously and could therefore have a hidden host;
1807 do not allow them to authenticate to an account. */
1808 reply("NSMSG_STAMPED_AUTHCOOKIE");
1811 if (!(hi = get_handle_info(argv[1]))) {
1812 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1815 if (!hi->email_addr) {
1816 reply("MSG_SET_EMAIL_ADDR");
1819 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1823 static NICKSERV_FUNC(cmd_delcookie)
1825 struct handle_info *hi;
1827 hi = user->handle_info;
1829 reply("NSMSG_NO_COOKIE");
1832 switch (hi->cookie->type) {
1835 reply("NSMSG_MUST_TIME_OUT");
1838 nickserv_eat_cookie(hi->cookie);
1839 reply("NSMSG_ATE_COOKIE");
1845 static NICKSERV_FUNC(cmd_resetpass)
1847 struct handle_info *hi;
1848 char crypted[MD5_CRYPT_LENGTH];
1850 NICKSERV_MIN_PARMS(3);
1851 if (user->handle_info) {
1852 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1855 if (IsStamped(user)) {
1856 /* Unauthenticated users might still have been stamped
1857 previously and could therefore have a hidden host;
1858 do not allow them to activate an account. */
1859 reply("NSMSG_STAMPED_RESETPASS");
1862 if (!(hi = get_handle_info(argv[1]))) {
1863 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1866 if (!hi->email_addr) {
1867 reply("MSG_SET_EMAIL_ADDR");
1870 cryptpass(argv[2], crypted);
1872 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1876 static NICKSERV_FUNC(cmd_cookie)
1878 struct handle_info *hi;
1881 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1884 NICKSERV_MIN_PARMS(3);
1885 if (!(hi = get_handle_info(argv[1]))) {
1886 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1892 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1893 reply("NSMSG_HANDLE_SUSPENDED");
1898 reply("NSMSG_NO_COOKIE");
1902 /* Check validity of operation before comparing cookie to
1903 * prohibit guessing by authed users. */
1904 if (user->handle_info
1905 && (hi->cookie->type != EMAIL_CHANGE)
1906 && (hi->cookie->type != PASSWORD_CHANGE)) {
1907 reply("NSMSG_CANNOT_COOKIE");
1911 if (strcmp(cookie, hi->cookie->cookie)) {
1912 reply("NSMSG_BAD_COOKIE");
1916 switch (hi->cookie->type) {
1918 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1919 set_user_handle_info(user, hi, 1);
1920 reply("NSMSG_HANDLE_ACTIVATED");
1922 case PASSWORD_CHANGE:
1923 set_user_handle_info(user, hi, 1);
1924 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1925 reply("NSMSG_PASSWORD_CHANGED");
1928 nickserv_set_email_addr(hi, hi->cookie->data);
1929 reply("NSMSG_EMAIL_CHANGED");
1932 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1933 set_user_handle_info(user, hi, 1);
1934 nickserv_addmask(user, hi, mask);
1935 reply("NSMSG_AUTH_SUCCESS");
1940 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1941 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1945 nickserv_eat_cookie(hi->cookie);
1950 static NICKSERV_FUNC(cmd_oregnick) {
1952 struct handle_info *target;
1953 struct nick_info *ni;
1955 NICKSERV_MIN_PARMS(3);
1956 if (!(target = modcmd_get_handle_info(user, argv[1])))
1959 if (!is_registerable_nick(nick)) {
1960 reply("NSMSG_BAD_NICK", nick);
1963 ni = dict_find(nickserv_nick_dict, nick, NULL);
1965 reply("NSMSG_NICK_EXISTS", nick);
1968 register_nick(nick, target);
1969 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1973 static NICKSERV_FUNC(cmd_regnick) {
1975 struct nick_info *ni;
1977 if (!is_registerable_nick(user->nick)) {
1978 reply("NSMSG_BAD_NICK", user->nick);
1981 /* count their nicks, see if it's too many */
1982 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1983 if (n >= nickserv_conf.nicks_per_handle) {
1984 reply("NSMSG_TOO_MANY_NICKS");
1987 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1989 reply("NSMSG_NICK_EXISTS", user->nick);
1992 register_nick(user->nick, user->handle_info);
1993 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1997 static NICKSERV_FUNC(cmd_pass)
1999 struct handle_info *hi;
2000 const char *old_pass, *new_pass;
2002 NICKSERV_MIN_PARMS(3);
2003 hi = user->handle_info;
2007 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2008 if (!checkpass(old_pass, hi->passwd)) {
2009 argv[1] = "BADPASS";
2010 reply("NSMSG_PASSWORD_INVALID");
2013 cryptpass(new_pass, hi->passwd);
2015 reply("NSMSG_PASS_SUCCESS");
2020 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2023 char *new_mask = canonicalize_hostmask(strdup(mask));
2024 for (i=0; i<hi->masks->used; i++) {
2025 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2026 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2031 string_list_append(hi->masks, new_mask);
2032 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2036 static NICKSERV_FUNC(cmd_addmask)
2039 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2040 int res = nickserv_addmask(user, user->handle_info, mask);
2044 if (!is_gline(argv[1])) {
2045 reply("NSMSG_MASK_INVALID", argv[1]);
2048 return nickserv_addmask(user, user->handle_info, argv[1]);
2052 static NICKSERV_FUNC(cmd_oaddmask)
2054 struct handle_info *hi;
2056 NICKSERV_MIN_PARMS(3);
2057 if (!(hi = get_victim_oper(user, argv[1])))
2059 return nickserv_addmask(user, hi, argv[2]);
2063 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2066 for (i=0; i<hi->masks->used; i++) {
2067 if (!strcmp(del_mask, hi->masks->list[i])) {
2068 char *old_mask = hi->masks->list[i];
2069 if (hi->masks->used == 1 && !force) {
2070 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2073 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2074 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2079 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2083 static NICKSERV_FUNC(cmd_delmask)
2085 NICKSERV_MIN_PARMS(2);
2086 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2089 static NICKSERV_FUNC(cmd_odelmask)
2091 struct handle_info *hi;
2092 NICKSERV_MIN_PARMS(3);
2093 if (!(hi = get_victim_oper(user, argv[1])))
2095 return nickserv_delmask(user, hi, argv[2], 1);
2099 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2100 unsigned int nn, add = 1, pos;
2101 unsigned long added, removed, flag;
2103 for (added=removed=nn=0; str[nn]; nn++) {
2105 case '+': add = 1; break;
2106 case '-': add = 0; break;
2108 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2109 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2112 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2113 /* cheesy avoidance of looking up the flag name.. */
2114 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2117 flag = 1 << (pos - 1);
2119 added |= flag, removed &= ~flag;
2121 removed |= flag, added &= ~flag;
2126 *premoved = removed;
2131 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2133 unsigned long before, after, added, removed;
2134 struct userNode *uNode;
2136 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2137 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2139 hi->flags = (hi->flags | added) & ~removed;
2140 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2142 /* Strip helping flag if they're only a support helper and not
2143 * currently in #support. */
2144 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2145 struct channelList *schannels;
2147 schannels = chanserv_support_channels();
2148 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2149 for (ii = 0; ii < schannels->used; ++ii)
2150 if (GetUserMode(schannels->list[ii], uNode))
2152 if (ii < schannels->used)
2156 HANDLE_CLEAR_FLAG(hi, HELPING);
2159 if (after && !before) {
2160 /* Add user to current helper list. */
2161 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2162 userList_append(&curr_helpers, uNode);
2163 } else if (!after && before) {
2164 /* Remove user from current helper list. */
2165 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2166 userList_remove(&curr_helpers, uNode);
2173 set_list(struct userNode *user, struct handle_info *hi, int override)
2177 char *set_display[] = {
2178 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2179 "EMAIL", "MAXLOGINS", "LANGUAGE"
2182 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2184 /* Do this so options are presented in a consistent order. */
2185 for (i = 0; i < ArrayLength(set_display); ++i)
2186 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2187 opt(user, hi, override, 0, NULL);
2190 static NICKSERV_FUNC(cmd_set)
2192 struct handle_info *hi;
2195 hi = user->handle_info;
2197 set_list(user, hi, 0);
2200 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2201 reply("NSMSG_INVALID_OPTION", argv[1]);
2204 return opt(user, hi, 0, argc-1, argv+1);
2207 static NICKSERV_FUNC(cmd_oset)
2209 struct handle_info *hi;
2210 struct svccmd *subcmd;
2212 char cmdname[MAXLEN];
2214 NICKSERV_MIN_PARMS(2);
2216 if (!(hi = get_victim_oper(user, argv[1])))
2220 set_list(user, hi, 0);
2224 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2225 reply("NSMSG_INVALID_OPTION", argv[2]);
2229 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2230 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2231 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2234 return opt(user, hi, 1, argc-2, argv+2);
2237 static OPTION_FUNC(opt_info)
2241 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2243 hi->infoline = NULL;
2245 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2249 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2250 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2254 static OPTION_FUNC(opt_width)
2257 hi->screen_width = strtoul(argv[1], NULL, 0);
2259 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2260 hi->screen_width = MIN_LINE_SIZE;
2261 else if (hi->screen_width > MAX_LINE_SIZE)
2262 hi->screen_width = MAX_LINE_SIZE;
2264 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2268 static OPTION_FUNC(opt_tablewidth)
2271 hi->table_width = strtoul(argv[1], NULL, 0);
2273 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2274 hi->table_width = MIN_LINE_SIZE;
2275 else if (hi->screen_width > MAX_LINE_SIZE)
2276 hi->table_width = MAX_LINE_SIZE;
2278 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2282 static OPTION_FUNC(opt_color)
2285 if (enabled_string(argv[1]))
2286 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2287 else if (disabled_string(argv[1]))
2288 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2290 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2295 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2299 static OPTION_FUNC(opt_privmsg)
2302 if (enabled_string(argv[1]))
2303 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2304 else if (disabled_string(argv[1]))
2305 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2307 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2312 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2316 static OPTION_FUNC(opt_style)
2321 if (!irccasecmp(argv[1], "Zoot"))
2322 hi->userlist_style = HI_STYLE_ZOOT;
2323 else if (!irccasecmp(argv[1], "def"))
2324 hi->userlist_style = HI_STYLE_DEF;
2327 switch (hi->userlist_style) {
2336 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2340 static OPTION_FUNC(opt_password)
2343 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2348 cryptpass(argv[1], hi->passwd);
2350 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2354 static OPTION_FUNC(opt_flags)
2357 unsigned int ii, flen;
2360 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2365 nickserv_apply_flags(user, hi, argv[1]);
2367 for (ii = flen = 0; handle_flags[ii]; ii++)
2368 if (hi->flags & (1 << ii))
2369 flags[flen++] = handle_flags[ii];
2372 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2374 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2378 static OPTION_FUNC(opt_email)
2382 if (!is_valid_email_addr(argv[1])) {
2383 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2386 if ((str = mail_prohibited_address(argv[1]))) {
2387 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2390 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2391 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2393 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2395 nickserv_set_email_addr(hi, argv[1]);
2397 nickserv_eat_cookie(hi->cookie);
2398 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2401 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2405 static OPTION_FUNC(opt_maxlogins)
2407 unsigned char maxlogins;
2409 maxlogins = strtoul(argv[1], NULL, 0);
2410 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2411 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2414 hi->maxlogins = maxlogins;
2416 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2417 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2421 static OPTION_FUNC(opt_language)
2423 struct language *lang;
2425 lang = language_find(argv[1]);
2426 if (irccasecmp(lang->name, argv[1]))
2427 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2428 hi->language = lang;
2430 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2434 static OPTION_FUNC(opt_karma)
2437 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2442 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2443 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2444 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2445 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2447 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2451 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2456 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2457 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2459 if ((user->handle_info->opserv_level < target->opserv_level)
2460 || ((user->handle_info->opserv_level == target->opserv_level)
2461 && (user->handle_info->opserv_level < 1000))) {
2462 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2465 if ((user->handle_info->opserv_level < new_level)
2466 || ((user->handle_info->opserv_level == new_level)
2467 && (user->handle_info->opserv_level < 1000))) {
2468 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2471 if (user->handle_info == target) {
2472 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2475 if (target->opserv_level == new_level)
2477 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2478 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2479 target->opserv_level = new_level;
2483 static OPTION_FUNC(opt_level)
2488 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2492 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2493 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2497 static OPTION_FUNC(opt_epithet)
2500 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2504 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2505 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2508 if ((epithet[0] == '*') && !epithet[1])
2511 hi->epithet = strdup(epithet);
2515 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2517 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2521 static OPTION_FUNC(opt_title)
2526 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2530 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2532 if (strchr(title, '.')) {
2533 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2536 if ((strlen(user->handle_info->handle) + strlen(title) +
2537 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2538 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2543 if (!strcmp(title, "*")) {
2544 hi->fakehost = NULL;
2546 hi->fakehost = malloc(strlen(title)+2);
2547 hi->fakehost[0] = '.';
2548 strcpy(hi->fakehost+1, title);
2551 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2552 title = hi->fakehost + 1;
2556 title = user_find_message(user, "MSG_NONE");
2557 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2561 static OPTION_FUNC(opt_fakehost)
2566 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2570 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2572 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2573 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2577 if (!strcmp(fake, "*"))
2578 hi->fakehost = NULL;
2580 hi->fakehost = strdup(fake);
2581 fake = hi->fakehost;
2584 fake = generate_fakehost(hi);
2586 fake = user_find_message(user, "MSG_NONE");
2587 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2591 static NICKSERV_FUNC(cmd_reclaim)
2593 struct handle_info *hi;
2594 struct nick_info *ni;
2595 struct userNode *victim;
2597 NICKSERV_MIN_PARMS(2);
2598 hi = user->handle_info;
2599 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2601 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2604 if (ni->owner != user->handle_info) {
2605 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2608 victim = GetUserH(ni->nick);
2610 reply("MSG_NICK_UNKNOWN", ni->nick);
2613 if (victim == user) {
2614 reply("NSMSG_NICK_USER_YOU");
2617 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2618 switch (nickserv_conf.reclaim_action) {
2619 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2620 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2621 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2622 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2627 static NICKSERV_FUNC(cmd_unregnick)
2630 struct handle_info *hi;
2631 struct nick_info *ni;
2633 hi = user->handle_info;
2634 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2635 ni = dict_find(nickserv_nick_dict, nick, NULL);
2637 reply("NSMSG_UNKNOWN_NICK", nick);
2640 if (hi != ni->owner) {
2641 reply("NSMSG_NOT_YOUR_NICK", nick);
2644 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2649 static NICKSERV_FUNC(cmd_ounregnick)
2651 struct nick_info *ni;
2653 NICKSERV_MIN_PARMS(2);
2654 if (!(ni = get_nick_info(argv[1]))) {
2655 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2658 if (!oper_outranks(user, ni->owner))
2660 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2665 static NICKSERV_FUNC(cmd_unregister)
2667 struct handle_info *hi;
2670 NICKSERV_MIN_PARMS(2);
2671 hi = user->handle_info;
2674 if (checkpass(passwd, hi->passwd)) {
2675 nickserv_unregister_handle(hi, user);
2678 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2679 reply("NSMSG_PASSWORD_INVALID");
2684 static NICKSERV_FUNC(cmd_ounregister)
2686 struct handle_info *hi;
2687 char reason[MAXLEN];
2689 NICKSERV_MIN_PARMS(2);
2690 if (!(hi = get_victim_oper(user, argv[1])))
2692 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2693 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2694 nickserv_unregister_handle(hi, user);
2698 static NICKSERV_FUNC(cmd_status)
2700 if (nickserv_conf.disable_nicks) {
2701 reply("NSMSG_GLOBAL_STATS_NONICK",
2702 dict_size(nickserv_handle_dict));
2704 if (user->handle_info) {
2706 struct nick_info *ni;
2707 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2708 reply("NSMSG_HANDLE_STATS", cnt);
2710 reply("NSMSG_HANDLE_NONE");
2712 reply("NSMSG_GLOBAL_STATS",
2713 dict_size(nickserv_handle_dict),
2714 dict_size(nickserv_nick_dict));
2719 static NICKSERV_FUNC(cmd_ghost)
2721 struct userNode *target;
2722 char reason[MAXLEN];
2724 NICKSERV_MIN_PARMS(2);
2725 if (!(target = GetUserH(argv[1]))) {
2726 reply("MSG_NICK_UNKNOWN", argv[1]);
2729 if (target == user) {
2730 reply("NSMSG_CANNOT_GHOST_SELF");
2733 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2734 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2737 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2738 DelUser(target, nickserv, 1, reason);
2739 reply("NSMSG_GHOST_KILLED", argv[1]);
2743 static NICKSERV_FUNC(cmd_vacation)
2745 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2746 reply("NSMSG_ON_VACATION");
2750 static NICKSERV_FUNC(cmd_addnote)
2752 struct handle_info *hi;
2753 unsigned long duration;
2756 struct handle_note *prev;
2757 struct handle_note *note;
2759 /* Parse parameters and figure out values for note's fields. */
2760 NICKSERV_MIN_PARMS(4);
2761 hi = get_victim_oper(user, argv[1]);
2764 duration = ParseInterval(argv[2]);
2765 if (duration > 2*365*86400) {
2766 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2769 unsplit_string(argv + 3, argc - 3, text);
2770 WALK_NOTES(hi, prev, note) {}
2771 id = prev ? (prev->id + 1) : 1;
2773 /* Create the new note structure. */
2774 note = calloc(1, sizeof(*note) + strlen(text));
2776 note->expires = duration ? (now + duration) : 0;
2779 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2780 strcpy(note->note, text);
2785 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2789 static NICKSERV_FUNC(cmd_delnote)
2791 struct handle_info *hi;
2792 struct handle_note *prev;
2793 struct handle_note *note;
2796 NICKSERV_MIN_PARMS(3);
2797 hi = get_victim_oper(user, argv[1]);
2800 id = strtoul(argv[2], NULL, 10);
2801 WALK_NOTES(hi, prev, note) {
2802 if (id == note->id) {
2804 prev->next = note->next;
2806 hi->notes = note->next;
2808 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2812 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2817 nickserv_saxdb_write(struct saxdb_context *ctx) {
2819 struct handle_info *hi;
2822 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2824 #ifdef WITH_PROTOCOL_BAHAMUT
2827 saxdb_start_record(ctx, iter_key(it), 0);
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 *fakehostmask;
3070 const char *handlemask;
3071 const char *emailmask;
3074 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3076 struct discrim_apply_info {
3077 struct nickserv_discrim *discrim;
3078 discrim_search_func func;
3079 struct userNode *source;
3080 unsigned int matched;
3083 static struct nickserv_discrim *
3084 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3087 struct nickserv_discrim *discrim;
3089 discrim = malloc(sizeof(*discrim));
3090 memset(discrim, 0, sizeof(*discrim));
3091 discrim->min_level = 0;
3092 discrim->max_level = INT_MAX;
3093 discrim->limit = 50;
3094 discrim->min_registered = 0;
3095 discrim->max_registered = INT_MAX;
3096 discrim->lastseen = LONG_MAX;
3097 discrim->min_karma = INT_MIN;
3098 discrim->max_karma = INT_MAX;
3100 for (i=0; i<argc; i++) {
3101 if (i == argc - 1) {
3102 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3105 if (!irccasecmp(argv[i], "limit")) {
3106 discrim->limit = strtoul(argv[++i], NULL, 0);
3107 } else if (!irccasecmp(argv[i], "flags")) {
3108 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3109 } else if (!irccasecmp(argv[i], "registered")) {
3110 const char *cmp = argv[++i];
3111 if (cmp[0] == '<') {
3112 if (cmp[1] == '=') {
3113 discrim->min_registered = now - ParseInterval(cmp+2);
3115 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3117 } else if (cmp[0] == '=') {
3118 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3119 } else if (cmp[0] == '>') {
3120 if (cmp[1] == '=') {
3121 discrim->max_registered = now - ParseInterval(cmp+2);
3123 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3126 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3128 } else if (!irccasecmp(argv[i], "seen")) {
3129 discrim->lastseen = now - ParseInterval(argv[++i]);
3130 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3131 discrim->nickmask = argv[++i];
3132 } else if (!irccasecmp(argv[i], "hostmask")) {
3134 if (!irccasecmp(argv[i], "exact")) {
3135 if (i == argc - 1) {
3136 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3139 discrim->hostmask_type = EXACT;
3140 } else if (!irccasecmp(argv[i], "subset")) {
3141 if (i == argc - 1) {
3142 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3145 discrim->hostmask_type = SUBSET;
3146 } else if (!irccasecmp(argv[i], "superset")) {
3147 if (i == argc - 1) {
3148 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3151 discrim->hostmask_type = SUPERSET;
3152 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3153 if (i == argc - 1) {
3154 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3157 discrim->hostmask_type = LASTQUIT;
3160 discrim->hostmask_type = SUPERSET;
3162 discrim->hostmask = argv[++i];
3163 } else if (!irccasecmp(argv[i], "fakehost")) {
3164 if (!irccasecmp(argv[++i], "*")) {
3165 discrim->fakehostmask = 0;
3167 discrim->fakehostmask = argv[i];
3169 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3170 if (!irccasecmp(argv[++i], "*")) {
3171 discrim->handlemask = 0;
3173 discrim->handlemask = argv[i];
3175 } else if (!irccasecmp(argv[i], "email")) {
3176 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3177 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3179 } else if (!irccasecmp(argv[++i], "*")) {
3180 discrim->emailmask = 0;
3182 discrim->emailmask = argv[i];
3184 } else if (!irccasecmp(argv[i], "access")) {
3185 const char *cmp = argv[++i];
3186 if (cmp[0] == '<') {
3187 if (discrim->min_level == 0) discrim->min_level = 1;
3188 if (cmp[1] == '=') {
3189 discrim->max_level = strtoul(cmp+2, NULL, 0);
3191 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3193 } else if (cmp[0] == '=') {
3194 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3195 } else if (cmp[0] == '>') {
3196 if (cmp[1] == '=') {
3197 discrim->min_level = strtoul(cmp+2, NULL, 0);
3199 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3202 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3204 } else if (!irccasecmp(argv[i], "karma")) {
3205 const char *cmp = argv[++i];
3206 if (cmp[0] == '<') {
3207 if (cmp[1] == '=') {
3208 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3210 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3212 } else if (cmp[0] == '=') {
3213 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3214 } else if (cmp[0] == '>') {
3215 if (cmp[1] == '=') {
3216 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3218 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3221 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3224 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3235 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3237 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3238 || (discrim->flags_off & hi->flags)
3239 || (discrim->min_registered > hi->registered)
3240 || (discrim->max_registered < hi->registered)
3241 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3242 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3243 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3244 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3245 || (discrim->min_level > hi->opserv_level)
3246 || (discrim->max_level < hi->opserv_level)
3247 || (discrim->min_karma > hi->karma)
3248 || (discrim->max_karma < hi->karma)
3252 if (discrim->hostmask) {
3254 for (i=0; i<hi->masks->used; i++) {
3255 const char *mask = hi->masks->list[i];
3256 if ((discrim->hostmask_type == SUBSET)
3257 && (match_ircglobs(discrim->hostmask, mask))) break;
3258 else if ((discrim->hostmask_type == EXACT)
3259 && !irccasecmp(discrim->hostmask, mask)) break;
3260 else if ((discrim->hostmask_type == SUPERSET)
3261 && (match_ircglobs(mask, discrim->hostmask))) break;
3262 else if ((discrim->hostmask_type == LASTQUIT)
3263 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3265 if (i==hi->masks->used) return 0;
3267 if (discrim->nickmask) {
3268 struct nick_info *nick = hi->nicks;
3270 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3273 if (!nick) return 0;
3279 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3281 dict_iterator_t it, next;
3282 unsigned int matched;
3284 for (it = dict_first(nickserv_handle_dict), matched = 0;
3285 it && (matched < discrim->limit);
3287 next = iter_next(it);
3288 if (nickserv_discrim_match(discrim, iter_data(it))) {
3289 dsf(source, iter_data(it));
3297 search_print_func(struct userNode *source, struct handle_info *match)
3299 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3303 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3308 search_unregister_func (struct userNode *source, struct handle_info *match)
3310 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3311 nickserv_unregister_handle(match, source);
3315 nickserv_sort_accounts_by_access(const void *a, const void *b)
3317 const struct handle_info *hi_a = *(const struct handle_info**)a;
3318 const struct handle_info *hi_b = *(const struct handle_info**)b;
3319 if (hi_a->opserv_level != hi_b->opserv_level)
3320 return hi_b->opserv_level - hi_a->opserv_level;
3321 return irccasecmp(hi_a->handle, hi_b->handle);
3325 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3327 struct handle_info_list hil;
3328 struct helpfile_table tbl;
3333 memset(&hil, 0, sizeof(hil));
3334 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3335 struct handle_info *hi = iter_data(it);
3336 if (hi->opserv_level)
3337 handle_info_list_append(&hil, hi);
3339 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3340 tbl.length = hil.used + 1;
3342 tbl.flags = TABLE_NO_FREE;
3343 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3344 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3347 for (ii = 0; ii < hil.used; ) {
3348 ary = malloc(tbl.width * sizeof(ary[0]));
3349 ary[0] = hil.list[ii]->handle;
3350 ary[1] = strtab(hil.list[ii]->opserv_level);
3351 tbl.contents[++ii] = ary;
3353 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3354 reply("MSG_MATCH_COUNT", hil.used);
3355 for (ii = 0; ii < hil.used; ii++)
3356 free(tbl.contents[ii]);
3361 static NICKSERV_FUNC(cmd_search)
3363 struct nickserv_discrim *discrim;
3364 discrim_search_func action;
3365 struct svccmd *subcmd;
3366 unsigned int matches;
3369 NICKSERV_MIN_PARMS(3);
3370 sprintf(buf, "search %s", argv[1]);
3371 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3372 if (!irccasecmp(argv[1], "print"))
3373 action = search_print_func;
3374 else if (!irccasecmp(argv[1], "count"))
3375 action = search_count_func;
3376 else if (!irccasecmp(argv[1], "unregister"))
3377 action = search_unregister_func;
3379 reply("NSMSG_INVALID_ACTION", argv[1]);
3383 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3386 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3390 if (action == search_print_func)
3391 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3392 else if (action == search_count_func)
3393 discrim->limit = INT_MAX;
3395 matches = nickserv_discrim_search(discrim, action, user);
3398 reply("MSG_MATCH_COUNT", matches);
3400 reply("MSG_NO_MATCHES");
3406 static MODCMD_FUNC(cmd_checkpass)
3408 struct handle_info *hi;
3410 NICKSERV_MIN_PARMS(3);
3411 if (!(hi = get_handle_info(argv[1]))) {
3412 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3415 if (checkpass(argv[2], hi->passwd))
3416 reply("CHECKPASS_YES");
3418 reply("CHECKPASS_NO");
3424 nickserv_db_read_handle(const char *handle, dict_t obj)
3427 struct string_list *masks, *slist;
3428 struct handle_info *hi;
3429 struct userNode *authed_users;
3430 struct userData *channels;
3431 unsigned long int id;
3435 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3436 id = str ? strtoul(str, NULL, 0) : 0;
3437 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3439 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3442 if ((hi = get_handle_info(handle))) {
3443 authed_users = hi->users;
3444 channels = hi->channels;
3446 hi->channels = NULL;
3447 dict_remove(nickserv_handle_dict, hi->handle);
3449 authed_users = NULL;
3452 hi = register_handle(handle, str, id);
3454 hi->users = authed_users;
3455 while (authed_users) {
3456 authed_users->handle_info = hi;
3457 authed_users = authed_users->next_authed;
3460 hi->channels = channels;
3461 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3462 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3463 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3464 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3465 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3466 hi->language = language_find(str ? str : "C");
3467 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3468 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3469 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3471 hi->infoline = strdup(str);
3472 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3473 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3474 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3475 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3476 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3477 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3478 /* We want to read the nicks even if disable_nicks is set. This is so
3479 * that we don't lose the nick data entirely. */
3480 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3482 for (ii=0; ii<slist->used; ii++)
3483 register_nick(slist->list[ii], hi);
3485 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3487 for (ii=0; str[ii]; ii++)
3488 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3490 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3491 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3492 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3493 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3494 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3495 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3496 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3498 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3500 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3501 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3503 nickserv_set_email_addr(hi, str);
3504 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3506 hi->epithet = strdup(str);
3507 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3509 hi->fakehost = strdup(str);
3510 /* Read the "cookie" sub-database (if it exists). */
3511 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3513 const char *data, *type, *expires, *cookie_str;
3514 struct handle_cookie *cookie;
3516 cookie = calloc(1, sizeof(*cookie));
3517 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3518 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3519 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3520 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3521 if (!type || !expires || !cookie_str) {
3522 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3525 if (!irccasecmp(type, KEY_ACTIVATION))
3526 cookie->type = ACTIVATION;
3527 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3528 cookie->type = PASSWORD_CHANGE;
3529 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3530 cookie->type = EMAIL_CHANGE;
3531 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3532 cookie->type = ALLOWAUTH;
3534 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3537 cookie->expires = strtoul(expires, NULL, 0);
3538 if (cookie->expires < now)
3541 cookie->data = strdup(data);
3542 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3546 nickserv_bake_cookie(cookie);
3548 nickserv_free_cookie(cookie);
3550 /* Read the "notes" sub-database (if it exists). */
3551 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3554 struct handle_note *last_note;
3555 struct handle_note *note;
3558 for (it = dict_first(subdb); it; it = iter_next(it)) {
3559 const char *expires;
3567 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3569 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", id, hi->handle);
3572 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3573 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3574 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3575 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3576 if (!setter || !text || !set) {
3577 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", id, hi->handle);
3580 note = calloc(1, sizeof(*note) + strlen(text));
3582 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3583 note->set = strtoul(set, NULL, 10);
3584 note->id = strtoul(id, NULL, 10);
3585 safestrncpy(note->setter, setter, sizeof(note->setter));
3586 strcpy(note->note, text);
3588 last_note->next = note;
3597 nickserv_saxdb_read(dict_t db) {
3599 struct record_data *rd;
3601 for (it=dict_first(db); it; it=iter_next(it)) {
3603 nickserv_db_read_handle(iter_key(it), rd->d.object);
3608 static NICKSERV_FUNC(cmd_mergedb)
3610 struct timeval start, stop;
3613 NICKSERV_MIN_PARMS(2);
3614 gettimeofday(&start, NULL);
3615 if (!(db = parse_database(argv[1]))) {
3616 reply("NSMSG_DB_UNREADABLE", argv[1]);
3619 nickserv_saxdb_read(db);
3621 gettimeofday(&stop, NULL);
3622 stop.tv_sec -= start.tv_sec;
3623 stop.tv_usec -= start.tv_usec;
3624 if (stop.tv_usec < 0) {
3626 stop.tv_usec += 1000000;
3628 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3633 expire_handles(UNUSED_ARG(void *data))
3635 dict_iterator_t it, next;
3637 struct handle_info *hi;
3639 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3640 next = iter_next(it);
3642 if ((hi->opserv_level > 0)
3644 || HANDLE_FLAGGED(hi, FROZEN)
3645 || HANDLE_FLAGGED(hi, NODELETE)) {
3648 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3649 if ((now - hi->lastseen) > expiry) {
3650 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3651 nickserv_unregister_handle(hi, NULL);
3655 if (nickserv_conf.handle_expire_frequency)
3656 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3660 nickserv_load_dict(const char *fname)
3664 if (!(file = fopen(fname, "r"))) {
3665 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3668 while (!feof(file)) {
3669 fgets(line, sizeof(line), file);
3672 if (line[strlen(line)-1] == '\n')
3673 line[strlen(line)-1] = 0;
3674 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3677 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3680 static enum reclaim_action
3681 reclaim_action_from_string(const char *str) {
3683 return RECLAIM_NONE;
3684 else if (!irccasecmp(str, "warn"))
3685 return RECLAIM_WARN;
3686 else if (!irccasecmp(str, "svsnick"))
3687 return RECLAIM_SVSNICK;
3688 else if (!irccasecmp(str, "kill"))
3689 return RECLAIM_KILL;
3691 return RECLAIM_NONE;
3695 nickserv_conf_read(void)
3697 dict_t conf_node, child;
3701 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3702 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3705 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3707 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3708 if (nickserv_conf.valid_handle_regex_set)
3709 regfree(&nickserv_conf.valid_handle_regex);
3711 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3712 nickserv_conf.valid_handle_regex_set = !err;
3713 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3715 nickserv_conf.valid_handle_regex_set = 0;
3717 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3718 if (nickserv_conf.valid_nick_regex_set)
3719 regfree(&nickserv_conf.valid_nick_regex);
3721 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3722 nickserv_conf.valid_nick_regex_set = !err;
3723 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3725 nickserv_conf.valid_nick_regex_set = 0;
3727 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3729 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3730 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3731 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3732 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3733 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3734 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3735 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3736 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3737 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3738 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3739 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3740 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3741 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3742 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3743 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3744 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3745 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3746 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3747 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3748 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3749 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3750 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3751 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3752 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3753 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3755 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3756 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3757 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3759 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3760 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3761 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3763 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3764 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3765 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3766 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3767 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3768 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3769 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3770 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3771 if (!nickserv_conf.disable_nicks) {
3772 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3773 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3774 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3775 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3776 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3777 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3778 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3779 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3781 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3782 for (it=dict_first(child); it; it=iter_next(it)) {
3783 const char *key = iter_key(it), *value;
3787 if (!strncasecmp(key, "uc_", 3))
3788 flag = toupper(key[3]);
3789 else if (!strncasecmp(key, "lc_", 3))
3790 flag = tolower(key[3]);
3794 if ((pos = handle_inverse_flags[flag])) {
3795 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3796 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3799 if (nickserv_conf.weak_password_dict)
3800 dict_delete(nickserv_conf.weak_password_dict);
3801 nickserv_conf.weak_password_dict = dict_new();
3802 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3803 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3804 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3805 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3807 nickserv_load_dict(str);
3808 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3809 if (nickserv && str)
3810 NickChange(nickserv, str, 0);
3811 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3812 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3813 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3814 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3815 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3816 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3817 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3818 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3819 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3820 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3821 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3822 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3823 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3824 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3825 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3826 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3827 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3828 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3829 str = conf_get_data("server/network", RECDB_QSTRING);
3830 nickserv_conf.network_name = str ? str : "some IRC network";
3831 if (!nickserv_conf.auth_policer_params) {
3832 nickserv_conf.auth_policer_params = policer_params_new();
3833 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3834 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3836 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3837 for (it=dict_first(child); it; it=iter_next(it))
3838 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3842 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3844 char newnick[NICKLEN+1];
3853 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3855 case RECLAIM_SVSNICK:
3857 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3858 } while (GetUserH(newnick));
3859 irc_svsnick(nickserv, user, newnick);
3862 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3863 DelUser(user, nickserv, 1, msg);
3869 nickserv_reclaim_p(void *data) {
3870 struct userNode *user = data;
3871 struct nick_info *ni = get_nick_info(user->nick);
3873 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3877 check_user_nick(struct userNode *user) {
3878 struct nick_info *ni;
3879 user->modes &= ~FLAGS_REGNICK;
3880 if (!(ni = get_nick_info(user->nick)))
3882 if (user->handle_info == ni->owner) {
3883 user->modes |= FLAGS_REGNICK;
3887 if (nickserv_conf.warn_nick_owned)
3888 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3889 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3891 if (nickserv_conf.auto_reclaim_delay)
3892 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3894 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3899 handle_new_user(struct userNode *user)
3901 return check_user_nick(user);
3905 handle_account(struct userNode *user, const char *stamp)
3907 struct handle_info *hi;
3909 #ifdef WITH_PROTOCOL_P10
3910 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3912 hi = dict_find(nickserv_id_dict, stamp, NULL);
3916 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3919 set_user_handle_info(user, hi, 0);
3921 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3926 handle_nick_change(struct userNode *user, const char *old_nick)
3928 struct handle_info *hi;
3930 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3931 dict_remove(nickserv_allow_auth_dict, old_nick);
3932 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3934 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3935 check_user_nick(user);
3939 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3941 dict_remove(nickserv_allow_auth_dict, user->nick);
3942 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3943 set_user_handle_info(user, NULL, 0);
3946 static struct modcmd *
3947 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3949 if (min_level > 0) {
3951 sprintf(buf, "%u", min_level);
3952 if (must_be_qualified) {
3953 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3955 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3957 } else if (min_level == 0) {
3958 if (must_be_qualified) {
3959 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3961 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3964 if (must_be_qualified) {
3965 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3967 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3973 nickserv_db_cleanup(void)
3975 unreg_del_user_func(nickserv_remove_user);
3976 userList_clean(&curr_helpers);
3977 policer_params_delete(nickserv_conf.auth_policer_params);
3978 dict_delete(nickserv_handle_dict);
3979 dict_delete(nickserv_nick_dict);
3980 dict_delete(nickserv_opt_dict);
3981 dict_delete(nickserv_allow_auth_dict);
3982 dict_delete(nickserv_email_dict);
3983 dict_delete(nickserv_id_dict);
3984 dict_delete(nickserv_conf.weak_password_dict);
3985 free(auth_func_list);
3986 free(unreg_func_list);
3988 free(allowauth_func_list);
3989 free(handle_merge_func_list);
3990 free(failpw_func_list);
3991 if (nickserv_conf.valid_handle_regex_set)
3992 regfree(&nickserv_conf.valid_handle_regex);
3993 if (nickserv_conf.valid_nick_regex_set)
3994 regfree(&nickserv_conf.valid_nick_regex);
3998 init_nickserv(const char *nick)
4001 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4002 reg_new_user_func(handle_new_user);
4003 reg_nick_change_func(handle_nick_change);
4004 reg_del_user_func(nickserv_remove_user);
4005 reg_account_func(handle_account);
4007 /* set up handle_inverse_flags */
4008 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4009 for (i=0; handle_flags[i]; i++) {
4010 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4011 flag_access_levels[i] = 0;
4014 conf_register_reload(nickserv_conf_read);
4015 nickserv_opt_dict = dict_new();
4016 nickserv_email_dict = dict_new();
4017 dict_set_free_keys(nickserv_email_dict, free);
4018 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4020 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4021 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4022 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4023 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4024 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4025 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4026 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4027 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4028 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4029 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4030 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4031 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4032 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4033 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4034 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4035 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4036 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4037 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4038 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4039 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4040 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4041 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4042 if (!nickserv_conf.disable_nicks) {
4043 /* nick management commands */
4044 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4045 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4046 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4047 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4048 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4049 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4051 if (nickserv_conf.email_enabled) {
4052 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4053 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4054 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4055 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4056 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4058 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4059 /* miscellaneous commands */
4060 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4061 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4062 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4063 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4064 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4066 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4067 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4068 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4069 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4070 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4071 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4072 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4073 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4074 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4075 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4076 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4077 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4078 if (nickserv_conf.titlehost_suffix) {
4079 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4080 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4082 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4083 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4084 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4085 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4087 nickserv_handle_dict = dict_new();
4088 dict_set_free_keys(nickserv_handle_dict, free);
4089 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4091 nickserv_id_dict = dict_new();
4092 dict_set_free_keys(nickserv_id_dict, free);
4094 nickserv_nick_dict = dict_new();
4095 dict_set_free_data(nickserv_nick_dict, free);
4097 nickserv_allow_auth_dict = dict_new();
4099 userList_init(&curr_helpers);
4102 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4103 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4104 nickserv_service = service_register(nickserv);
4106 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4107 reg_exit_func(nickserv_db_cleanup);
4108 if(nickserv_conf.handle_expire_frequency)
4109 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4110 message_register_table(msgtab);