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;
1364 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsSupport(user)) {
1366 reply("NSMSG_HANDLEINFO_NO_NOTES");
1368 struct handle_note *prev, *note;
1370 WALK_NOTES(hi, prev, note) {
1371 char set_time[INTERVALLEN];
1372 intervalString(set_time, now - note->set, user->handle_info);
1373 if (note->expires) {
1374 char exp_time[INTERVALLEN];
1375 intervalString(exp_time, note->expires - now, user->handle_info);
1376 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1378 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1385 unsigned long flen = 1;
1386 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1388 for (i=0, flen=1; handle_flags[i]; i++)
1389 if (hi->flags & 1 << i)
1390 flags[flen++] = handle_flags[i];
1392 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1394 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1397 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1398 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1399 || (hi->opserv_level > 0)) {
1400 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1404 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1406 if (hi->last_quit_host[0])
1407 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1409 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1411 if (nickserv_conf.disable_nicks) {
1412 /* nicks disabled; don't show anything about registered nicks */
1413 } else if (hi->nicks) {
1414 struct nick_info *ni, *next_ni;
1415 for (ni = hi->nicks; ni; ni = next_ni) {
1416 herelen = strlen(ni->nick);
1417 if (pos + herelen + 1 > ArrayLength(buff)) {
1419 goto print_nicks_buff;
1423 memcpy(buff+pos, ni->nick, herelen);
1424 pos += herelen; buff[pos++] = ' ';
1428 reply("NSMSG_HANDLEINFO_NICKS", buff);
1433 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1436 if (hi->masks->used) {
1437 for (i=0; i < hi->masks->used; i++) {
1438 herelen = strlen(hi->masks->list[i]);
1439 if (pos + herelen + 1 > ArrayLength(buff)) {
1441 goto print_mask_buff;
1443 memcpy(buff+pos, hi->masks->list[i], herelen);
1444 pos += herelen; buff[pos++] = ' ';
1445 if (i+1 == hi->masks->used) {
1448 reply("NSMSG_HANDLEINFO_MASKS", buff);
1453 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1457 struct userData *channel, *next;
1460 for (channel = hi->channels; channel; channel = next) {
1461 next = channel->u_next;
1462 name = channel->channel->channel->name;
1463 herelen = strlen(name);
1464 if (pos + herelen + 7 > ArrayLength(buff)) {
1466 goto print_chans_buff;
1468 if (IsUserSuspended(channel))
1470 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1474 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1479 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1482 for (target = hi->users; target; target = next_un) {
1483 herelen = strlen(target->nick);
1484 if (pos + herelen + 1 > ArrayLength(buff)) {
1486 goto print_cnick_buff;
1488 next_un = target->next_authed;
1490 memcpy(buff+pos, target->nick, herelen);
1491 pos += herelen; buff[pos++] = ' ';
1495 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1503 static NICKSERV_FUNC(cmd_userinfo)
1505 struct userNode *target;
1507 NICKSERV_MIN_PARMS(2);
1508 if (!(target = GetUserH(argv[1]))) {
1509 reply("MSG_NICK_UNKNOWN", argv[1]);
1512 if (target->handle_info)
1513 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1515 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1519 static NICKSERV_FUNC(cmd_nickinfo)
1521 struct nick_info *ni;
1523 NICKSERV_MIN_PARMS(2);
1524 if (!(ni = get_nick_info(argv[1]))) {
1525 reply("MSG_NICK_UNKNOWN", argv[1]);
1528 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1532 static NICKSERV_FUNC(cmd_notes)
1534 struct handle_info *hi;
1535 struct handle_note *prev, *note;
1538 NICKSERV_MIN_PARMS(2);
1539 if (!(hi = get_victim_oper(user, argv[1])))
1542 WALK_NOTES(hi, prev, note) {
1543 char set_time[INTERVALLEN];
1544 intervalString(set_time, now - note->set, user->handle_info);
1545 if (note->expires) {
1546 char exp_time[INTERVALLEN];
1547 intervalString(exp_time, note->expires - now, user->handle_info);
1548 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1550 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1554 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1558 static NICKSERV_FUNC(cmd_rename_handle)
1560 struct handle_info *hi;
1561 char msgbuf[MAXLEN], *old_handle;
1564 NICKSERV_MIN_PARMS(3);
1565 if (!(hi = get_victim_oper(user, argv[1])))
1567 if (!is_valid_handle(argv[2])) {
1568 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1571 if (get_handle_info(argv[2])) {
1572 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1576 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1577 hi->handle = strdup(argv[2]);
1578 dict_insert(nickserv_handle_dict, hi->handle, hi);
1579 for (nn=0; nn<rf_list_used; nn++)
1580 rf_list[nn](hi, old_handle);
1581 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1582 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1583 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1588 static failpw_func_t *failpw_func_list;
1589 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1592 reg_failpw_func(failpw_func_t func)
1594 if (failpw_func_used == failpw_func_size) {
1595 if (failpw_func_size) {
1596 failpw_func_size <<= 1;
1597 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1599 failpw_func_size = 8;
1600 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1603 failpw_func_list[failpw_func_used++] = func;
1606 static NICKSERV_FUNC(cmd_auth)
1608 int pw_arg, used, maxlogins;
1609 struct handle_info *hi;
1611 struct userNode *other;
1613 if (user->handle_info) {
1614 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1617 if (IsStamped(user)) {
1618 /* Unauthenticated users might still have been stamped
1619 previously and could therefore have a hidden host;
1620 do not allow them to authenticate. */
1621 reply("NSMSG_STAMPED_AUTH");
1625 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1627 } else if (argc == 2) {
1628 if (nickserv_conf.disable_nicks) {
1629 if (!(hi = get_handle_info(user->nick))) {
1630 reply("NSMSG_HANDLE_NOT_FOUND");
1634 /* try to look up their handle from their nick */
1635 struct nick_info *ni;
1636 ni = get_nick_info(user->nick);
1638 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1645 reply("MSG_MISSING_PARAMS", argv[0]);
1646 svccmd_send_help(user, nickserv, cmd);
1650 reply("NSMSG_HANDLE_NOT_FOUND");
1653 /* Responses from here on look up the language used by the handle they asked about. */
1654 passwd = argv[pw_arg];
1655 if (!valid_user_for(user, hi)) {
1656 if (hi->email_addr && nickserv_conf.email_enabled)
1657 send_message_type(4, user, cmd->parent->bot,
1658 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1661 send_message_type(4, user, cmd->parent->bot,
1662 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1664 argv[pw_arg] = "BADMASK";
1667 if (!checkpass(passwd, hi->passwd)) {
1669 send_message_type(4, user, cmd->parent->bot,
1670 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1671 argv[pw_arg] = "BADPASS";
1672 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1673 if (nickserv_conf.autogag_enabled) {
1674 if (!user->auth_policer.params) {
1675 user->auth_policer.last_req = now;
1676 user->auth_policer.params = nickserv_conf.auth_policer_params;
1678 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1680 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1681 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1682 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1684 argv[pw_arg] = "GAGGED";
1689 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1690 send_message_type(4, user, cmd->parent->bot,
1691 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1692 argv[pw_arg] = "SUSPENDED";
1695 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1696 for (used = 0, other = hi->users; other; other = other->next_authed) {
1697 if (++used >= maxlogins) {
1698 send_message_type(4, user, cmd->parent->bot,
1699 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1701 argv[pw_arg] = "MAXLOGINS";
1706 set_user_handle_info(user, hi, 1);
1707 if (nickserv_conf.email_required && !hi->email_addr)
1708 reply("NSMSG_PLEASE_SET_EMAIL");
1709 if (!is_secure_password(hi->handle, passwd, NULL))
1710 reply("NSMSG_WEAK_PASSWORD");
1711 if (hi->passwd[0] != '$')
1712 cryptpass(passwd, hi->passwd);
1713 if (!hi->masks->used) {
1715 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1716 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1717 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1719 argv[pw_arg] = "****";
1720 reply("NSMSG_AUTH_SUCCESS");
1724 static allowauth_func_t *allowauth_func_list;
1725 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1728 reg_allowauth_func(allowauth_func_t func)
1730 if (allowauth_func_used == allowauth_func_size) {
1731 if (allowauth_func_size) {
1732 allowauth_func_size <<= 1;
1733 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1735 allowauth_func_size = 8;
1736 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1739 allowauth_func_list[allowauth_func_used++] = func;
1742 static NICKSERV_FUNC(cmd_allowauth)
1744 struct userNode *target;
1745 struct handle_info *hi;
1748 NICKSERV_MIN_PARMS(2);
1749 if (!(target = GetUserH(argv[1]))) {
1750 reply("MSG_NICK_UNKNOWN", argv[1]);
1753 if (target->handle_info) {
1754 reply("NSMSG_USER_PREV_AUTH", target->nick);
1757 if (IsStamped(target)) {
1758 /* Unauthenticated users might still have been stamped
1759 previously and could therefore have a hidden host;
1760 do not allow them to authenticate to an account. */
1761 reply("NSMSG_USER_PREV_STAMP", target->nick);
1766 else if (!(hi = get_handle_info(argv[2]))) {
1767 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1771 if (hi->opserv_level > user->handle_info->opserv_level) {
1772 reply("MSG_USER_OUTRANKED", hi->handle);
1775 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1776 || (hi->opserv_level > 0))
1777 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1778 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1781 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1782 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1783 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1784 if (nickserv_conf.email_enabled)
1785 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1787 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1788 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1790 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1792 for (n=0; n<allowauth_func_used; n++)
1793 allowauth_func_list[n](user, target, hi);
1797 static NICKSERV_FUNC(cmd_authcookie)
1799 struct handle_info *hi;
1801 NICKSERV_MIN_PARMS(2);
1802 if (user->handle_info) {
1803 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1806 if (IsStamped(user)) {
1807 /* Unauthenticated users might still have been stamped
1808 previously and could therefore have a hidden host;
1809 do not allow them to authenticate to an account. */
1810 reply("NSMSG_STAMPED_AUTHCOOKIE");
1813 if (!(hi = get_handle_info(argv[1]))) {
1814 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1817 if (!hi->email_addr) {
1818 reply("MSG_SET_EMAIL_ADDR");
1821 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1825 static NICKSERV_FUNC(cmd_delcookie)
1827 struct handle_info *hi;
1829 hi = user->handle_info;
1831 reply("NSMSG_NO_COOKIE");
1834 switch (hi->cookie->type) {
1837 reply("NSMSG_MUST_TIME_OUT");
1840 nickserv_eat_cookie(hi->cookie);
1841 reply("NSMSG_ATE_COOKIE");
1847 static NICKSERV_FUNC(cmd_resetpass)
1849 struct handle_info *hi;
1850 char crypted[MD5_CRYPT_LENGTH];
1852 NICKSERV_MIN_PARMS(3);
1853 if (user->handle_info) {
1854 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1857 if (IsStamped(user)) {
1858 /* Unauthenticated users might still have been stamped
1859 previously and could therefore have a hidden host;
1860 do not allow them to activate an account. */
1861 reply("NSMSG_STAMPED_RESETPASS");
1864 if (!(hi = get_handle_info(argv[1]))) {
1865 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1868 if (!hi->email_addr) {
1869 reply("MSG_SET_EMAIL_ADDR");
1872 cryptpass(argv[2], crypted);
1874 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1878 static NICKSERV_FUNC(cmd_cookie)
1880 struct handle_info *hi;
1883 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1886 NICKSERV_MIN_PARMS(3);
1887 if (!(hi = get_handle_info(argv[1]))) {
1888 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1894 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1895 reply("NSMSG_HANDLE_SUSPENDED");
1900 reply("NSMSG_NO_COOKIE");
1904 /* Check validity of operation before comparing cookie to
1905 * prohibit guessing by authed users. */
1906 if (user->handle_info
1907 && (hi->cookie->type != EMAIL_CHANGE)
1908 && (hi->cookie->type != PASSWORD_CHANGE)) {
1909 reply("NSMSG_CANNOT_COOKIE");
1913 if (strcmp(cookie, hi->cookie->cookie)) {
1914 reply("NSMSG_BAD_COOKIE");
1918 switch (hi->cookie->type) {
1920 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1921 set_user_handle_info(user, hi, 1);
1922 reply("NSMSG_HANDLE_ACTIVATED");
1924 case PASSWORD_CHANGE:
1925 set_user_handle_info(user, hi, 1);
1926 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1927 reply("NSMSG_PASSWORD_CHANGED");
1930 nickserv_set_email_addr(hi, hi->cookie->data);
1931 reply("NSMSG_EMAIL_CHANGED");
1934 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1935 set_user_handle_info(user, hi, 1);
1936 nickserv_addmask(user, hi, mask);
1937 reply("NSMSG_AUTH_SUCCESS");
1942 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1943 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1947 nickserv_eat_cookie(hi->cookie);
1952 static NICKSERV_FUNC(cmd_oregnick) {
1954 struct handle_info *target;
1955 struct nick_info *ni;
1957 NICKSERV_MIN_PARMS(3);
1958 if (!(target = modcmd_get_handle_info(user, argv[1])))
1961 if (!is_registerable_nick(nick)) {
1962 reply("NSMSG_BAD_NICK", nick);
1965 ni = dict_find(nickserv_nick_dict, nick, NULL);
1967 reply("NSMSG_NICK_EXISTS", nick);
1970 register_nick(nick, target);
1971 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1975 static NICKSERV_FUNC(cmd_regnick) {
1977 struct nick_info *ni;
1979 if (!is_registerable_nick(user->nick)) {
1980 reply("NSMSG_BAD_NICK", user->nick);
1983 /* count their nicks, see if it's too many */
1984 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1985 if (n >= nickserv_conf.nicks_per_handle) {
1986 reply("NSMSG_TOO_MANY_NICKS");
1989 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1991 reply("NSMSG_NICK_EXISTS", user->nick);
1994 register_nick(user->nick, user->handle_info);
1995 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1999 static NICKSERV_FUNC(cmd_pass)
2001 struct handle_info *hi;
2002 const char *old_pass, *new_pass;
2004 NICKSERV_MIN_PARMS(3);
2005 hi = user->handle_info;
2009 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2010 if (!checkpass(old_pass, hi->passwd)) {
2011 argv[1] = "BADPASS";
2012 reply("NSMSG_PASSWORD_INVALID");
2015 cryptpass(new_pass, hi->passwd);
2017 reply("NSMSG_PASS_SUCCESS");
2022 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2025 char *new_mask = canonicalize_hostmask(strdup(mask));
2026 for (i=0; i<hi->masks->used; i++) {
2027 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2028 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2033 string_list_append(hi->masks, new_mask);
2034 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2038 static NICKSERV_FUNC(cmd_addmask)
2041 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2042 int res = nickserv_addmask(user, user->handle_info, mask);
2046 if (!is_gline(argv[1])) {
2047 reply("NSMSG_MASK_INVALID", argv[1]);
2050 return nickserv_addmask(user, user->handle_info, argv[1]);
2054 static NICKSERV_FUNC(cmd_oaddmask)
2056 struct handle_info *hi;
2058 NICKSERV_MIN_PARMS(3);
2059 if (!(hi = get_victim_oper(user, argv[1])))
2061 return nickserv_addmask(user, hi, argv[2]);
2065 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2068 for (i=0; i<hi->masks->used; i++) {
2069 if (!strcmp(del_mask, hi->masks->list[i])) {
2070 char *old_mask = hi->masks->list[i];
2071 if (hi->masks->used == 1 && !force) {
2072 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2075 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2076 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2081 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2085 static NICKSERV_FUNC(cmd_delmask)
2087 NICKSERV_MIN_PARMS(2);
2088 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2091 static NICKSERV_FUNC(cmd_odelmask)
2093 struct handle_info *hi;
2094 NICKSERV_MIN_PARMS(3);
2095 if (!(hi = get_victim_oper(user, argv[1])))
2097 return nickserv_delmask(user, hi, argv[2], 1);
2101 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2102 unsigned int nn, add = 1, pos;
2103 unsigned long added, removed, flag;
2105 for (added=removed=nn=0; str[nn]; nn++) {
2107 case '+': add = 1; break;
2108 case '-': add = 0; break;
2110 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2111 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2114 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2115 /* cheesy avoidance of looking up the flag name.. */
2116 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2119 flag = 1 << (pos - 1);
2121 added |= flag, removed &= ~flag;
2123 removed |= flag, added &= ~flag;
2128 *premoved = removed;
2133 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2135 unsigned long before, after, added, removed;
2136 struct userNode *uNode;
2138 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2139 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2141 hi->flags = (hi->flags | added) & ~removed;
2142 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2144 /* Strip helping flag if they're only a support helper and not
2145 * currently in #support. */
2146 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2147 struct channelList *schannels;
2149 schannels = chanserv_support_channels();
2150 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2151 for (ii = 0; ii < schannels->used; ++ii)
2152 if (GetUserMode(schannels->list[ii], uNode))
2154 if (ii < schannels->used)
2158 HANDLE_CLEAR_FLAG(hi, HELPING);
2161 if (after && !before) {
2162 /* Add user to current helper list. */
2163 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2164 userList_append(&curr_helpers, uNode);
2165 } else if (!after && before) {
2166 /* Remove user from current helper list. */
2167 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2168 userList_remove(&curr_helpers, uNode);
2175 set_list(struct userNode *user, struct handle_info *hi, int override)
2179 char *set_display[] = {
2180 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2181 "EMAIL", "MAXLOGINS", "LANGUAGE"
2184 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2186 /* Do this so options are presented in a consistent order. */
2187 for (i = 0; i < ArrayLength(set_display); ++i)
2188 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2189 opt(user, hi, override, 0, NULL);
2192 static NICKSERV_FUNC(cmd_set)
2194 struct handle_info *hi;
2197 hi = user->handle_info;
2199 set_list(user, hi, 0);
2202 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2203 reply("NSMSG_INVALID_OPTION", argv[1]);
2206 return opt(user, hi, 0, argc-1, argv+1);
2209 static NICKSERV_FUNC(cmd_oset)
2211 struct handle_info *hi;
2212 struct svccmd *subcmd;
2214 char cmdname[MAXLEN];
2216 NICKSERV_MIN_PARMS(2);
2218 if (!(hi = get_victim_oper(user, argv[1])))
2222 set_list(user, hi, 0);
2226 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2227 reply("NSMSG_INVALID_OPTION", argv[2]);
2231 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2232 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2233 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2236 return opt(user, hi, 1, argc-2, argv+2);
2239 static OPTION_FUNC(opt_info)
2243 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2245 hi->infoline = NULL;
2247 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2251 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2252 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2256 static OPTION_FUNC(opt_width)
2259 hi->screen_width = strtoul(argv[1], NULL, 0);
2261 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2262 hi->screen_width = MIN_LINE_SIZE;
2263 else if (hi->screen_width > MAX_LINE_SIZE)
2264 hi->screen_width = MAX_LINE_SIZE;
2266 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2270 static OPTION_FUNC(opt_tablewidth)
2273 hi->table_width = strtoul(argv[1], NULL, 0);
2275 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2276 hi->table_width = MIN_LINE_SIZE;
2277 else if (hi->screen_width > MAX_LINE_SIZE)
2278 hi->table_width = MAX_LINE_SIZE;
2280 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2284 static OPTION_FUNC(opt_color)
2287 if (enabled_string(argv[1]))
2288 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2289 else if (disabled_string(argv[1]))
2290 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2292 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2297 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2301 static OPTION_FUNC(opt_privmsg)
2304 if (enabled_string(argv[1]))
2305 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2306 else if (disabled_string(argv[1]))
2307 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2309 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2314 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2318 static OPTION_FUNC(opt_style)
2323 if (!irccasecmp(argv[1], "Zoot"))
2324 hi->userlist_style = HI_STYLE_ZOOT;
2325 else if (!irccasecmp(argv[1], "def"))
2326 hi->userlist_style = HI_STYLE_DEF;
2329 switch (hi->userlist_style) {
2338 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2342 static OPTION_FUNC(opt_password)
2345 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2350 cryptpass(argv[1], hi->passwd);
2352 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2356 static OPTION_FUNC(opt_flags)
2359 unsigned int ii, flen;
2362 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2367 nickserv_apply_flags(user, hi, argv[1]);
2369 for (ii = flen = 0; handle_flags[ii]; ii++)
2370 if (hi->flags & (1 << ii))
2371 flags[flen++] = handle_flags[ii];
2374 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2376 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2380 static OPTION_FUNC(opt_email)
2384 if (!is_valid_email_addr(argv[1])) {
2385 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2388 if ((str = mail_prohibited_address(argv[1]))) {
2389 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2392 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2393 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2395 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2397 nickserv_set_email_addr(hi, argv[1]);
2399 nickserv_eat_cookie(hi->cookie);
2400 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2403 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2407 static OPTION_FUNC(opt_maxlogins)
2409 unsigned char maxlogins;
2411 maxlogins = strtoul(argv[1], NULL, 0);
2412 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2413 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2416 hi->maxlogins = maxlogins;
2418 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2419 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2423 static OPTION_FUNC(opt_language)
2425 struct language *lang;
2427 lang = language_find(argv[1]);
2428 if (irccasecmp(lang->name, argv[1]))
2429 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2430 hi->language = lang;
2432 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2436 static OPTION_FUNC(opt_karma)
2439 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2444 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2445 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2446 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2447 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2449 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2453 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2458 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2459 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2461 if ((user->handle_info->opserv_level < target->opserv_level)
2462 || ((user->handle_info->opserv_level == target->opserv_level)
2463 && (user->handle_info->opserv_level < 1000))) {
2464 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2467 if ((user->handle_info->opserv_level < new_level)
2468 || ((user->handle_info->opserv_level == new_level)
2469 && (user->handle_info->opserv_level < 1000))) {
2470 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2473 if (user->handle_info == target) {
2474 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2477 if (target->opserv_level == new_level)
2479 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2480 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2481 target->opserv_level = new_level;
2485 static OPTION_FUNC(opt_level)
2490 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2494 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2495 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2499 static OPTION_FUNC(opt_epithet)
2502 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2506 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2507 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2510 if ((epithet[0] == '*') && !epithet[1])
2513 hi->epithet = strdup(epithet);
2517 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2519 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2523 static OPTION_FUNC(opt_title)
2528 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2532 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2534 if (strchr(title, '.')) {
2535 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2538 if ((strlen(user->handle_info->handle) + strlen(title) +
2539 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2540 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2545 if (!strcmp(title, "*")) {
2546 hi->fakehost = NULL;
2548 hi->fakehost = malloc(strlen(title)+2);
2549 hi->fakehost[0] = '.';
2550 strcpy(hi->fakehost+1, title);
2553 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2554 title = hi->fakehost + 1;
2558 title = user_find_message(user, "MSG_NONE");
2559 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2563 static OPTION_FUNC(opt_fakehost)
2568 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2572 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2574 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2575 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2579 if (!strcmp(fake, "*"))
2580 hi->fakehost = NULL;
2582 hi->fakehost = strdup(fake);
2583 fake = hi->fakehost;
2586 fake = generate_fakehost(hi);
2588 fake = user_find_message(user, "MSG_NONE");
2589 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2593 static NICKSERV_FUNC(cmd_reclaim)
2595 struct handle_info *hi;
2596 struct nick_info *ni;
2597 struct userNode *victim;
2599 NICKSERV_MIN_PARMS(2);
2600 hi = user->handle_info;
2601 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2603 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2606 if (ni->owner != user->handle_info) {
2607 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2610 victim = GetUserH(ni->nick);
2612 reply("MSG_NICK_UNKNOWN", ni->nick);
2615 if (victim == user) {
2616 reply("NSMSG_NICK_USER_YOU");
2619 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2620 switch (nickserv_conf.reclaim_action) {
2621 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2622 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2623 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2624 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2629 static NICKSERV_FUNC(cmd_unregnick)
2632 struct handle_info *hi;
2633 struct nick_info *ni;
2635 hi = user->handle_info;
2636 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2637 ni = dict_find(nickserv_nick_dict, nick, NULL);
2639 reply("NSMSG_UNKNOWN_NICK", nick);
2642 if (hi != ni->owner) {
2643 reply("NSMSG_NOT_YOUR_NICK", nick);
2646 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2651 static NICKSERV_FUNC(cmd_ounregnick)
2653 struct nick_info *ni;
2655 NICKSERV_MIN_PARMS(2);
2656 if (!(ni = get_nick_info(argv[1]))) {
2657 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2660 if (!oper_outranks(user, ni->owner))
2662 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2667 static NICKSERV_FUNC(cmd_unregister)
2669 struct handle_info *hi;
2672 NICKSERV_MIN_PARMS(2);
2673 hi = user->handle_info;
2676 if (checkpass(passwd, hi->passwd)) {
2677 nickserv_unregister_handle(hi, user);
2680 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2681 reply("NSMSG_PASSWORD_INVALID");
2686 static NICKSERV_FUNC(cmd_ounregister)
2688 struct handle_info *hi;
2689 char reason[MAXLEN];
2691 NICKSERV_MIN_PARMS(2);
2692 if (!(hi = get_victim_oper(user, argv[1])))
2694 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2695 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2696 nickserv_unregister_handle(hi, user);
2700 static NICKSERV_FUNC(cmd_status)
2702 if (nickserv_conf.disable_nicks) {
2703 reply("NSMSG_GLOBAL_STATS_NONICK",
2704 dict_size(nickserv_handle_dict));
2706 if (user->handle_info) {
2708 struct nick_info *ni;
2709 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2710 reply("NSMSG_HANDLE_STATS", cnt);
2712 reply("NSMSG_HANDLE_NONE");
2714 reply("NSMSG_GLOBAL_STATS",
2715 dict_size(nickserv_handle_dict),
2716 dict_size(nickserv_nick_dict));
2721 static NICKSERV_FUNC(cmd_ghost)
2723 struct userNode *target;
2724 char reason[MAXLEN];
2726 NICKSERV_MIN_PARMS(2);
2727 if (!(target = GetUserH(argv[1]))) {
2728 reply("MSG_NICK_UNKNOWN", argv[1]);
2731 if (target == user) {
2732 reply("NSMSG_CANNOT_GHOST_SELF");
2735 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2736 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2739 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2740 DelUser(target, nickserv, 1, reason);
2741 reply("NSMSG_GHOST_KILLED", argv[1]);
2745 static NICKSERV_FUNC(cmd_vacation)
2747 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2748 reply("NSMSG_ON_VACATION");
2752 static NICKSERV_FUNC(cmd_addnote)
2754 struct handle_info *hi;
2755 unsigned long duration;
2758 struct handle_note *prev;
2759 struct handle_note *note;
2761 /* Parse parameters and figure out values for note's fields. */
2762 NICKSERV_MIN_PARMS(4);
2763 hi = get_victim_oper(user, argv[1]);
2766 duration = ParseInterval(argv[2]);
2767 if (duration > 2*365*86400) {
2768 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
2771 unsplit_string(argv + 3, argc - 3, text);
2772 WALK_NOTES(hi, prev, note) {}
2773 id = prev ? (prev->id + 1) : 1;
2775 /* Create the new note structure. */
2776 note = calloc(1, sizeof(*note) + strlen(text));
2778 note->expires = duration ? (now + duration) : 0;
2781 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
2782 strcpy(note->note, text);
2787 reply("NSMSG_NOTE_ADDED", id, hi->handle);
2791 static NICKSERV_FUNC(cmd_delnote)
2793 struct handle_info *hi;
2794 struct handle_note *prev;
2795 struct handle_note *note;
2798 NICKSERV_MIN_PARMS(3);
2799 hi = get_victim_oper(user, argv[1]);
2802 id = strtoul(argv[2], NULL, 10);
2803 WALK_NOTES(hi, prev, note) {
2804 if (id == note->id) {
2806 prev->next = note->next;
2808 hi->notes = note->next;
2810 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
2814 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
2819 nickserv_saxdb_write(struct saxdb_context *ctx) {
2821 struct handle_info *hi;
2824 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2826 #ifdef WITH_PROTOCOL_BAHAMUT
2829 saxdb_start_record(ctx, iter_key(it), 0);
2831 struct handle_cookie *cookie = hi->cookie;
2834 switch (cookie->type) {
2835 case ACTIVATION: type = KEY_ACTIVATION; break;
2836 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2837 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2838 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2839 default: type = NULL; break;
2842 saxdb_start_record(ctx, KEY_COOKIE, 0);
2843 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2844 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2846 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2847 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2848 saxdb_end_record(ctx);
2852 struct handle_note *prev, *note;
2853 saxdb_start_record(ctx, KEY_NOTES, 0);
2854 WALK_NOTES(hi, prev, note) {
2855 snprintf(flags, sizeof(flags), "%d", note->id);
2856 saxdb_start_record(ctx, flags, 0);
2858 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
2859 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
2860 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
2861 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
2862 saxdb_end_record(ctx);
2864 saxdb_end_record(ctx);
2867 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2869 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2871 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2875 for (ii=flen=0; handle_flags[ii]; ++ii)
2876 if (hi->flags & (1 << ii))
2877 flags[flen++] = handle_flags[ii];
2879 saxdb_write_string(ctx, KEY_FLAGS, flags);
2881 #ifdef WITH_PROTOCOL_BAHAMUT
2882 saxdb_write_int(ctx, KEY_ID, hi->id);
2885 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2886 if (hi->last_quit_host[0])
2887 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2888 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2890 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
2891 if (hi->masks->used)
2892 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2894 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2896 struct string_list *slist;
2897 struct nick_info *ni;
2899 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2900 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2901 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2905 if (hi->opserv_level)
2906 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2907 if (hi->language != lang_C)
2908 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2909 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2910 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2911 if (hi->screen_width)
2912 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2913 if (hi->table_width)
2914 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2915 flags[0] = hi->userlist_style;
2917 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2918 saxdb_end_record(ctx);
2923 static handle_merge_func_t *handle_merge_func_list;
2924 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2927 reg_handle_merge_func(handle_merge_func_t func)
2929 if (handle_merge_func_used == handle_merge_func_size) {
2930 if (handle_merge_func_size) {
2931 handle_merge_func_size <<= 1;
2932 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2934 handle_merge_func_size = 8;
2935 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2938 handle_merge_func_list[handle_merge_func_used++] = func;
2941 static NICKSERV_FUNC(cmd_merge)
2943 struct handle_info *hi_from, *hi_to;
2944 struct userNode *last_user;
2945 struct userData *cList, *cListNext;
2946 unsigned int ii, jj, n;
2947 char buffer[MAXLEN];
2949 NICKSERV_MIN_PARMS(3);
2951 if (!(hi_from = get_victim_oper(user, argv[1])))
2953 if (!(hi_to = get_victim_oper(user, argv[2])))
2955 if (hi_to == hi_from) {
2956 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2960 for (n=0; n<handle_merge_func_used; n++)
2961 handle_merge_func_list[n](user, hi_to, hi_from);
2963 /* Append "from" handle's nicks to "to" handle's nick list. */
2965 struct nick_info *last_ni;
2966 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2967 last_ni->next = hi_from->nicks;
2969 while (hi_from->nicks) {
2970 hi_from->nicks->owner = hi_to;
2971 hi_from->nicks = hi_from->nicks->next;
2974 /* Merge the hostmasks. */
2975 for (ii=0; ii<hi_from->masks->used; ii++) {
2976 char *mask = hi_from->masks->list[ii];
2977 for (jj=0; jj<hi_to->masks->used; jj++)
2978 if (match_ircglobs(hi_to->masks->list[jj], mask))
2980 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2981 string_list_append(hi_to->masks, strdup(mask));
2984 /* Merge the lists of authed users. */
2986 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2987 last_user->next_authed = hi_from->users;
2989 hi_to->users = hi_from->users;
2991 /* Repoint the old "from" handle's users. */
2992 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2993 last_user->handle_info = hi_to;
2995 hi_from->users = NULL;
2997 /* Merge channel userlists. */
2998 for (cList=hi_from->channels; cList; cList=cListNext) {
2999 struct userData *cList2;
3000 cListNext = cList->u_next;
3001 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3002 if (cList->channel == cList2->channel)
3004 if (cList2 && (cList2->access >= cList->access)) {
3005 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);
3006 /* keep cList2 in hi_to; remove cList from hi_from */
3007 del_channel_user(cList, 1);
3010 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);
3011 /* remove the lower-ranking cList2 from hi_to */
3012 del_channel_user(cList2, 1);
3014 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3016 /* cList needs to be moved from hi_from to hi_to */
3017 cList->handle = hi_to;
3018 /* Remove from linked list for hi_from */
3019 assert(!cList->u_prev);
3020 hi_from->channels = cList->u_next;
3022 cList->u_next->u_prev = cList->u_prev;
3023 /* Add to linked list for hi_to */
3024 cList->u_prev = NULL;
3025 cList->u_next = hi_to->channels;
3026 if (hi_to->channels)
3027 hi_to->channels->u_prev = cList;
3028 hi_to->channels = cList;
3032 /* Do they get an OpServ level promotion? */
3033 if (hi_from->opserv_level > hi_to->opserv_level)
3034 hi_to->opserv_level = hi_from->opserv_level;
3036 /* What about last seen time? */
3037 if (hi_from->lastseen > hi_to->lastseen)
3038 hi_to->lastseen = hi_from->lastseen;
3040 /* New karma is the sum of the two original karmas. */
3041 hi_to->karma += hi_from->karma;
3043 /* Does a fakehost carry over? (This intentionally doesn't set it
3044 * for users previously attached to hi_to. They'll just have to
3047 if (hi_from->fakehost && !hi_to->fakehost)
3048 hi_to->fakehost = strdup(hi_from->fakehost);
3050 /* Notify of success. */
3051 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3052 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3053 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3055 /* Unregister the "from" handle. */
3056 nickserv_unregister_handle(hi_from, NULL);
3061 struct nickserv_discrim {
3062 unsigned long flags_on, flags_off;
3063 time_t min_registered, max_registered;
3066 int min_level, max_level;
3067 int min_karma, max_karma;
3068 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3069 const char *nickmask;
3070 const char *hostmask;
3071 const char *fakehostmask;
3072 const char *handlemask;
3073 const char *emailmask;
3076 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3078 struct discrim_apply_info {
3079 struct nickserv_discrim *discrim;
3080 discrim_search_func func;
3081 struct userNode *source;
3082 unsigned int matched;
3085 static struct nickserv_discrim *
3086 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3089 struct nickserv_discrim *discrim;
3091 discrim = malloc(sizeof(*discrim));
3092 memset(discrim, 0, sizeof(*discrim));
3093 discrim->min_level = 0;
3094 discrim->max_level = INT_MAX;
3095 discrim->limit = 50;
3096 discrim->min_registered = 0;
3097 discrim->max_registered = INT_MAX;
3098 discrim->lastseen = LONG_MAX;
3099 discrim->min_karma = INT_MIN;
3100 discrim->max_karma = INT_MAX;
3102 for (i=0; i<argc; i++) {
3103 if (i == argc - 1) {
3104 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3107 if (!irccasecmp(argv[i], "limit")) {
3108 discrim->limit = strtoul(argv[++i], NULL, 0);
3109 } else if (!irccasecmp(argv[i], "flags")) {
3110 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3111 } else if (!irccasecmp(argv[i], "registered")) {
3112 const char *cmp = argv[++i];
3113 if (cmp[0] == '<') {
3114 if (cmp[1] == '=') {
3115 discrim->min_registered = now - ParseInterval(cmp+2);
3117 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3119 } else if (cmp[0] == '=') {
3120 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3121 } else if (cmp[0] == '>') {
3122 if (cmp[1] == '=') {
3123 discrim->max_registered = now - ParseInterval(cmp+2);
3125 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3128 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3130 } else if (!irccasecmp(argv[i], "seen")) {
3131 discrim->lastseen = now - ParseInterval(argv[++i]);
3132 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3133 discrim->nickmask = argv[++i];
3134 } else if (!irccasecmp(argv[i], "hostmask")) {
3136 if (!irccasecmp(argv[i], "exact")) {
3137 if (i == argc - 1) {
3138 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3141 discrim->hostmask_type = EXACT;
3142 } else if (!irccasecmp(argv[i], "subset")) {
3143 if (i == argc - 1) {
3144 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3147 discrim->hostmask_type = SUBSET;
3148 } else if (!irccasecmp(argv[i], "superset")) {
3149 if (i == argc - 1) {
3150 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3153 discrim->hostmask_type = SUPERSET;
3154 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3155 if (i == argc - 1) {
3156 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3159 discrim->hostmask_type = LASTQUIT;
3162 discrim->hostmask_type = SUPERSET;
3164 discrim->hostmask = argv[++i];
3165 } else if (!irccasecmp(argv[i], "fakehost")) {
3166 if (!irccasecmp(argv[++i], "*")) {
3167 discrim->fakehostmask = 0;
3169 discrim->fakehostmask = argv[i];
3171 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3172 if (!irccasecmp(argv[++i], "*")) {
3173 discrim->handlemask = 0;
3175 discrim->handlemask = argv[i];
3177 } else if (!irccasecmp(argv[i], "email")) {
3178 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3179 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3181 } else if (!irccasecmp(argv[++i], "*")) {
3182 discrim->emailmask = 0;
3184 discrim->emailmask = argv[i];
3186 } else if (!irccasecmp(argv[i], "access")) {
3187 const char *cmp = argv[++i];
3188 if (cmp[0] == '<') {
3189 if (discrim->min_level == 0) discrim->min_level = 1;
3190 if (cmp[1] == '=') {
3191 discrim->max_level = strtoul(cmp+2, NULL, 0);
3193 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3195 } else if (cmp[0] == '=') {
3196 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3197 } else if (cmp[0] == '>') {
3198 if (cmp[1] == '=') {
3199 discrim->min_level = strtoul(cmp+2, NULL, 0);
3201 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3204 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3206 } else if (!irccasecmp(argv[i], "karma")) {
3207 const char *cmp = argv[++i];
3208 if (cmp[0] == '<') {
3209 if (cmp[1] == '=') {
3210 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3212 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3214 } else if (cmp[0] == '=') {
3215 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3216 } else if (cmp[0] == '>') {
3217 if (cmp[1] == '=') {
3218 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3220 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3223 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3226 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3237 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3239 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3240 || (discrim->flags_off & hi->flags)
3241 || (discrim->min_registered > hi->registered)
3242 || (discrim->max_registered < hi->registered)
3243 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3244 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3245 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3246 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3247 || (discrim->min_level > hi->opserv_level)
3248 || (discrim->max_level < hi->opserv_level)
3249 || (discrim->min_karma > hi->karma)
3250 || (discrim->max_karma < hi->karma)
3254 if (discrim->hostmask) {
3256 for (i=0; i<hi->masks->used; i++) {
3257 const char *mask = hi->masks->list[i];
3258 if ((discrim->hostmask_type == SUBSET)
3259 && (match_ircglobs(discrim->hostmask, mask))) break;
3260 else if ((discrim->hostmask_type == EXACT)
3261 && !irccasecmp(discrim->hostmask, mask)) break;
3262 else if ((discrim->hostmask_type == SUPERSET)
3263 && (match_ircglobs(mask, discrim->hostmask))) break;
3264 else if ((discrim->hostmask_type == LASTQUIT)
3265 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3267 if (i==hi->masks->used) return 0;
3269 if (discrim->nickmask) {
3270 struct nick_info *nick = hi->nicks;
3272 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3275 if (!nick) return 0;
3281 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3283 dict_iterator_t it, next;
3284 unsigned int matched;
3286 for (it = dict_first(nickserv_handle_dict), matched = 0;
3287 it && (matched < discrim->limit);
3289 next = iter_next(it);
3290 if (nickserv_discrim_match(discrim, iter_data(it))) {
3291 dsf(source, iter_data(it));
3299 search_print_func(struct userNode *source, struct handle_info *match)
3301 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3305 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3310 search_unregister_func (struct userNode *source, struct handle_info *match)
3312 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3313 nickserv_unregister_handle(match, source);
3317 nickserv_sort_accounts_by_access(const void *a, const void *b)
3319 const struct handle_info *hi_a = *(const struct handle_info**)a;
3320 const struct handle_info *hi_b = *(const struct handle_info**)b;
3321 if (hi_a->opserv_level != hi_b->opserv_level)
3322 return hi_b->opserv_level - hi_a->opserv_level;
3323 return irccasecmp(hi_a->handle, hi_b->handle);
3327 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3329 struct handle_info_list hil;
3330 struct helpfile_table tbl;
3335 memset(&hil, 0, sizeof(hil));
3336 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3337 struct handle_info *hi = iter_data(it);
3338 if (hi->opserv_level)
3339 handle_info_list_append(&hil, hi);
3341 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3342 tbl.length = hil.used + 1;
3344 tbl.flags = TABLE_NO_FREE;
3345 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3346 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3349 for (ii = 0; ii < hil.used; ) {
3350 ary = malloc(tbl.width * sizeof(ary[0]));
3351 ary[0] = hil.list[ii]->handle;
3352 ary[1] = strtab(hil.list[ii]->opserv_level);
3353 tbl.contents[++ii] = ary;
3355 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3356 reply("MSG_MATCH_COUNT", hil.used);
3357 for (ii = 0; ii < hil.used; ii++)
3358 free(tbl.contents[ii]);
3363 static NICKSERV_FUNC(cmd_search)
3365 struct nickserv_discrim *discrim;
3366 discrim_search_func action;
3367 struct svccmd *subcmd;
3368 unsigned int matches;
3371 NICKSERV_MIN_PARMS(3);
3372 sprintf(buf, "search %s", argv[1]);
3373 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3374 if (!irccasecmp(argv[1], "print"))
3375 action = search_print_func;
3376 else if (!irccasecmp(argv[1], "count"))
3377 action = search_count_func;
3378 else if (!irccasecmp(argv[1], "unregister"))
3379 action = search_unregister_func;
3381 reply("NSMSG_INVALID_ACTION", argv[1]);
3385 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3388 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3392 if (action == search_print_func)
3393 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3394 else if (action == search_count_func)
3395 discrim->limit = INT_MAX;
3397 matches = nickserv_discrim_search(discrim, action, user);
3400 reply("MSG_MATCH_COUNT", matches);
3402 reply("MSG_NO_MATCHES");
3408 static MODCMD_FUNC(cmd_checkpass)
3410 struct handle_info *hi;
3412 NICKSERV_MIN_PARMS(3);
3413 if (!(hi = get_handle_info(argv[1]))) {
3414 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3417 if (checkpass(argv[2], hi->passwd))
3418 reply("CHECKPASS_YES");
3420 reply("CHECKPASS_NO");
3426 nickserv_db_read_handle(const char *handle, dict_t obj)
3429 struct string_list *masks, *slist;
3430 struct handle_info *hi;
3431 struct userNode *authed_users;
3432 struct userData *channels;
3433 unsigned long int id;
3437 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3438 id = str ? strtoul(str, NULL, 0) : 0;
3439 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3441 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3444 if ((hi = get_handle_info(handle))) {
3445 authed_users = hi->users;
3446 channels = hi->channels;
3448 hi->channels = NULL;
3449 dict_remove(nickserv_handle_dict, hi->handle);
3451 authed_users = NULL;
3454 hi = register_handle(handle, str, id);
3456 hi->users = authed_users;
3457 while (authed_users) {
3458 authed_users->handle_info = hi;
3459 authed_users = authed_users->next_authed;
3462 hi->channels = channels;
3463 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3464 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3465 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3466 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3467 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3468 hi->language = language_find(str ? str : "C");
3469 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3470 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3471 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3473 hi->infoline = strdup(str);
3474 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3475 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3476 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3477 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3478 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3479 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3480 /* We want to read the nicks even if disable_nicks is set. This is so
3481 * that we don't lose the nick data entirely. */
3482 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3484 for (ii=0; ii<slist->used; ii++)
3485 register_nick(slist->list[ii], hi);
3487 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3489 for (ii=0; str[ii]; ii++)
3490 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3492 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3493 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3494 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3495 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3496 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3497 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3498 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3500 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3502 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3503 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3505 nickserv_set_email_addr(hi, str);
3506 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3508 hi->epithet = strdup(str);
3509 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3511 hi->fakehost = strdup(str);
3512 /* Read the "cookie" sub-database (if it exists). */
3513 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3515 const char *data, *type, *expires, *cookie_str;
3516 struct handle_cookie *cookie;
3518 cookie = calloc(1, sizeof(*cookie));
3519 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3520 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3521 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3522 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3523 if (!type || !expires || !cookie_str) {
3524 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3527 if (!irccasecmp(type, KEY_ACTIVATION))
3528 cookie->type = ACTIVATION;
3529 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3530 cookie->type = PASSWORD_CHANGE;
3531 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3532 cookie->type = EMAIL_CHANGE;
3533 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3534 cookie->type = ALLOWAUTH;
3536 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3539 cookie->expires = strtoul(expires, NULL, 0);
3540 if (cookie->expires < now)
3543 cookie->data = strdup(data);
3544 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3548 nickserv_bake_cookie(cookie);
3550 nickserv_free_cookie(cookie);
3552 /* Read the "notes" sub-database (if it exists). */
3553 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3556 struct handle_note *last_note;
3557 struct handle_note *note;
3560 for (it = dict_first(subdb); it; it = iter_next(it)) {
3561 const char *expires;
3569 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3571 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", id, hi->handle);
3574 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3575 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3576 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3577 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3578 if (!setter || !text || !set) {
3579 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", id, hi->handle);
3582 note = calloc(1, sizeof(*note) + strlen(text));
3584 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3585 note->set = strtoul(set, NULL, 10);
3586 note->id = strtoul(id, NULL, 10);
3587 safestrncpy(note->setter, setter, sizeof(note->setter));
3588 strcpy(note->note, text);
3590 last_note->next = note;
3599 nickserv_saxdb_read(dict_t db) {
3601 struct record_data *rd;
3603 for (it=dict_first(db); it; it=iter_next(it)) {
3605 nickserv_db_read_handle(iter_key(it), rd->d.object);
3610 static NICKSERV_FUNC(cmd_mergedb)
3612 struct timeval start, stop;
3615 NICKSERV_MIN_PARMS(2);
3616 gettimeofday(&start, NULL);
3617 if (!(db = parse_database(argv[1]))) {
3618 reply("NSMSG_DB_UNREADABLE", argv[1]);
3621 nickserv_saxdb_read(db);
3623 gettimeofday(&stop, NULL);
3624 stop.tv_sec -= start.tv_sec;
3625 stop.tv_usec -= start.tv_usec;
3626 if (stop.tv_usec < 0) {
3628 stop.tv_usec += 1000000;
3630 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3635 expire_handles(UNUSED_ARG(void *data))
3637 dict_iterator_t it, next;
3639 struct handle_info *hi;
3641 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3642 next = iter_next(it);
3644 if ((hi->opserv_level > 0)
3646 || HANDLE_FLAGGED(hi, FROZEN)
3647 || HANDLE_FLAGGED(hi, NODELETE)) {
3650 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3651 if ((now - hi->lastseen) > expiry) {
3652 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3653 nickserv_unregister_handle(hi, NULL);
3657 if (nickserv_conf.handle_expire_frequency)
3658 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3662 nickserv_load_dict(const char *fname)
3666 if (!(file = fopen(fname, "r"))) {
3667 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3670 while (!feof(file)) {
3671 fgets(line, sizeof(line), file);
3674 if (line[strlen(line)-1] == '\n')
3675 line[strlen(line)-1] = 0;
3676 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3679 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3682 static enum reclaim_action
3683 reclaim_action_from_string(const char *str) {
3685 return RECLAIM_NONE;
3686 else if (!irccasecmp(str, "warn"))
3687 return RECLAIM_WARN;
3688 else if (!irccasecmp(str, "svsnick"))
3689 return RECLAIM_SVSNICK;
3690 else if (!irccasecmp(str, "kill"))
3691 return RECLAIM_KILL;
3693 return RECLAIM_NONE;
3697 nickserv_conf_read(void)
3699 dict_t conf_node, child;
3703 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3704 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3707 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3709 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3710 if (nickserv_conf.valid_handle_regex_set)
3711 regfree(&nickserv_conf.valid_handle_regex);
3713 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3714 nickserv_conf.valid_handle_regex_set = !err;
3715 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3717 nickserv_conf.valid_handle_regex_set = 0;
3719 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3720 if (nickserv_conf.valid_nick_regex_set)
3721 regfree(&nickserv_conf.valid_nick_regex);
3723 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3724 nickserv_conf.valid_nick_regex_set = !err;
3725 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3727 nickserv_conf.valid_nick_regex_set = 0;
3729 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3731 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3732 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3733 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3734 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3735 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3736 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3737 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3738 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3739 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3740 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3741 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3742 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3743 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3744 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3745 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3746 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3747 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3748 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3749 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3750 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3751 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3752 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3753 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3754 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3755 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3757 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3758 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3759 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3761 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3762 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3763 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3765 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3766 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3767 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3768 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3769 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3770 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3771 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3772 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3773 if (!nickserv_conf.disable_nicks) {
3774 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3775 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3776 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3777 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3778 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3779 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3780 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3781 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3783 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3784 for (it=dict_first(child); it; it=iter_next(it)) {
3785 const char *key = iter_key(it), *value;
3789 if (!strncasecmp(key, "uc_", 3))
3790 flag = toupper(key[3]);
3791 else if (!strncasecmp(key, "lc_", 3))
3792 flag = tolower(key[3]);
3796 if ((pos = handle_inverse_flags[flag])) {
3797 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3798 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3801 if (nickserv_conf.weak_password_dict)
3802 dict_delete(nickserv_conf.weak_password_dict);
3803 nickserv_conf.weak_password_dict = dict_new();
3804 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3805 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3806 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3807 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3809 nickserv_load_dict(str);
3810 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3811 if (nickserv && str)
3812 NickChange(nickserv, str, 0);
3813 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3814 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3815 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3816 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3817 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3818 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3819 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3820 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3821 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3822 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3823 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3824 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3825 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3826 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3827 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3828 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3829 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3830 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3831 str = conf_get_data("server/network", RECDB_QSTRING);
3832 nickserv_conf.network_name = str ? str : "some IRC network";
3833 if (!nickserv_conf.auth_policer_params) {
3834 nickserv_conf.auth_policer_params = policer_params_new();
3835 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3836 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3838 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3839 for (it=dict_first(child); it; it=iter_next(it))
3840 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3844 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3846 char newnick[NICKLEN+1];
3855 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3857 case RECLAIM_SVSNICK:
3859 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3860 } while (GetUserH(newnick));
3861 irc_svsnick(nickserv, user, newnick);
3864 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3865 DelUser(user, nickserv, 1, msg);
3871 nickserv_reclaim_p(void *data) {
3872 struct userNode *user = data;
3873 struct nick_info *ni = get_nick_info(user->nick);
3875 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3879 check_user_nick(struct userNode *user) {
3880 struct nick_info *ni;
3881 user->modes &= ~FLAGS_REGNICK;
3882 if (!(ni = get_nick_info(user->nick)))
3884 if (user->handle_info == ni->owner) {
3885 user->modes |= FLAGS_REGNICK;
3889 if (nickserv_conf.warn_nick_owned)
3890 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3891 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3893 if (nickserv_conf.auto_reclaim_delay)
3894 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3896 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3901 handle_new_user(struct userNode *user)
3903 return check_user_nick(user);
3907 handle_account(struct userNode *user, const char *stamp)
3909 struct handle_info *hi;
3911 #ifdef WITH_PROTOCOL_P10
3912 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3914 hi = dict_find(nickserv_id_dict, stamp, NULL);
3918 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3921 set_user_handle_info(user, hi, 0);
3923 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3928 handle_nick_change(struct userNode *user, const char *old_nick)
3930 struct handle_info *hi;
3932 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3933 dict_remove(nickserv_allow_auth_dict, old_nick);
3934 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3936 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3937 check_user_nick(user);
3941 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3943 dict_remove(nickserv_allow_auth_dict, user->nick);
3944 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3945 set_user_handle_info(user, NULL, 0);
3948 static struct modcmd *
3949 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3951 if (min_level > 0) {
3953 sprintf(buf, "%u", min_level);
3954 if (must_be_qualified) {
3955 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3957 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3959 } else if (min_level == 0) {
3960 if (must_be_qualified) {
3961 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3963 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3966 if (must_be_qualified) {
3967 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3969 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3975 nickserv_db_cleanup(void)
3977 unreg_del_user_func(nickserv_remove_user);
3978 userList_clean(&curr_helpers);
3979 policer_params_delete(nickserv_conf.auth_policer_params);
3980 dict_delete(nickserv_handle_dict);
3981 dict_delete(nickserv_nick_dict);
3982 dict_delete(nickserv_opt_dict);
3983 dict_delete(nickserv_allow_auth_dict);
3984 dict_delete(nickserv_email_dict);
3985 dict_delete(nickserv_id_dict);
3986 dict_delete(nickserv_conf.weak_password_dict);
3987 free(auth_func_list);
3988 free(unreg_func_list);
3990 free(allowauth_func_list);
3991 free(handle_merge_func_list);
3992 free(failpw_func_list);
3993 if (nickserv_conf.valid_handle_regex_set)
3994 regfree(&nickserv_conf.valid_handle_regex);
3995 if (nickserv_conf.valid_nick_regex_set)
3996 regfree(&nickserv_conf.valid_nick_regex);
4000 init_nickserv(const char *nick)
4003 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4004 reg_new_user_func(handle_new_user);
4005 reg_nick_change_func(handle_nick_change);
4006 reg_del_user_func(nickserv_remove_user);
4007 reg_account_func(handle_account);
4009 /* set up handle_inverse_flags */
4010 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4011 for (i=0; handle_flags[i]; i++) {
4012 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4013 flag_access_levels[i] = 0;
4016 conf_register_reload(nickserv_conf_read);
4017 nickserv_opt_dict = dict_new();
4018 nickserv_email_dict = dict_new();
4019 dict_set_free_keys(nickserv_email_dict, free);
4020 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4022 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4023 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4024 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4025 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4026 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4027 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4028 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4029 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4030 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4031 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4032 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4033 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4034 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4035 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4036 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4037 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4038 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4039 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4040 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4041 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4042 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4043 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4044 if (!nickserv_conf.disable_nicks) {
4045 /* nick management commands */
4046 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4047 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4048 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4049 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4050 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4051 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4053 if (nickserv_conf.email_enabled) {
4054 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4055 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4056 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4057 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4058 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4060 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4061 /* miscellaneous commands */
4062 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4063 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4064 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4065 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4066 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4068 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4069 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4070 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4071 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4072 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4073 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4074 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4075 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4076 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4077 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4078 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4079 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4080 if (nickserv_conf.titlehost_suffix) {
4081 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4082 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4084 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4085 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4086 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4087 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4089 nickserv_handle_dict = dict_new();
4090 dict_set_free_keys(nickserv_handle_dict, free);
4091 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4093 nickserv_id_dict = dict_new();
4094 dict_set_free_keys(nickserv_id_dict, free);
4096 nickserv_nick_dict = dict_new();
4097 dict_set_free_data(nickserv_nick_dict, free);
4099 nickserv_allow_auth_dict = dict_new();
4101 userList_init(&curr_helpers);
4104 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4105 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4106 nickserv_service = service_register(nickserv);
4108 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4109 reg_exit_func(nickserv_db_cleanup);
4110 if(nickserv_conf.handle_expire_frequency)
4111 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4112 message_register_table(msgtab);