]> jfr.im git - irc/evilnet/x3.git/blame - src/nickserv.c
Sync logging now configurable
[irc/evilnet/x3.git] / src / nickserv.c
CommitLineData
d76ed9a9 1/* nickserv.c - Nick/authentication service
2 * Copyright 2000-2004 srvx Development Team
3 *
4 * This file is part of srvx.
5 *
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.
10 *
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.
15 *
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.
19 */
20
21#include "chanserv.h"
22#include "conf.h"
23#include "global.h"
24#include "modcmd.h"
25#include "opserv.h" /* for gag_create(), opserv_bad_channel() */
26#include "saxdb.h"
27#include "sendmail.h"
28#include "timeq.h"
29
30#ifdef HAVE_REGEX_H
31#include <regex.h>
32#endif
33
34#define NICKSERV_CONF_NAME "services/nickserv"
35
36#define KEY_DISABLE_NICKS "disable_nicks"
37#define KEY_DEFAULT_HOSTMASK "default_hostmask"
38#define KEY_NICKS_PER_HANDLE "nicks_per_handle"
39#define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
40#define KEY_PASSWORD_MIN_LENGTH "password_min_length"
41#define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
42#define KEY_PASSWORD_MIN_UPPER "password_min_upper"
43#define KEY_PASSWORD_MIN_LOWER "password_min_lower"
44#define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
45#define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
46#define KEY_VALID_NICK_REGEX "valid_nick_regex"
47#define KEY_DB_BACKUP_FREQ "db_backup_freq"
48#define KEY_MODOPER_LEVEL "modoper_level"
49#define KEY_SET_EPITHET_LEVEL "set_epithet_level"
50#define KEY_SET_TITLE_LEVEL "set_title_level"
51#define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
52#define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
53#define KEY_FLAG_LEVELS "flag_levels"
54#define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
55#define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
56#define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
57#define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
58#define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
59#define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
60#define KEY_DICT_FILE "dict_file"
61#define KEY_NICK "nick"
62#define KEY_LANGUAGE "language"
63#define KEY_AUTOGAG_ENABLED "autogag_enabled"
64#define KEY_AUTOGAG_DURATION "autogag_duration"
65#define KEY_AUTH_POLICER "auth_policer"
66#define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
67#define KEY_EMAIL_ENABLED "email_enabled"
68#define KEY_EMAIL_REQUIRED "email_required"
8dc1d9ae 69#define KEY_SYNC_LOG "sync_log"
d76ed9a9 70#define KEY_COOKIE_TIMEOUT "cookie_timeout"
71#define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
72#define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
73
74#define KEY_ID "id"
75#define KEY_PASSWD "passwd"
76#define KEY_NICKS "nicks"
77#define KEY_MASKS "masks"
78#define KEY_OPSERV_LEVEL "opserv_level"
79#define KEY_FLAGS "flags"
80#define KEY_REGISTER_ON "register"
81#define KEY_LAST_SEEN "lastseen"
82#define KEY_INFO "info"
83#define KEY_USERLIST_STYLE "user_style"
84#define KEY_SCREEN_WIDTH "screen_width"
85#define KEY_LAST_AUTHED_HOST "last_authed_host"
86#define KEY_LAST_QUIT_HOST "last_quit_host"
87#define KEY_EMAIL_ADDR "email_addr"
88#define KEY_COOKIE "cookie"
89#define KEY_COOKIE_DATA "data"
90#define KEY_COOKIE_TYPE "type"
91#define KEY_COOKIE_EXPIRES "expires"
92#define KEY_ACTIVATION "activation"
93#define KEY_PASSWORD_CHANGE "password change"
94#define KEY_EMAIL_CHANGE "email change"
95#define KEY_ALLOWAUTH "allowauth"
96#define KEY_EPITHET "epithet"
97#define KEY_TABLE_WIDTH "table_width"
98#define KEY_ANNOUNCEMENTS "announcements"
99#define KEY_MAXLOGINS "maxlogins"
100#define KEY_FAKEHOST "fakehost"
101
102#define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
103
104#define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
105#define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
106typedef OPTION_FUNC(option_func_t);
107
108DEFINE_LIST(handle_info_list, struct handle_info*);
109
110#define NICKSERV_MIN_PARMS(N) do { \
111 if (argc < N) { \
112 reply("MSG_MISSING_PARAMS", argv[0]); \
113 svccmd_send_help(user, nickserv, cmd); \
114 return 0; \
115 } } while (0)
116
117struct userNode *nickserv;
118struct userList curr_helpers;
119const char *handle_flags = HANDLE_FLAGS;
120
121static struct module *nickserv_module;
122static struct service *nickserv_service;
123static struct log_type *NS_LOG;
124static dict_t nickserv_handle_dict; /* contains struct handle_info* */
125static dict_t nickserv_id_dict; /* contains struct handle_info* */
126static dict_t nickserv_nick_dict; /* contains struct nick_info* */
127static dict_t nickserv_opt_dict; /* contains option_func_t* */
128static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
129static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
130static char handle_inverse_flags[256];
131static unsigned int flag_access_levels[32];
132static const struct message_entry msgtab[] = {
133 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
d0cb2fb6 134 { "NSMSG_HANDLE_TOLONG", "The account name %s is too long. Account names must be %lu charactors or less."},
d76ed9a9 135 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
136 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
137 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
138 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
139 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
140 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
141 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
142 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
143 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
144 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
145 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
146 { "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." },
147 { "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." },
148 { "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." },
149 { "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." },
150 { "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." },
151 { "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." },
152 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
153 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
154 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
155 { "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." },
156 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
157 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
158 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
159 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
160 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
161 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
162 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
163 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
164 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
165 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
166 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
167 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
168 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
169 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
170 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
171 { "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)" },
172 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
173 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
174 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
175 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
176 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
177 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
178 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
179 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
180 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
181 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
182 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
183 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
a32da4c7 184 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
d76ed9a9 185 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
186 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
187 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
188 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
189 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
190 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
191 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
192 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
193 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
194 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
195 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
196 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
197 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
198 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
199 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
200 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
201 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
202 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
203 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
204 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
205 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
206 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
207 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
208 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
209 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
210 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
211 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
212 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
213 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
214 { "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)." },
215 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
216 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
217 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
218 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
219 { "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." },
220 { "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." },
221 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
222 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
223 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
224 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
225 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
226 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
227 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
228 { "NSMSG_PASS_SUCCESS", "Password changed." },
229 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
230 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
231 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
232 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
233 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
234 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
235 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
236 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
237 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
238 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
239 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
240 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
241 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
242 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
243 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
244 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
245 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
246 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
247 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
248 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
249 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
250 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
251 { "NSMSG_NO_ACCESS", "Access denied." },
252 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
253 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
254 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
255 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
256 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
257 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
258 { "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." },
259 { "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." },
260 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
261 { "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." },
262 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
263 { "NSMSG_SEARCH_MATCH", "Match: %s" },
264 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
265 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
266 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
267 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
268 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
269 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
270 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
271 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
272 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
273 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
274 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
275 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
276 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
277 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
278 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
279 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
280 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
281 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
282 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
283 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
284 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
285 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
286 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
287 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
288 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
289 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
290 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
291 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
292 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
293 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
1c0d5243 294 { "NSEMAIL_ACTIVATION_BODY",
295 "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"
296 "%2$s\n"
297 "To verify your email address and complete the account registration, log on to %1$s and type the following command:\n"
298 "/msg %3$s@%4$s COOKIE %5$s %2$s\n"
299 "This 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"
300 "/msg %3$s@%4$s AUTH %5$s your-password\n"
301 "(Please remember to fill in 'your-password' with the actual password you gave to us when you registered.)\n"
302 "OR configure Login-On-Connect (see http://www.afternet.org/login-on-connect for instructions) to connect pre-logged in every time.\n"
303 "\n"
304 "If you did NOT request this account, you do not need to do anything.\n"
305 "Please contact the %1$s staff if you have questions, and be sure to check our website." },
306 { "NSEMAIL_ACTIVATION_BODY_WEB",
307 "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"
308 "%2$s\n"
309 "To verify your email address and complete the account registration, visit the following URL:\n"
310 "http://www.afternet.org/play/index.php?option=com_registration&task=activate&username=%5$s&cookie=%2$s\n"
311 "\n"
312 "If you did NOT request this account, you do not need to do anything.\n"
313 "Please contact the %1$s staff if you have questions, and be sure to check our website." },
d76ed9a9 314 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
1c0d5243 315 { "NSEMAIL_PASSWORD_CHANGE_BODY",
316 "This email has been sent to verify that you wish to change the password on your account %5$s. Your cookie is %2$s.\n"
317 "To complete the password change, log on to %1$s and type the following command:\n"
318 "/msg %3$s@%4$s COOKIE %5$s %2$s\n"
319 "If you did NOT request your password to be changed, you do not need to do anything.\n"
320 "Please contact the %1$s staff if you have questions." },
321 { "NSEMAIL_PASSWORD_CHANGE_BODY_WEB",
322 "This email has been sent to verify that you wish to change the password on your account %5$s. Your cookie is %2$s.\n"
323 "To complete the password change, click the following URL:\n"
324 "http://www.afternet.org/play/index.php?option=com_registration&task=passcookie&username=%5$s&cookie=%2$s\n"
325 "If you did NOT request your password to be changed, you do not need to do anything.\n"
326 "Please contact the %1$s staff if you have questions." },
d76ed9a9 327 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
328 { "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." },
329 { "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." },
330 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
331 { "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." },
332 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
333 { "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." },
334 { "CHECKPASS_YES", "Yes." },
335 { "CHECKPASS_NO", "No." },
336 { NULL, NULL }
337};
338
339enum reclaim_action {
340 RECLAIM_NONE,
341 RECLAIM_WARN,
342 RECLAIM_SVSNICK,
343 RECLAIM_KILL
344};
345static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
346static void nickserv_reclaim_p(void *data);
347
348static struct {
349 unsigned int disable_nicks : 1;
350 unsigned int valid_handle_regex_set : 1;
351 unsigned int valid_nick_regex_set : 1;
352 unsigned int autogag_enabled : 1;
353 unsigned int email_enabled : 1;
354 unsigned int email_required : 1;
355 unsigned int default_hostmask : 1;
356 unsigned int warn_nick_owned : 1;
357 unsigned int warn_clone_auth : 1;
8dc1d9ae 358 unsigned int sync_log : 1;
d76ed9a9 359 unsigned long nicks_per_handle;
360 unsigned long password_min_length;
361 unsigned long password_min_digits;
362 unsigned long password_min_upper;
363 unsigned long password_min_lower;
364 unsigned long db_backup_frequency;
365 unsigned long handle_expire_frequency;
366 unsigned long autogag_duration;
367 unsigned long email_visible_level;
368 unsigned long cookie_timeout;
369 unsigned long handle_expire_delay;
370 unsigned long nochan_handle_expire_delay;
371 unsigned long modoper_level;
372 unsigned long set_epithet_level;
373 unsigned long set_title_level;
374 unsigned long set_fakehost_level;
375 unsigned long handles_per_email;
376 unsigned long email_search_level;
377 const char *network_name;
378 const char *titlehost_suffix;
379 regex_t valid_handle_regex;
380 regex_t valid_nick_regex;
381 dict_t weak_password_dict;
382 struct policer_params *auth_policer_params;
383 enum reclaim_action reclaim_action;
384 enum reclaim_action auto_reclaim_action;
385 unsigned long auto_reclaim_delay;
386 unsigned char default_maxlogins;
387 unsigned char hard_maxlogins;
388} nickserv_conf;
389
390/* We have 2^32 unique account IDs to use. */
391unsigned long int highest_id = 0;
392
393static char *
394canonicalize_hostmask(char *mask)
395{
396 char *out = mask, *temp;
397 if ((temp = strchr(mask, '!'))) {
398 temp++;
399 while (*temp) *out++ = *temp++;
400 *out++ = 0;
401 }
402 return mask;
403}
404
405static struct handle_info *
406register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
407{
408 struct handle_info *hi;
409
410#ifdef WITH_PROTOCOL_BAHAMUT
411 char id_base64[IDLEN + 1];
412 do
413 {
414 /* Assign a unique account ID to the account; note that 0 is
415 an invalid account ID. 1 is therefore the first account ID. */
416 if (!id) {
417 id = 1 + highest_id++;
418 } else {
419 /* Note: highest_id is and must always be the highest ID. */
420 if(id > highest_id) {
421 highest_id = id;
422 }
423 }
424 inttobase64(id_base64, id, IDLEN);
425
426 /* Make sure an account with the same ID doesn't exist. If a
427 duplicate is found, log some details and assign a new one.
428 This should be impossible, but it never hurts to expect it. */
429 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
430 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
431 id = 0;
432 }
433 } while(!id);
434#endif
435
436 hi = calloc(1, sizeof(*hi));
437 hi->userlist_style = HI_DEFAULT_STYLE;
438 hi->announcements = '?';
439 hi->handle = strdup(handle);
440 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
441 hi->infoline = NULL;
442 dict_insert(nickserv_handle_dict, hi->handle, hi);
443
444#ifdef WITH_PROTOCOL_BAHAMUT
445 hi->id = id;
446 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
447#endif
448
449 return hi;
450}
451
452static void
453register_nick(const char *nick, struct handle_info *owner)
454{
455 struct nick_info *ni;
456 ni = malloc(sizeof(struct nick_info));
457 safestrncpy(ni->nick, nick, sizeof(ni->nick));
458 ni->owner = owner;
459 ni->next = owner->nicks;
460 owner->nicks = ni;
461 dict_insert(nickserv_nick_dict, ni->nick, ni);
462}
463
d76ed9a9 464static void
465delete_nick(struct nick_info *ni)
466{
467 struct nick_info *last, *next;
468 struct userNode *user;
469 /* Check to see if we should mark a user as unregistered. */
470 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
471 user->modes &= ~FLAGS_REGNICK;
472 irc_regnick(user);
473 }
474 /* Remove ni from the nick_info linked list. */
475 if (ni == ni->owner->nicks) {
476 ni->owner->nicks = ni->next;
477 } else {
478 last = ni->owner->nicks;
479 next = last->next;
480 while (next != ni) {
481 last = next;
482 next = last->next;
483 }
484 last->next = next->next;
485 }
486 dict_remove(nickserv_nick_dict, ni->nick);
487}
488
489static unreg_func_t *unreg_func_list;
490static unsigned int unreg_func_size = 0, unreg_func_used = 0;
491
492void
493reg_unreg_func(unreg_func_t func)
494{
495 if (unreg_func_used == unreg_func_size) {
496 if (unreg_func_size) {
497 unreg_func_size <<= 1;
498 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
499 } else {
500 unreg_func_size = 8;
501 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
502 }
503 }
504 unreg_func_list[unreg_func_used++] = func;
505}
506
507static void
508nickserv_free_cookie(void *data)
509{
510 struct handle_cookie *cookie = data;
511 if (cookie->hi) cookie->hi->cookie = NULL;
512 if (cookie->data) free(cookie->data);
513 free(cookie);
514}
515
516static void
517free_handle_info(void *vhi)
518{
519 struct handle_info *hi = vhi;
520
521#ifdef WITH_PROTOCOL_BAHAMUT
522 char id[IDLEN + 1];
523
524 inttobase64(id, hi->id, IDLEN);
525 dict_remove(nickserv_id_dict, id);
526#endif
527
528 free_string_list(hi->masks);
529 assert(!hi->users);
530
531 while (hi->nicks)
532 delete_nick(hi->nicks);
533 free(hi->infoline);
534 free(hi->epithet);
535 free(hi->fakehost);
536 if (hi->cookie) {
537 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
538 nickserv_free_cookie(hi->cookie);
539 }
540 if (hi->email_addr) {
541 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
542 handle_info_list_remove(hil, hi);
543 if (!hil->used)
544 dict_remove(nickserv_email_dict, hi->email_addr);
545 }
546 free(hi);
547}
548
549static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
550
551static void
552nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
553{
554 unsigned int n;
555
556 for (n=0; n<unreg_func_used; n++)
557 unreg_func_list[n](notify, hi);
558 while (hi->users)
559 set_user_handle_info(hi->users, NULL, 0);
560 if (notify) {
561 if (nickserv_conf.disable_nicks)
562 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
563 else
564 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
565 }
8dc1d9ae 566
567 if (nickserv_conf.sync_log)
568 SyncLog("UNREGISTER %s", hi->handle);
569
d76ed9a9 570 dict_remove(nickserv_handle_dict, hi->handle);
571}
572
573struct handle_info*
574get_handle_info(const char *handle)
575{
576 return dict_find(nickserv_handle_dict, handle, 0);
577}
578
579struct nick_info*
580get_nick_info(const char *nick)
581{
582 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
583}
584
585struct modeNode *
586find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
587{
588 unsigned int nn;
589 struct modeNode *mn;
590
591 for (nn=0; nn<channel->members.used; ++nn) {
592 mn = channel->members.list[nn];
593 if ((mn->user != except) && (mn->user->handle_info == handle))
594 return mn;
595 }
596 return NULL;
597}
598
599int
600oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
601 if (!user->handle_info) {
602 if (!quiet)
603 send_message(user, bot, "MSG_AUTHENTICATE");
604 return 0;
605 }
606
607 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
608 if (!quiet)
609 send_message(user, bot, "NSMSG_NO_ACCESS");
610 return 0;
611 }
612
613 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
614 if (!quiet)
615 send_message(user, bot, "MSG_OPER_SUSPENDED");
616 return 0;
617 }
618
619 if (user->handle_info->opserv_level < min_level) {
620 if (!quiet)
621 send_message(user, bot, "NSMSG_NO_ACCESS");
622 return 0;
623 }
624
625 return 1;
626}
627
628static int
629is_valid_handle(const char *handle)
630{
631 struct userNode *user;
632 /* cant register a juped nick/service nick as handle, to prevent confusion */
633 user = GetUserH(handle);
634 if (user && IsLocal(user))
635 return 0;
636 /* check against maximum length */
637 if (strlen(handle) > NICKSERV_HANDLE_LEN)
638 return 0;
639 /* for consistency, only allow account names that could be nicks */
640 if (!is_valid_nick(handle))
641 return 0;
642 /* disallow account names that look like bad words */
643 if (opserv_bad_channel(handle))
644 return 0;
645 /* test either regex or containing all valid chars */
646 if (nickserv_conf.valid_handle_regex_set) {
647 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
648 if (err) {
649 char buff[256];
650 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
651 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
652 }
653 return !err;
654 } else {
655 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
656 }
657}
658
659static int
660is_registerable_nick(const char *nick)
661{
662 /* make sure it could be used as an account name */
663 if (!is_valid_handle(nick))
664 return 0;
665 /* check length */
666 if (strlen(nick) > NICKLEN)
667 return 0;
668 /* test either regex or as valid handle */
669 if (nickserv_conf.valid_nick_regex_set) {
670 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
671 if (err) {
672 char buff[256];
673 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
674 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
675 }
676 return !err;
677 }
678 return 1;
679}
680
681static int
682is_valid_email_addr(const char *email)
683{
684 return strchr(email, '@') != NULL;
685}
686
687static const char *
688visible_email_addr(struct userNode *user, struct handle_info *hi)
689{
690 if (hi->email_addr) {
691 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
692 return hi->email_addr;
693 } else {
694 return "Set.";
695 }
696 } else {
697 return "Not set.";
698 }
699}
700
701struct handle_info *
702smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
703{
704 struct handle_info *hi;
705 struct userNode *target;
706
707 switch (*name) {
708 case '*':
709 if (!(hi = get_handle_info(++name))) {
710 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
711 return 0;
712 }
713 return hi;
714 default:
715 if (!(target = GetUserH(name))) {
716 send_message(user, service, "MSG_NICK_UNKNOWN", name);
717 return 0;
718 }
719 if (IsLocal(target)) {
720 if (IsService(target))
721 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
722 else
723 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
724 return 0;
725 }
726 if (!(hi = target->handle_info)) {
727 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
728 return 0;
729 }
730 return hi;
731 }
732}
733
734int
735oper_outranks(struct userNode *user, struct handle_info *hi) {
736 if (user->handle_info->opserv_level > hi->opserv_level)
737 return 1;
738 if (user->handle_info->opserv_level == hi->opserv_level) {
739 if ((user->handle_info->opserv_level == 1000)
740 || (user->handle_info == hi)
741 || ((user->handle_info->opserv_level == 0)
742 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
743 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
744 return 1;
745 }
746 }
747 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
748 return 0;
749}
750
751static struct handle_info *
752get_victim_oper(struct userNode *user, const char *target)
753{
754 struct handle_info *hi;
755 if (!(hi = smart_get_handle_info(nickserv, user, target)))
756 return 0;
757 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
758 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
759 return 0;
760 }
761 return oper_outranks(user, hi) ? hi : NULL;
762}
763
764static int
765valid_user_for(struct userNode *user, struct handle_info *hi)
766{
767 unsigned int ii;
768
769 /* If no hostmasks on the account, allow it. */
770 if (!hi->masks->used)
771 return 1;
772 /* If any hostmask matches, allow it. */
773 for (ii=0; ii<hi->masks->used; ii++)
774 if (user_matches_glob(user, hi->masks->list[ii], 0))
775 return 1;
776 /* If they are allowauthed to this account, allow it (removing the aa). */
777 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
778 dict_remove(nickserv_allow_auth_dict, user->nick);
779 return 2;
780 }
781 /* The user is not allowed to use this account. */
782 return 0;
783}
784
785static int
786is_secure_password(const char *handle, const char *pass, struct userNode *user)
787{
788 unsigned int i, len;
789 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
790 len = strlen(pass);
791 if (len < nickserv_conf.password_min_length) {
792 if (user)
793 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
794 return 0;
795 }
796 if (!irccasecmp(pass, handle)) {
797 if (user)
798 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
799 return 0;
800 }
801 dict_find(nickserv_conf.weak_password_dict, pass, &i);
802 if (i) {
803 if (user)
804 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
805 return 0;
806 }
807 for (i=0; i<len; i++) {
808 if (isdigit(pass[i]))
809 cnt_digits++;
810 if (isupper(pass[i]))
811 cnt_upper++;
812 if (islower(pass[i]))
813 cnt_lower++;
814 }
815 if ((cnt_lower < nickserv_conf.password_min_lower)
816 || (cnt_upper < nickserv_conf.password_min_upper)
817 || (cnt_digits < nickserv_conf.password_min_digits)) {
818 if (user)
819 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
820 return 0;
821 }
822 return 1;
823}
824
825static auth_func_t *auth_func_list;
826static unsigned int auth_func_size = 0, auth_func_used = 0;
827
828void
829reg_auth_func(auth_func_t func)
830{
831 if (auth_func_used == auth_func_size) {
832 if (auth_func_size) {
833 auth_func_size <<= 1;
834 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
835 } else {
836 auth_func_size = 8;
837 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
838 }
839 }
840 auth_func_list[auth_func_used++] = func;
841}
842
843static handle_rename_func_t *rf_list;
844static unsigned int rf_list_size, rf_list_used;
845
846void
847reg_handle_rename_func(handle_rename_func_t func)
848{
849 if (rf_list_used == rf_list_size) {
850 if (rf_list_size) {
851 rf_list_size <<= 1;
852 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
853 } else {
854 rf_list_size = 8;
855 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
856 }
857 }
858 rf_list[rf_list_used++] = func;
859}
860
861static char *
862generate_fakehost(struct handle_info *handle)
863{
864 extern const char *hidden_host_suffix;
865 static char buffer[HOSTLEN+1];
866
867 if (!handle->fakehost) {
868 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
869 return buffer;
870 } else if (handle->fakehost[0] == '.') {
871 /* A leading dot indicates the stored value is actually a title. */
872 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
873 return buffer;
874 }
875 return handle->fakehost;
876}
877
878static void
879apply_fakehost(struct handle_info *handle)
880{
881 struct userNode *target;
882 char *fake;
883
884 if (!handle->users)
885 return;
886 fake = generate_fakehost(handle);
887 for (target = handle->users; target; target = target->next_authed)
888 assign_fakehost(target, fake, 1);
889}
890
891static void
892set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
893{
894 unsigned int n;
895 struct handle_info *old_info;
896
897 /* This can happen if somebody uses COOKIE while authed, or if
898 * they re-auth to their current handle (which is silly, but users
899 * are like that). */
900 if (user->handle_info == hi)
901 return;
902
903 if (user->handle_info) {
904 struct userNode *other;
905
906 if (IsHelper(user))
907 userList_remove(&curr_helpers, user);
908
909 /* remove from next_authed linked list */
910 if (user->handle_info->users == user) {
911 user->handle_info->users = user->next_authed;
912 } else {
913 for (other = user->handle_info->users;
914 other->next_authed != user;
915 other = other->next_authed) ;
916 other->next_authed = user->next_authed;
917 }
918 /* if nobody left on old handle, and they're not an oper, remove !god */
919 if (!user->handle_info->users && !user->handle_info->opserv_level)
920 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
921 /* record them as being last seen at this time */
922 user->handle_info->lastseen = now;
923 /* and record their hostmask */
924 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
925 }
926 old_info = user->handle_info;
927 user->handle_info = hi;
928 if (hi && !hi->users && !hi->opserv_level)
929 HANDLE_CLEAR_FLAG(hi, HELPING);
930 for (n=0; n<auth_func_used; n++)
931 auth_func_list[n](user, old_info);
932 if (hi) {
933 struct nick_info *ni;
934
935 HANDLE_CLEAR_FLAG(hi, FROZEN);
936 if (nickserv_conf.warn_clone_auth) {
937 struct userNode *other;
938 for (other = hi->users; other; other = other->next_authed)
939 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
940 }
941 user->next_authed = hi->users;
942 hi->users = user;
943 hi->lastseen = now;
944 if (IsHelper(user))
945 userList_append(&curr_helpers, user);
946
947 if (hi->fakehost || old_info)
948 apply_fakehost(hi);
949
950 if (stamp) {
951#ifdef WITH_PROTOCOL_BAHAMUT
952 /* Stamp users with their account ID. */
953 char id[IDLEN + 1];
954 inttobase64(id, hi->id, IDLEN);
955#elif WITH_PROTOCOL_P10
956 /* Stamp users with their account name. */
957 char *id = hi->handle;
958#else
959 const char *id = "???";
960#endif
961 if (!nickserv_conf.disable_nicks) {
962 struct nick_info *ni;
963 for (ni = hi->nicks; ni; ni = ni->next) {
964 if (!irccasecmp(user->nick, ni->nick)) {
965 user->modes |= FLAGS_REGNICK;
966 break;
967 }
968 }
969 }
b21e2cfe 970 StampUser(user, id, hi->registered);
d76ed9a9 971 }
972
973 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
974 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
975 } else {
976 /* We cannot clear the user's account ID, unfortunately. */
977 user->next_authed = NULL;
978 }
979}
980
981static struct handle_info*
982nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
983{
984 struct handle_info *hi;
985 struct nick_info *ni;
986 char crypted[MD5_CRYPT_LENGTH];
987
988 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
989 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
990 return 0;
991 }
992
d0cb2fb6 993 if(strlen(handle) > 15)
994 {
995 send_message(user, nickserv, "NSMSG_HANDLE_TOLONG", handle, 15);
996 return 0;
997 }
998
d76ed9a9 999 if (!is_secure_password(handle, passwd, user))
1000 return 0;
1001
1002 cryptpass(passwd, crypted);
1003 hi = register_handle(handle, crypted, 0);
1004 hi->masks = alloc_string_list(1);
1005 hi->users = NULL;
1006 hi->language = lang_C;
1007 hi->registered = now;
1008 hi->lastseen = now;
1009 hi->flags = HI_DEFAULT_FLAGS;
1010 if (settee && !no_auth)
1011 set_user_handle_info(settee, hi, 1);
1012
1013 if (user != settee)
1014 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1015 else if (nickserv_conf.disable_nicks)
1016 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1017 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1018 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1019 else {
1020 register_nick(user->nick, hi);
1021 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1022 }
1023 if (settee && (user != settee))
1024 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1025 return hi;
1026}
1027
1028static void
1029nickserv_bake_cookie(struct handle_cookie *cookie)
1030{
1031 cookie->hi->cookie = cookie;
1032 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1033}
1034
1035static void
1c0d5243 1036nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data, int weblink)
d76ed9a9 1037{
1038 struct handle_cookie *cookie;
1039 char subject[128], body[4096], *misc;
1040 const char *netname, *fmt;
1041 int first_time = 0;
1042
1043 if (hi->cookie) {
1044 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1045 return;
1046 }
1047
1048 cookie = calloc(1, sizeof(*cookie));
1049 cookie->hi = hi;
1050 cookie->type = type;
1051 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1052 cookie->expires = now + nickserv_conf.cookie_timeout;
1053 inttobase64(cookie->cookie, rand(), 5);
1054 inttobase64(cookie->cookie+5, rand(), 5);
1055
1056 netname = nickserv_conf.network_name;
1057 subject[0] = 0;
1058
1059 switch (cookie->type) {
1060 case ACTIVATION:
1061 hi->passwd[0] = 0; /* invalidate password */
1062 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1063 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1064 snprintf(subject, sizeof(subject), fmt, netname);
1c0d5243 1065
1066 if(weblink)
1067 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY_WEB");
1068 else
1069 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1070
d76ed9a9 1071 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1072 first_time = 1;
1073 break;
1074 case PASSWORD_CHANGE:
1075 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1076 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1077 snprintf(subject, sizeof(subject), fmt, netname);
1c0d5243 1078 if(weblink)
1079 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY_WEB");
1080 else
1081 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
d76ed9a9 1082 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1083 break;
1084 case EMAIL_CHANGE:
1085 misc = hi->email_addr;
1086 hi->email_addr = cookie->data;
1087 if (misc) {
1088 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1089 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1090 snprintf(subject, sizeof(subject), fmt, netname);
1091 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1092 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1093 sendmail(nickserv, hi, subject, body, 1);
1094 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1095 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1096 } else {
1097 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1098 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1099 snprintf(subject, sizeof(subject), fmt, netname);
1100 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1101 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1102 sendmail(nickserv, hi, subject, body, 1);
1103 subject[0] = 0;
1104 }
1105 hi->email_addr = misc;
1106 break;
1107 case ALLOWAUTH:
1108 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1109 snprintf(subject, sizeof(subject), fmt, netname);
1110 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1111 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1112 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1113 break;
1114 default:
1115 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1116 break;
1117 }
1118 if (subject[0])
1119 sendmail(nickserv, hi, subject, body, first_time);
1120 nickserv_bake_cookie(cookie);
1121}
1122
1123static void
1124nickserv_eat_cookie(struct handle_cookie *cookie)
1125{
1126 cookie->hi->cookie = NULL;
1127 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1128 nickserv_free_cookie(cookie);
1129}
1130
1131static void
1132nickserv_free_email_addr(void *data)
1133{
1134 handle_info_list_clean(data);
1135 free(data);
1136}
1137
1138static void
1139nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1140{
1141 struct handle_info_list *hil;
1142 /* Remove from old handle_info_list ... */
1143 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1144 handle_info_list_remove(hil, hi);
1145 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1146 hi->email_addr = NULL;
1147 }
1148 /* Add to the new list.. */
1149 if (new_email_addr) {
1150 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1151 hil = calloc(1, sizeof(*hil));
1152 hil->tag = strdup(new_email_addr);
1153 handle_info_list_init(hil);
1154 dict_insert(nickserv_email_dict, hil->tag, hil);
1155 }
1156 handle_info_list_append(hil, hi);
1157 hi->email_addr = hil->tag;
1158 }
1159}
1160
1161static NICKSERV_FUNC(cmd_register)
1162{
1163 struct handle_info *hi;
1164 const char *email_addr, *password;
eb5d6b73 1165 char syncpass[MD5_CRYPT_LENGTH];
1c0d5243 1166 int no_auth, weblink;
d76ed9a9 1167
1168 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1169 /* Require the first handle registered to belong to someone +o. */
1170 reply("NSMSG_REQUIRE_OPER");
1171 return 0;
1172 }
1173
1174 if (user->handle_info) {
1175 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1176 return 0;
1177 }
1178
1179 if (IsRegistering(user)) {
1180 reply("NSMSG_ALREADY_REGISTERING");
1181 return 0;
1182 }
1183
1184 if (IsStamped(user)) {
1185 /* Unauthenticated users might still have been stamped
1186 previously and could therefore have a hidden host;
1187 do not allow them to register a new account. */
1188 reply("NSMSG_STAMPED_REGISTER");
1189 return 0;
1190 }
1191
1192 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1193
1194 if (!is_valid_handle(argv[1])) {
1195 reply("NSMSG_BAD_HANDLE", argv[1]);
1196 return 0;
1197 }
1198
1c0d5243 1199
d76ed9a9 1200 if ((argc >= 4) && nickserv_conf.email_enabled) {
1201 struct handle_info_list *hil;
1202 const char *str;
1203
1204 /* Remember email address. */
1205 email_addr = argv[3];
1206
1207 /* Check that the email address looks valid.. */
1208 if (!is_valid_email_addr(email_addr)) {
1209 reply("NSMSG_BAD_EMAIL_ADDR");
1210 return 0;
1211 }
1212
1213 /* .. and that we are allowed to send to it. */
1214 if ((str = sendmail_prohibited_address(email_addr))) {
1215 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1216 return 0;
1217 }
1218
1219 /* If we do email verify, make sure we don't spam the address. */
1220 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1221 unsigned int nn;
1222 for (nn=0; nn<hil->used; nn++) {
1223 if (hil->list[nn]->cookie) {
1224 reply("NSMSG_EMAIL_UNACTIVATED");
1225 return 0;
1226 }
1227 }
1228 if (hil->used >= nickserv_conf.handles_per_email) {
1229 reply("NSMSG_EMAIL_OVERUSED");
1230 return 0;
1231 }
1232 }
1233
1234 no_auth = 1;
1235 } else {
1236 email_addr = 0;
1237 no_auth = 0;
1238 }
1239
1240 password = argv[2];
1241 argv[2] = "****";
1c0d5243 1242 /* Webregister hack - send URL instead of IRC cookie
1243 * commands in email
1244 */
1245 if((argc >= 5) && !strcmp(argv[4],"WEBLINK"))
1246 weblink = 1;
1247 else
1248 weblink = 0;
d76ed9a9 1249 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1250 return 0;
1251 /* Add any masks they should get. */
1252 if (nickserv_conf.default_hostmask) {
1253 string_list_append(hi->masks, strdup("*@*"));
1254 } else {
1255 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1256 if (user->ip.s_addr && user->hostname[strspn(user->hostname, "0123456789.")])
1257 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1258 }
1259
1260 /* If they're the first to register, give them level 1000. */
1261 if (dict_size(nickserv_handle_dict) == 1) {
1262 hi->opserv_level = 1000;
1263 reply("NSMSG_ROOT_HANDLE", argv[1]);
1264 }
1265
1266 /* Set their email address. */
1267 if (email_addr)
1268 nickserv_set_email_addr(hi, email_addr);
1269
1270 /* If they need to do email verification, tell them. */
1271 if (no_auth)
1c0d5243 1272 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd, weblink);
d76ed9a9 1273
1274 /* Set registering flag.. */
1275 user->modes |= FLAGS_REGISTERING;
1276
8dc1d9ae 1277 if (nickserv_conf.sync_log) {
1278 cryptpass(password, syncpass);
1279 SyncLog("REGISTER %s %s %s", hi->handle, syncpass, email_addr);
1280 }
eb5d6b73 1281
d76ed9a9 1282 return 1;
1283}
1284
1285static NICKSERV_FUNC(cmd_oregister)
1286{
1287 char *mask;
1288 struct userNode *settee;
1289 struct handle_info *hi;
1290
1291 NICKSERV_MIN_PARMS(4);
1292
1293 if (!is_valid_handle(argv[1])) {
1294 reply("NSMSG_BAD_HANDLE", argv[1]);
1295 return 0;
1296 }
1297
1298 if (strchr(argv[3], '@')) {
1299 mask = canonicalize_hostmask(strdup(argv[3]));
1300 if (argc > 4) {
1301 settee = GetUserH(argv[4]);
1302 if (!settee) {
1303 reply("MSG_NICK_UNKNOWN", argv[4]);
1304 free(mask);
1305 return 0;
1306 }
1307 } else {
1308 settee = NULL;
1309 }
1310 } else if ((settee = GetUserH(argv[3]))) {
1311 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1312 } else {
1313 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1314 return 0;
1315 }
1316 if (settee && settee->handle_info) {
1317 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1318 free(mask);
1319 return 0;
1320 }
1321 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1322 free(mask);
1323 return 0;
1324 }
1325 string_list_append(hi->masks, mask);
1326 return 1;
1327}
1328
1329static NICKSERV_FUNC(cmd_handleinfo)
1330{
1331 char buff[400];
1332 unsigned int i, pos=0, herelen;
1333 struct userNode *target, *next_un;
1334 struct handle_info *hi;
1335 const char *nsmsg_none;
1336
1337 if (argc < 2) {
1338 if (!(hi = user->handle_info)) {
1339 reply("NSMSG_MUST_AUTH");
1340 return 0;
1341 }
1342 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1343 return 0;
1344 }
1345
1346 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1347 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1348#ifdef WITH_PROTOCOL_BAHAMUT
1349 reply("NSMSG_HANDLEINFO_ID", hi->id);
1350#endif
1351 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1352
1353 if (!hi->users) {
1354 intervalString(buff, now - hi->lastseen, user->handle_info);
1355 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1356 } else {
1357 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1358 }
1359
1360 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1361 if (HANDLE_FLAGGED(hi, FROZEN))
1362 reply("NSMSG_HANDLEINFO_VACATION");
1363
1364 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1365 struct do_not_register *dnr;
1366 if ((dnr = chanserv_is_dnr(NULL, hi)))
1367 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1368 if (!oper_outranks(user, hi))
1369 return 1;
1370 } else if (hi != user->handle_info)
1371 return 1;
1372
1373 if (nickserv_conf.email_enabled)
1374 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1375
1376 if (hi->cookie) {
1377 const char *type;
1378 switch (hi->cookie->type) {
1379 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1380 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1381 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1382 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1383 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1384 }
1385 reply(type);
1386 }
1387
1388 if (hi->flags) {
1389 unsigned long flen = 1;
1390 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1391 flags[0] = '+';
1392 for (i=0, flen=1; handle_flags[i]; i++)
1393 if (hi->flags & 1 << i)
1394 flags[flen++] = handle_flags[i];
1395 flags[flen] = 0;
1396 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1397 } else {
1398 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1399 }
1400
1401 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1402 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1403 || (hi->opserv_level > 0)) {
1404 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1405 }
1406
1407 if (hi->fakehost)
1408 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1409
1410 if (hi->last_quit_host[0])
1411 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1412 else
1413 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1414
1415 if (nickserv_conf.disable_nicks) {
1416 /* nicks disabled; don't show anything about registered nicks */
1417 } else if (hi->nicks) {
1418 struct nick_info *ni, *next_ni;
1419 for (ni = hi->nicks; ni; ni = next_ni) {
1420 herelen = strlen(ni->nick);
1421 if (pos + herelen + 1 > ArrayLength(buff)) {
1422 next_ni = ni;
1423 goto print_nicks_buff;
1424 } else {
1425 next_ni = ni->next;
1426 }
1427 memcpy(buff+pos, ni->nick, herelen);
1428 pos += herelen; buff[pos++] = ' ';
1429 if (!next_ni) {
1430 print_nicks_buff:
1431 buff[pos-1] = 0;
1432 reply("NSMSG_HANDLEINFO_NICKS", buff);
1433 pos = 0;
1434 }
1435 }
1436 } else {
1437 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1438 }
1439
1440 if (hi->masks->used) {
1441 for (i=0; i < hi->masks->used; i++) {
1442 herelen = strlen(hi->masks->list[i]);
1443 if (pos + herelen + 1 > ArrayLength(buff)) {
1444 i--;
1445 goto print_mask_buff;
1446 }
1447 memcpy(buff+pos, hi->masks->list[i], herelen);
1448 pos += herelen; buff[pos++] = ' ';
1449 if (i+1 == hi->masks->used) {
1450 print_mask_buff:
1451 buff[pos-1] = 0;
1452 reply("NSMSG_HANDLEINFO_MASKS", buff);
1453 pos = 0;
1454 }
1455 }
1456 } else {
1457 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1458 }
1459
1460 if (hi->channels) {
1461 struct userData *channel, *next;
1462 char *name;
1463
1464 for (channel = hi->channels; channel; channel = next) {
1465 next = channel->u_next;
1466 name = channel->channel->channel->name;
1467 herelen = strlen(name);
1468 if (pos + herelen + 7 > ArrayLength(buff)) {
1469 next = channel;
1470 goto print_chans_buff;
1471 }
1472 if (IsUserSuspended(channel))
1473 buff[pos++] = '-';
1474 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1475 if (next == NULL) {
1476 print_chans_buff:
1477 buff[pos-1] = 0;
1478 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1479 pos = 0;
1480 }
1481 }
1482 } else {
1483 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1484 }
1485
1486 for (target = hi->users; target; target = next_un) {
1487 herelen = strlen(target->nick);
1488 if (pos + herelen + 1 > ArrayLength(buff)) {
1489 next_un = target;
1490 goto print_cnick_buff;
1491 } else {
1492 next_un = target->next_authed;
1493 }
1494 memcpy(buff+pos, target->nick, herelen);
1495 pos += herelen; buff[pos++] = ' ';
1496 if (!next_un) {
1497 print_cnick_buff:
1498 buff[pos-1] = 0;
1499 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1500 pos = 0;
1501 }
1502 }
1503
1504 return 1;
1505}
1506
1507static NICKSERV_FUNC(cmd_userinfo)
1508{
1509 struct userNode *target;
1510
1511 NICKSERV_MIN_PARMS(2);
1512 if (!(target = GetUserH(argv[1]))) {
1513 reply("MSG_NICK_UNKNOWN", argv[1]);
1514 return 0;
1515 }
1516 if (target->handle_info)
1517 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1518 else
1519 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1520 return 1;
1521}
1522
1523static NICKSERV_FUNC(cmd_nickinfo)
1524{
1525 struct nick_info *ni;
1526
1527 NICKSERV_MIN_PARMS(2);
1528 if (!(ni = get_nick_info(argv[1]))) {
1529 reply("MSG_NICK_UNKNOWN", argv[1]);
1530 return 0;
1531 }
1532 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1533 return 1;
1534}
1535
1536static NICKSERV_FUNC(cmd_rename_handle)
1537{
1538 struct handle_info *hi;
1539 char msgbuf[MAXLEN], *old_handle;
1540 unsigned int nn;
1541
1542 NICKSERV_MIN_PARMS(3);
1543 if (!(hi = get_victim_oper(user, argv[1])))
1544 return 0;
1545 if (!is_valid_handle(argv[2])) {
1546 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1547 return 0;
1548 }
1549 if (get_handle_info(argv[2])) {
1550 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1551 return 0;
1552 }
d0cb2fb6 1553 if(strlen(argv[2]) > 15)
1554 {
1555 reply("NMSG_HANDLE_TOLONG", argv[2], 15);
1556 return 0;
1557 }
d76ed9a9 1558
1559 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1560 hi->handle = strdup(argv[2]);
1561 dict_insert(nickserv_handle_dict, hi->handle, hi);
1562 for (nn=0; nn<rf_list_used; nn++)
1563 rf_list[nn](hi, old_handle);
1564 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1565 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1566 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1567 free(old_handle);
1568 return 1;
1569}
1570
1571static failpw_func_t *failpw_func_list;
1572static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1573
1574void
1575reg_failpw_func(failpw_func_t func)
1576{
1577 if (failpw_func_used == failpw_func_size) {
1578 if (failpw_func_size) {
1579 failpw_func_size <<= 1;
1580 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1581 } else {
1582 failpw_func_size = 8;
1583 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1584 }
1585 }
1586 failpw_func_list[failpw_func_used++] = func;
1587}
1588
d9cd0e9d 1589/*
b21e2cfe 1590 * Return hi if the handle/pass pair matches, NULL if it doesnt.
d9cd0e9d 1591 *
1592 * called by nefariouses enhanced AC login-on-connect code
1593 *
1594 */
b21e2cfe 1595struct handle_info *loc_auth(char *handle, char *password)
d9cd0e9d 1596{
1597 int pw_arg, used, maxlogins;
b21e2cfe 1598 unsigned int ii;
1599 int wildmask = 0;
d9cd0e9d 1600 struct handle_info *hi;
d9cd0e9d 1601 struct userNode *other;
d9cd0e9d 1602
1603 hi = dict_find(nickserv_handle_dict, handle, NULL);
1604 pw_arg = 2;
1605 if (!hi) {
b21e2cfe 1606 return NULL;
1607 }
1608
1609 /* We don't know the users hostname, or anything because they
1610 * havn't registered yet. So we can only allow LOC if your
1611 * account has *@* as a hostmask.
1612 */
1613 for (ii=0; ii<hi->masks->used; ii++)
1614 {
1615 if (!strcmp(hi->masks->list[ii], "*@*"))
1616 {
1617 wildmask++;
1618 break;
1619 }
d9cd0e9d 1620 }
b21e2cfe 1621 if(wildmask < 1)
1622 return NULL;
1623
d9cd0e9d 1624 /* Responses from here on look up the language used by the handle they asked about. */
1625 if (!checkpass(password, hi->passwd)) {
b21e2cfe 1626 return NULL;
d9cd0e9d 1627 }
1628 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
b21e2cfe 1629 return NULL;
d9cd0e9d 1630 }
1631 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
d9cd0e9d 1632 for (used = 0, other = hi->users; other; other = other->next_authed) {
1633 if (++used >= maxlogins) {
b21e2cfe 1634 return NULL;
d9cd0e9d 1635 }
1636 }
b21e2cfe 1637 return hi;
d9cd0e9d 1638}
1639
d76ed9a9 1640static NICKSERV_FUNC(cmd_auth)
1641{
1642 int pw_arg, used, maxlogins;
1643 struct handle_info *hi;
1644 const char *passwd;
1645 struct userNode *other;
1646
1647 if (user->handle_info) {
1648 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1649 return 0;
1650 }
1651 if (IsStamped(user)) {
1652 /* Unauthenticated users might still have been stamped
1653 previously and could therefore have a hidden host;
1654 do not allow them to authenticate. */
1655 reply("NSMSG_STAMPED_AUTH");
1656 return 0;
1657 }
1658 if (argc == 3) {
1659 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1660 pw_arg = 2;
1661 } else if (argc == 2) {
1662 if (nickserv_conf.disable_nicks) {
1663 if (!(hi = get_handle_info(user->nick))) {
1664 reply("NSMSG_HANDLE_NOT_FOUND");
1665 return 0;
1666 }
1667 } else {
1668 /* try to look up their handle from their nick */
1669 struct nick_info *ni;
1670 ni = get_nick_info(user->nick);
1671 if (!ni) {
1672 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1673 return 0;
1674 }
1675 hi = ni->owner;
1676 }
1677 pw_arg = 1;
1678 } else {
1679 reply("MSG_MISSING_PARAMS", argv[0]);
1680 svccmd_send_help(user, nickserv, cmd);
1681 return 0;
1682 }
1683 if (!hi) {
1684 reply("NSMSG_HANDLE_NOT_FOUND");
1685 return 0;
1686 }
1687 /* Responses from here on look up the language used by the handle they asked about. */
1688 passwd = argv[pw_arg];
1689 if (!valid_user_for(user, hi)) {
1690 if (hi->email_addr && nickserv_conf.email_enabled)
1691 send_message_type(4, user, cmd->parent->bot,
1692 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1693 hi->handle);
1694 else
1695 send_message_type(4, user, cmd->parent->bot,
1696 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1697 hi->handle);
1698 argv[pw_arg] = "BADMASK";
1699 return 1;
1700 }
1701 if (!checkpass(passwd, hi->passwd)) {
1702 unsigned int n;
1703 send_message_type(4, user, cmd->parent->bot,
1704 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1705 argv[pw_arg] = "BADPASS";
1706 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1707 if (nickserv_conf.autogag_enabled) {
1708 if (!user->auth_policer.params) {
1709 user->auth_policer.last_req = now;
1710 user->auth_policer.params = nickserv_conf.auth_policer_params;
1711 }
1712 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1713 char *hostmask;
1714 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1715 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1716 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1717 free(hostmask);
1718 argv[pw_arg] = "GAGGED";
1719 }
1720 }
1721 return 1;
1722 }
1723 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1724 send_message_type(4, user, cmd->parent->bot,
1725 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1726 argv[pw_arg] = "SUSPENDED";
1727 return 1;
1728 }
1729 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1730 for (used = 0, other = hi->users; other; other = other->next_authed) {
1731 if (++used >= maxlogins) {
1732 send_message_type(4, user, cmd->parent->bot,
1733 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1734 maxlogins);
1735 argv[pw_arg] = "MAXLOGINS";
1736 return 1;
1737 }
1738 }
1739
1740 set_user_handle_info(user, hi, 1);
1741 if (nickserv_conf.email_required && !hi->email_addr)
1742 reply("NSMSG_PLEASE_SET_EMAIL");
1743 if (!is_secure_password(hi->handle, passwd, NULL))
1744 reply("NSMSG_WEAK_PASSWORD");
1745 if (hi->passwd[0] != '$')
1746 cryptpass(passwd, hi->passwd);
1747 reply("NSMSG_AUTH_SUCCESS");
1748 argv[pw_arg] = "****";
1749 return 1;
1750}
1751
1752static allowauth_func_t *allowauth_func_list;
1753static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1754
1755void
1756reg_allowauth_func(allowauth_func_t func)
1757{
1758 if (allowauth_func_used == allowauth_func_size) {
1759 if (allowauth_func_size) {
1760 allowauth_func_size <<= 1;
1761 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1762 } else {
1763 allowauth_func_size = 8;
1764 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1765 }
1766 }
1767 allowauth_func_list[allowauth_func_used++] = func;
1768}
1769
1770static NICKSERV_FUNC(cmd_allowauth)
1771{
1772 struct userNode *target;
1773 struct handle_info *hi;
1774 unsigned int n;
1775
1776 NICKSERV_MIN_PARMS(2);
1777 if (!(target = GetUserH(argv[1]))) {
1778 reply("MSG_NICK_UNKNOWN", argv[1]);
1779 return 0;
1780 }
1781 if (target->handle_info) {
1782 reply("NSMSG_USER_PREV_AUTH", target->nick);
1783 return 0;
1784 }
1785 if (IsStamped(target)) {
1786 /* Unauthenticated users might still have been stamped
1787 previously and could therefore have a hidden host;
1788 do not allow them to authenticate to an account. */
1789 reply("NSMSG_USER_PREV_STAMP", target->nick);
1790 return 0;
1791 }
1792 if (argc == 2)
1793 hi = NULL;
1794 else if (!(hi = get_handle_info(argv[2]))) {
1795 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1796 return 0;
1797 }
1798 if (hi) {
1799 if (hi->opserv_level > user->handle_info->opserv_level) {
1800 reply("MSG_USER_OUTRANKED", hi->handle);
1801 return 0;
1802 }
1803 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1804 || (hi->opserv_level > 0))
1805 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1806 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1807 return 0;
1808 }
1809 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1810 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1811 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1812 if (nickserv_conf.email_enabled)
1813 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1814 } else {
1815 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1816 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1817 else
1818 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1819 }
1820 for (n=0; n<allowauth_func_used; n++)
1821 allowauth_func_list[n](user, target, hi);
1822 return 1;
1823}
1824
1825static NICKSERV_FUNC(cmd_authcookie)
1826{
1827 struct handle_info *hi;
1828
1829 NICKSERV_MIN_PARMS(2);
1830 if (user->handle_info) {
1831 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1832 return 0;
1833 }
1834 if (IsStamped(user)) {
1835 /* Unauthenticated users might still have been stamped
1836 previously and could therefore have a hidden host;
1837 do not allow them to authenticate to an account. */
1838 reply("NSMSG_STAMPED_AUTHCOOKIE");
1839 return 0;
1840 }
1841 if (!(hi = get_handle_info(argv[1]))) {
1842 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1843 return 0;
1844 }
1845 if (!hi->email_addr) {
1846 reply("MSG_SET_EMAIL_ADDR");
1847 return 0;
1848 }
1c0d5243 1849 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL, 0);
d76ed9a9 1850 return 1;
1851}
1852
1853static NICKSERV_FUNC(cmd_delcookie)
1854{
1855 struct handle_info *hi;
1856
1857 hi = user->handle_info;
1858 if (!hi->cookie) {
1859 reply("NSMSG_NO_COOKIE");
1860 return 0;
1861 }
1862 switch (hi->cookie->type) {
1863 case ACTIVATION:
1864 case EMAIL_CHANGE:
1865 reply("NSMSG_MUST_TIME_OUT");
1866 break;
1867 default:
1868 nickserv_eat_cookie(hi->cookie);
1869 reply("NSMSG_ATE_COOKIE");
1870 break;
1871 }
1872 return 1;
1873}
1874
1875static NICKSERV_FUNC(cmd_resetpass)
1876{
1877 struct handle_info *hi;
1878 char crypted[MD5_CRYPT_LENGTH];
1c0d5243 1879 int weblink;
d76ed9a9 1880
1881 NICKSERV_MIN_PARMS(3);
1c0d5243 1882 if(argc >= 4 && !strcmp(argv[3], "WEBLINK"))
1883 weblink = 1;
1884 else
1885 weblink = 0;
d76ed9a9 1886 if (user->handle_info) {
1887 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1888 return 0;
1889 }
1890 if (IsStamped(user)) {
1891 /* Unauthenticated users might still have been stamped
1892 previously and could therefore have a hidden host;
1893 do not allow them to activate an account. */
1894 reply("NSMSG_STAMPED_RESETPASS");
1895 return 0;
1896 }
1897 if (!(hi = get_handle_info(argv[1]))) {
1898 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1899 return 0;
1900 }
1901 if (!hi->email_addr) {
1902 reply("MSG_SET_EMAIL_ADDR");
1903 return 0;
1904 }
1905 cryptpass(argv[2], crypted);
1906 argv[2] = "****";
1c0d5243 1907 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted, weblink);
d76ed9a9 1908 return 1;
1909}
1910
1911static NICKSERV_FUNC(cmd_cookie)
1912{
1913 struct handle_info *hi;
1914 const char *cookie;
1915
1916 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1917 cookie = argv[1];
1918 } else {
1919 NICKSERV_MIN_PARMS(3);
1920 if (!(hi = get_handle_info(argv[1]))) {
1921 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1922 return 0;
1923 }
1924 cookie = argv[2];
1925 }
1926
1927 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1928 reply("NSMSG_HANDLE_SUSPENDED");
1929 return 0;
1930 }
1931
1932 if (!hi->cookie) {
1933 reply("NSMSG_NO_COOKIE");
1934 return 0;
1935 }
1936
1937 /* Check validity of operation before comparing cookie to
1938 * prohibit guessing by authed users. */
1939 if (user->handle_info
1940 && (hi->cookie->type != EMAIL_CHANGE)
1941 && (hi->cookie->type != PASSWORD_CHANGE)) {
1942 reply("NSMSG_CANNOT_COOKIE");
1943 return 0;
1944 }
1945
1946 if (strcmp(cookie, hi->cookie->cookie)) {
1947 reply("NSMSG_BAD_COOKIE");
1948 return 0;
1949 }
1950
1951 switch (hi->cookie->type) {
1952 case ACTIVATION:
1953 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1954 set_user_handle_info(user, hi, 1);
1955 reply("NSMSG_HANDLE_ACTIVATED");
8dc1d9ae 1956 if (nickserv_conf.sync_log)
1957 SyncLog("ACCOUNTACC %s", hi->handle);
d76ed9a9 1958 break;
1959 case PASSWORD_CHANGE:
1960 set_user_handle_info(user, hi, 1);
1961 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1962 reply("NSMSG_PASSWORD_CHANGED");
8dc1d9ae 1963 if (nickserv_conf.sync_log)
1964 SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
d76ed9a9 1965 break;
1966 case EMAIL_CHANGE:
1967 nickserv_set_email_addr(hi, hi->cookie->data);
1968 reply("NSMSG_EMAIL_CHANGED");
8dc1d9ae 1969 if (nickserv_conf.sync_log)
1970 SyncLog("EMAILCHANGE %s %s", hi->handle, hi->cookie->data);
d76ed9a9 1971 break;
1972 case ALLOWAUTH:
1973 set_user_handle_info(user, hi, 1);
1974 reply("NSMSG_AUTH_SUCCESS");
1975 break;
1976 default:
1977 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1978 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1979 break;
1980 }
1981
1982 nickserv_eat_cookie(hi->cookie);
1983
1984 return 1;
1985}
1986
1987static NICKSERV_FUNC(cmd_oregnick) {
1988 const char *nick;
1989 struct handle_info *target;
1990 struct nick_info *ni;
1991
1992 NICKSERV_MIN_PARMS(3);
1993 if (!(target = modcmd_get_handle_info(user, argv[1])))
1994 return 0;
1995 nick = argv[2];
1996 if (!is_registerable_nick(nick)) {
1997 reply("NSMSG_BAD_NICK", nick);
1998 return 0;
1999 }
2000 ni = dict_find(nickserv_nick_dict, nick, NULL);
2001 if (ni) {
2002 reply("NSMSG_NICK_EXISTS", nick);
2003 return 0;
2004 }
2005 register_nick(nick, target);
2006 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2007 return 1;
2008}
2009
2010static NICKSERV_FUNC(cmd_regnick) {
2011 unsigned n;
2012 struct nick_info *ni;
2013
2014 if (!is_registerable_nick(user->nick)) {
2015 reply("NSMSG_BAD_NICK", user->nick);
2016 return 0;
2017 }
2018 /* count their nicks, see if it's too many */
2019 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2020 if (n >= nickserv_conf.nicks_per_handle) {
2021 reply("NSMSG_TOO_MANY_NICKS");
2022 return 0;
2023 }
2024 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2025 if (ni) {
2026 reply("NSMSG_NICK_EXISTS", user->nick);
2027 return 0;
2028 }
2029 register_nick(user->nick, user->handle_info);
2030 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2031 return 1;
2032}
2033
2034static NICKSERV_FUNC(cmd_pass)
2035{
2036 struct handle_info *hi;
2037 const char *old_pass, *new_pass;
2038
2039 NICKSERV_MIN_PARMS(3);
2040 hi = user->handle_info;
2041 old_pass = argv[1];
2042 new_pass = argv[2];
2043 argv[2] = "****";
2044 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2045 if (!checkpass(old_pass, hi->passwd)) {
2046 argv[1] = "BADPASS";
2047 reply("NSMSG_PASSWORD_INVALID");
2048 return 0;
2049 }
2050 cryptpass(new_pass, hi->passwd);
8dc1d9ae 2051 if (nickserv_conf.sync_log)
2052 SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
d76ed9a9 2053 argv[1] = "****";
2054 reply("NSMSG_PASS_SUCCESS");
2055 return 1;
2056}
2057
2058static int
2059nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2060{
2061 unsigned int i;
2062 char *new_mask = canonicalize_hostmask(strdup(mask));
2063 for (i=0; i<hi->masks->used; i++) {
2064 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2065 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2066 free(new_mask);
2067 return 0;
2068 }
2069 }
2070 string_list_append(hi->masks, new_mask);
2071 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2072 return 1;
2073}
2074
2075static NICKSERV_FUNC(cmd_addmask)
2076{
2077 if (argc < 2) {
2078 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2079 int res = nickserv_addmask(user, user->handle_info, mask);
2080 free(mask);
2081 return res;
2082 } else {
2083 if (!is_gline(argv[1])) {
2084 reply("NSMSG_MASK_INVALID", argv[1]);
2085 return 0;
2086 }
2087 return nickserv_addmask(user, user->handle_info, argv[1]);
2088 }
2089}
2090
2091static NICKSERV_FUNC(cmd_oaddmask)
2092{
2093 struct handle_info *hi;
2094
2095 NICKSERV_MIN_PARMS(3);
2096 if (!(hi = get_victim_oper(user, argv[1])))
2097 return 0;
2098 return nickserv_addmask(user, hi, argv[2]);
2099}
2100
2101static int
2102nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
2103{
2104 unsigned int i;
2105 for (i=0; i<hi->masks->used; i++) {
2106 if (!strcmp(del_mask, hi->masks->list[i])) {
2107 char *old_mask = hi->masks->list[i];
2108 if (hi->masks->used == 1) {
2109 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2110 return 0;
2111 }
2112 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2113 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2114 free(old_mask);
2115 return 1;
2116 }
2117 }
2118 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2119 return 0;
2120}
2121
2122static NICKSERV_FUNC(cmd_delmask)
2123{
2124 NICKSERV_MIN_PARMS(2);
2125 return nickserv_delmask(user, user->handle_info, argv[1]);
2126}
2127
2128static NICKSERV_FUNC(cmd_odelmask)
2129{
2130 struct handle_info *hi;
2131 NICKSERV_MIN_PARMS(3);
2132 if (!(hi = get_victim_oper(user, argv[1])))
2133 return 0;
2134 return nickserv_delmask(user, hi, argv[2]);
2135}
2136
2137int
2138nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2139 unsigned int nn, add = 1, pos;
2140 unsigned long added, removed, flag;
2141
2142 for (added=removed=nn=0; str[nn]; nn++) {
2143 switch (str[nn]) {
2144 case '+': add = 1; break;
2145 case '-': add = 0; break;
2146 default:
2147 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2148 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2149 return 0;
2150 }
2151 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2152 /* cheesy avoidance of looking up the flag name.. */
2153 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2154 return 0;
2155 }
2156 flag = 1 << (pos - 1);
2157 if (add)
2158 added |= flag, removed &= ~flag;
2159 else
2160 removed |= flag, added &= ~flag;
2161 break;
2162 }
2163 }
2164 *padded = added;
2165 *premoved = removed;
2166 return 1;
2167}
2168
2169static int
2170nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2171{
2172 unsigned long before, after, added, removed;
2173 struct userNode *uNode;
2174
2175 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2176 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2177 return 0;
2178 hi->flags = (hi->flags | added) & ~removed;
2179 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2180
2181 /* Strip helping flag if they're only a support helper and not
2182 * currently in #support. */
2183 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2184 struct channelList *schannels;
2185 unsigned int ii;
2186 schannels = chanserv_support_channels();
2187 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2188 for (ii = 0; ii < schannels->used; ++ii)
2189 if (GetUserMode(schannels->list[ii], uNode))
2190 break;
2191 if (ii < schannels->used)
2192 break;
2193 }
2194 if (!uNode)
2195 HANDLE_CLEAR_FLAG(hi, HELPING);
2196 }
2197
2198 if (after && !before) {
2199 /* Add user to current helper list. */
2200 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2201 userList_append(&curr_helpers, uNode);
2202 } else if (!after && before) {
2203 /* Remove user from current helper list. */
2204 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2205 userList_remove(&curr_helpers, uNode);
2206 }
2207
2208 return 1;
2209}
2210
2211static void
2212set_list(struct userNode *user, struct handle_info *hi, int override)
2213{
2214 option_func_t *opt;
2215 unsigned int i;
2216 char *set_display[] = {
2217 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2218 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2219 };
2220
2221 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2222
2223 /* Do this so options are presented in a consistent order. */
2224 for (i = 0; i < ArrayLength(set_display); ++i)
2225 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2226 opt(user, hi, override, 0, NULL);
2227}
2228
2229static NICKSERV_FUNC(cmd_set)
2230{
2231 struct handle_info *hi;
2232 option_func_t *opt;
2233
2234 hi = user->handle_info;
2235 if (argc < 2) {
2236 set_list(user, hi, 0);
2237 return 1;
2238 }
2239 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2240 reply("NSMSG_INVALID_OPTION", argv[1]);
2241 return 0;
2242 }
2243 return opt(user, hi, 0, argc-1, argv+1);
2244}
2245
2246static NICKSERV_FUNC(cmd_oset)
2247{
2248 struct handle_info *hi;
2249 option_func_t *opt;
2250
2251 NICKSERV_MIN_PARMS(2);
2252
2253 if (!(hi = get_victim_oper(user, argv[1])))
2254 return 0;
2255
2256 if (argc < 3) {
2257 set_list(user, hi, 0);
2258 return 1;
2259 }
2260
2261 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2262 reply("NSMSG_INVALID_OPTION", argv[2]);
2263 return 0;
2264 }
2265
2266 return opt(user, hi, 1, argc-2, argv+2);
2267}
2268
2269static OPTION_FUNC(opt_info)
2270{
2271 const char *info;
2272 if (argc > 1) {
2273 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2274 free(hi->infoline);
2275 hi->infoline = NULL;
2276 } else {
2277 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2278 }
2279 }
2280
2281 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2282 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2283 return 1;
2284}
2285
2286static OPTION_FUNC(opt_width)
2287{
2288 if (argc > 1)
2289 hi->screen_width = strtoul(argv[1], NULL, 0);
2290
2291 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2292 hi->screen_width = MIN_LINE_SIZE;
2293 else if (hi->screen_width > MAX_LINE_SIZE)
2294 hi->screen_width = MAX_LINE_SIZE;
2295
2296 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2297 return 1;
2298}
2299
2300static OPTION_FUNC(opt_tablewidth)
2301{
2302 if (argc > 1)
2303 hi->table_width = strtoul(argv[1], NULL, 0);
2304
2305 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2306 hi->table_width = MIN_LINE_SIZE;
2307 else if (hi->screen_width > MAX_LINE_SIZE)
2308 hi->table_width = MAX_LINE_SIZE;
2309
2310 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2311 return 1;
2312}
2313
2314static OPTION_FUNC(opt_color)
2315{
2316 if (argc > 1) {
2317 if (enabled_string(argv[1]))
2318 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2319 else if (disabled_string(argv[1]))
2320 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2321 else {
2322 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2323 return 0;
2324 }
2325 }
2326
2327 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2328 return 1;
2329}
2330
2331static OPTION_FUNC(opt_privmsg)
2332{
2333 if (argc > 1) {
2334 if (enabled_string(argv[1]))
2335 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2336 else if (disabled_string(argv[1]))
2337 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2338 else {
2339 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2340 return 0;
2341 }
2342 }
2343
2344 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2345 return 1;
2346}
2347
2348static OPTION_FUNC(opt_style)
2349{
2350 char *style;
2351
2352 if (argc > 1) {
2353 if (!irccasecmp(argv[1], "Zoot"))
2354 hi->userlist_style = HI_STYLE_ZOOT;
2355 else if (!irccasecmp(argv[1], "def"))
2356 hi->userlist_style = HI_STYLE_DEF;
2357 }
2358
2359 switch (hi->userlist_style) {
2360 case HI_STYLE_DEF:
2361 style = "def";
2362 break;
2363 case HI_STYLE_ZOOT:
2364 default:
2365 style = "Zoot";
2366 }
2367
2368 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2369 return 1;
2370}
2371
2372static OPTION_FUNC(opt_announcements)
2373{
2374 const char *choice;
2375
2376 if (argc > 1) {
2377 if (enabled_string(argv[1]))
2378 hi->announcements = 'y';
2379 else if (disabled_string(argv[1]))
2380 hi->announcements = 'n';
2381 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2382 hi->announcements = '?';
2383 else {
2384 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2385 return 0;
2386 }
2387 }
2388
2389 switch (hi->announcements) {
2390 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2391 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2392 case '?': choice = "default"; break;
2393 default: choice = "unknown"; break;
2394 }
2395 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2396 return 1;
2397}
2398
2399static OPTION_FUNC(opt_password)
2400{
2401 if (!override) {
2402 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2403 return 0;
2404 }
2405
2406 if (argc > 1)
2407 cryptpass(argv[1], hi->passwd);
2408
2409 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2410 return 1;
2411}
2412
2413static OPTION_FUNC(opt_flags)
2414{
2415 char flags[33];
2416 unsigned int ii, flen;
2417
2418 if (!override) {
2419 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2420 return 0;
2421 }
2422
2423 if (argc > 1)
2424 nickserv_apply_flags(user, hi, argv[1]);
2425
2426 for (ii = flen = 0; handle_flags[ii]; ii++)
2427 if (hi->flags & (1 << ii))
2428 flags[flen++] = handle_flags[ii];
2429 flags[flen] = '\0';
2430 if (hi->flags)
2431 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2432 else
2433 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2434 return 1;
2435}
2436
2437static OPTION_FUNC(opt_email)
2438{
2439 if (argc > 1) {
2440 const char *str;
2441 if (!is_valid_email_addr(argv[1])) {
2442 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2443 return 0;
2444 }
2445 if ((str = sendmail_prohibited_address(argv[1]))) {
2446 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2447 return 0;
2448 }
2449 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2450 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2451 else if (!override)
1c0d5243 2452 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1], 0);
d76ed9a9 2453 else {
2454 nickserv_set_email_addr(hi, argv[1]);
2455 if (hi->cookie)
2456 nickserv_eat_cookie(hi->cookie);
2457 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2458 }
2459 } else
2460 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2461 return 1;
2462}
2463
2464static OPTION_FUNC(opt_maxlogins)
2465{
2466 unsigned char maxlogins;
2467 if (argc > 1) {
2468 maxlogins = strtoul(argv[1], NULL, 0);
2469 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2470 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2471 return 0;
2472 }
2473 hi->maxlogins = maxlogins;
2474 }
2475 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2476 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2477 return 1;
2478}
2479
2480static OPTION_FUNC(opt_language)
2481{
2482 struct language *lang;
2483 if (argc > 1) {
2484 lang = language_find(argv[1]);
2485 if (irccasecmp(lang->name, argv[1]))
2486 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2487 hi->language = lang;
2488 }
2489 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2490 return 1;
2491}
2492
2493int
2494oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2495 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2496 return 0;
2497 if ((user->handle_info->opserv_level < target->opserv_level)
2498 || ((user->handle_info->opserv_level == target->opserv_level)
2499 && (user->handle_info->opserv_level < 1000))) {
2500 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2501 return 0;
2502 }
2503 if ((user->handle_info->opserv_level < new_level)
2504 || ((user->handle_info->opserv_level == new_level)
2505 && (user->handle_info->opserv_level < 1000))) {
2506 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2507 return 0;
2508 }
2509 if (user->handle_info == target) {
2510 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2511 return 0;
2512 }
2513 if (target->opserv_level == new_level)
2514 return 0;
2515 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2516 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2517 target->opserv_level = new_level;
2518 return 1;
2519}
2520
2521static OPTION_FUNC(opt_level)
2522{
2523 int res;
2524
2525 if (!override) {
2526 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2527 return 0;
2528 }
2529
2530 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2531 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2532 return res;
2533}
2534
2535static OPTION_FUNC(opt_epithet)
2536{
2537 if (!override) {
2538 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2539 return 0;
2540 }
2541
2542 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2543 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2544 if (hi->epithet)
2545 free(hi->epithet);
2546 if ((epithet[0] == '*') && !epithet[1])
2547 hi->epithet = NULL;
2548 else
2549 hi->epithet = strdup(epithet);
2550 }
2551
2552 if (hi->epithet)
2553 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2554 else
2555 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2556 return 1;
2557}
2558
2559static OPTION_FUNC(opt_title)
2560{
2561 const char *title;
2562
2563 if (!override) {
2564 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2565 return 0;
2566 }
2567
2568 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2569 title = argv[1];
2570 if (strchr(title, '.')) {
2571 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2572 return 0;
2573 }
a32da4c7 2574 if ((strlen(user->handle_info->handle) + strlen(title) +
2575 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2576 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2577 return 0;
2578 }
2579
d76ed9a9 2580 free(hi->fakehost);
2581 if (!strcmp(title, "*")) {
2582 hi->fakehost = NULL;
2583 } else {
2584 hi->fakehost = malloc(strlen(title)+2);
2585 hi->fakehost[0] = '.';
2586 strcpy(hi->fakehost+1, title);
2587 }
2588 apply_fakehost(hi);
2589 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2590 title = hi->fakehost + 1;
2591 else
2592 title = NULL;
2593 if (!title)
2594 title = user_find_message(user, "MSG_NONE");
2595 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2596 return 1;
2597}
2598
2599static OPTION_FUNC(opt_fakehost)
2600{
2601 const char *fake;
2602
2603 if (!override) {
2604 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2605 return 0;
2606 }
2607
2608 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2609 fake = argv[1];
2610 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
a32da4c7 2611 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
d76ed9a9 2612 return 0;
2613 }
2614 free(hi->fakehost);
2615 if (!strcmp(fake, "*"))
2616 hi->fakehost = NULL;
2617 else
2618 hi->fakehost = strdup(fake);
2619 fake = hi->fakehost;
2620 apply_fakehost(hi);
2621 } else
2622 fake = generate_fakehost(hi);
2623 if (!fake)
2624 fake = user_find_message(user, "MSG_NONE");
2625 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2626 return 1;
2627}
2628
2629static NICKSERV_FUNC(cmd_reclaim)
2630{
2631 struct handle_info *hi;
2632 struct nick_info *ni;
2633 struct userNode *victim;
2634
2635 NICKSERV_MIN_PARMS(2);
2636 hi = user->handle_info;
2637 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2638 if (!ni) {
2639 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2640 return 0;
2641 }
2642 if (ni->owner != user->handle_info) {
2643 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2644 return 0;
2645 }
2646 victim = GetUserH(ni->nick);
2647 if (!victim) {
2648 reply("MSG_NICK_UNKNOWN", ni->nick);
2649 return 0;
2650 }
2651 if (victim == user) {
2652 reply("NSMSG_NICK_USER_YOU");
2653 return 0;
2654 }
2655 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2656 switch (nickserv_conf.reclaim_action) {
2657 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2658 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2659 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2660 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2661 }
2662 return 1;
2663}
2664
2665static NICKSERV_FUNC(cmd_unregnick)
2666{
2667 const char *nick;
2668 struct handle_info *hi;
2669 struct nick_info *ni;
2670
2671 hi = user->handle_info;
2672 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2673 ni = dict_find(nickserv_nick_dict, nick, NULL);
2674 if (!ni) {
2675 reply("NSMSG_UNKNOWN_NICK", nick);
2676 return 0;
2677 }
2678 if (hi != ni->owner) {
2679 reply("NSMSG_NOT_YOUR_NICK", nick);
2680 return 0;
2681 }
2682 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2683 delete_nick(ni);
2684 return 1;
2685}
2686
2687static NICKSERV_FUNC(cmd_ounregnick)
2688{
2689 struct nick_info *ni;
2690
2691 NICKSERV_MIN_PARMS(2);
2692 if (!(ni = get_nick_info(argv[1]))) {
2693 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2694 return 0;
2695 }
2696 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2697 reply("MSG_USER_OUTRANKED", ni->nick);
2698 return 0;
2699 }
2700 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2701 delete_nick(ni);
2702 return 1;
2703}
2704
2705static NICKSERV_FUNC(cmd_unregister)
2706{
2707 struct handle_info *hi;
2708 char *passwd;
2709
2710 NICKSERV_MIN_PARMS(2);
2711 hi = user->handle_info;
2712 passwd = argv[1];
2713 argv[1] = "****";
2714 if (checkpass(passwd, hi->passwd)) {
2715 nickserv_unregister_handle(hi, user);
2716 return 1;
2717 } else {
2718 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2719 reply("NSMSG_PASSWORD_INVALID");
2720 return 0;
2721 }
2722}
2723
2724static NICKSERV_FUNC(cmd_ounregister)
2725{
2726 struct handle_info *hi;
2727
2728 NICKSERV_MIN_PARMS(2);
2729 if (!(hi = get_victim_oper(user, argv[1])))
2730 return 0;
2731 nickserv_unregister_handle(hi, user);
a32da4c7 2732 return 1;
d76ed9a9 2733}
2734
2735static NICKSERV_FUNC(cmd_status)
2736{
2737 if (nickserv_conf.disable_nicks) {
2738 reply("NSMSG_GLOBAL_STATS_NONICK",
2739 dict_size(nickserv_handle_dict));
2740 } else {
2741 if (user->handle_info) {
2742 int cnt=0;
2743 struct nick_info *ni;
2744 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2745 reply("NSMSG_HANDLE_STATS", cnt);
2746 } else {
2747 reply("NSMSG_HANDLE_NONE");
2748 }
2749 reply("NSMSG_GLOBAL_STATS",
2750 dict_size(nickserv_handle_dict),
2751 dict_size(nickserv_nick_dict));
2752 }
2753 return 1;
2754}
2755
2756static NICKSERV_FUNC(cmd_ghost)
2757{
2758 struct userNode *target;
2759 char reason[MAXLEN];
2760
2761 NICKSERV_MIN_PARMS(2);
2762 if (!(target = GetUserH(argv[1]))) {
2763 reply("MSG_NICK_UNKNOWN", argv[1]);
2764 return 0;
2765 }
2766 if (target == user) {
2767 reply("NSMSG_CANNOT_GHOST_SELF");
2768 return 0;
2769 }
2770 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2771 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2772 return 0;
2773 }
2774 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2775 DelUser(target, nickserv, 1, reason);
2776 reply("NSMSG_GHOST_KILLED", argv[1]);
2777 return 1;
2778}
2779
2780static NICKSERV_FUNC(cmd_vacation)
2781{
2782 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2783 reply("NSMSG_ON_VACATION");
2784 return 1;
2785}
2786
2787static int
2788nickserv_saxdb_write(struct saxdb_context *ctx) {
2789 dict_iterator_t it;
2790 struct handle_info *hi;
2791 char flags[33];
2792
2793 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2794 hi = iter_data(it);
2795#ifdef WITH_PROTOCOL_BAHAMUT
2796 assert(hi->id);
2797#endif
2798 saxdb_start_record(ctx, iter_key(it), 0);
2799 if (hi->announcements != '?') {
2800 flags[0] = hi->announcements;
2801 flags[1] = 0;
2802 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2803 }
2804 if (hi->cookie) {
2805 struct handle_cookie *cookie = hi->cookie;
2806 char *type;
2807
2808 switch (cookie->type) {
2809 case ACTIVATION: type = KEY_ACTIVATION; break;
2810 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2811 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2812 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2813 default: type = NULL; break;
2814 }
2815 if (type) {
2816 saxdb_start_record(ctx, KEY_COOKIE, 0);
2817 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2818 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2819 if (cookie->data)
2820 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2821 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2822 saxdb_end_record(ctx);
2823 }
2824 }
2825 if (hi->email_addr)
2826 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2827 if (hi->epithet)
2828 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2829 if (hi->fakehost)
2830 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2831 if (hi->flags) {
2832 int ii, flen;
2833
2834 for (ii=flen=0; handle_flags[ii]; ++ii)
2835 if (hi->flags & (1 << ii))
2836 flags[flen++] = handle_flags[ii];
2837 flags[flen] = 0;
2838 saxdb_write_string(ctx, KEY_FLAGS, flags);
2839 }
2840#ifdef WITH_PROTOCOL_BAHAMUT
2841 saxdb_write_int(ctx, KEY_ID, hi->id);
2842#endif
2843 if (hi->infoline)
2844 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2845 if (hi->last_quit_host[0])
2846 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2847 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2848 if (hi->masks->used)
2849 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2850 if (hi->maxlogins)
2851 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2852 if (hi->nicks) {
2853 struct string_list *slist;
2854 struct nick_info *ni;
2855
2856 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2857 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2858 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2859 free(slist->list);
2860 free(slist);
2861 }
2862 if (hi->opserv_level)
2863 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2864 if (hi->language != lang_C)
2865 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2866 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2867 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2868 if (hi->screen_width)
2869 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2870 if (hi->table_width)
2871 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2872 flags[0] = hi->userlist_style;
2873 flags[1] = 0;
2874 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2875 saxdb_end_record(ctx);
2876 }
2877 return 0;
2878}
2879
2880static handle_merge_func_t *handle_merge_func_list;
2881static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2882
2883void
2884reg_handle_merge_func(handle_merge_func_t func)
2885{
2886 if (handle_merge_func_used == handle_merge_func_size) {
2887 if (handle_merge_func_size) {
2888 handle_merge_func_size <<= 1;
2889 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2890 } else {
2891 handle_merge_func_size = 8;
2892 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2893 }
2894 }
2895 handle_merge_func_list[handle_merge_func_used++] = func;
2896}
2897
2898static NICKSERV_FUNC(cmd_merge)
2899{
2900 struct handle_info *hi_from, *hi_to;
2901 struct userNode *last_user;
2902 struct userData *cList, *cListNext;
2903 unsigned int ii, jj, n;
2904 char buffer[MAXLEN];
2905
2906 NICKSERV_MIN_PARMS(3);
2907
2908 if (!(hi_from = get_victim_oper(user, argv[1])))
2909 return 0;
2910 if (!(hi_to = get_victim_oper(user, argv[2])))
2911 return 0;
2912 if (hi_to == hi_from) {
2913 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2914 return 0;
2915 }
2916
2917 for (n=0; n<handle_merge_func_used; n++)
2918 handle_merge_func_list[n](user, hi_to, hi_from);
2919
2920 /* Append "from" handle's nicks to "to" handle's nick list. */
2921 if (hi_to->nicks) {
2922 struct nick_info *last_ni;
2923 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2924 last_ni->next = hi_from->nicks;
2925 }
2926 while (hi_from->nicks) {
2927 hi_from->nicks->owner = hi_to;
2928 hi_from->nicks = hi_from->nicks->next;
2929 }
2930
2931 /* Merge the hostmasks. */
2932 for (ii=0; ii<hi_from->masks->used; ii++) {
2933 char *mask = hi_from->masks->list[ii];
2934 for (jj=0; jj<hi_to->masks->used; jj++)
2935 if (match_ircglobs(hi_to->masks->list[jj], mask))
2936 break;
2937 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2938 string_list_append(hi_to->masks, strdup(mask));
2939 }
2940
2941 /* Merge the lists of authed users. */
2942 if (hi_to->users) {
2943 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2944 last_user->next_authed = hi_from->users;
2945 } else {
2946 hi_to->users = hi_from->users;
2947 }
2948 /* Repoint the old "from" handle's users. */
2949 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2950 last_user->handle_info = hi_to;
2951 }
2952 hi_from->users = NULL;
2953
2954 /* Merge channel userlists. */
2955 for (cList=hi_from->channels; cList; cList=cListNext) {
2956 struct userData *cList2;
2957 cListNext = cList->u_next;
2958 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2959 if (cList->channel == cList2->channel)
2960 break;
2961 if (cList2 && (cList2->access >= cList->access)) {
2962 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);
2963 /* keep cList2 in hi_to; remove cList from hi_from */
2964 del_channel_user(cList, 1);
2965 } else {
2966 if (cList2) {
2967 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);
2968 /* remove the lower-ranking cList2 from hi_to */
2969 del_channel_user(cList2, 1);
2970 } else {
2971 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
2972 }
2973 /* cList needs to be moved from hi_from to hi_to */
2974 cList->handle = hi_to;
2975 /* Remove from linked list for hi_from */
2976 assert(!cList->u_prev);
2977 hi_from->channels = cList->u_next;
2978 if (cList->u_next)
2979 cList->u_next->u_prev = cList->u_prev;
2980 /* Add to linked list for hi_to */
2981 cList->u_prev = NULL;
2982 cList->u_next = hi_to->channels;
2983 if (hi_to->channels)
2984 hi_to->channels->u_prev = cList;
2985 hi_to->channels = cList;
2986 }
2987 }
2988
2989 /* Do they get an OpServ level promotion? */
2990 if (hi_from->opserv_level > hi_to->opserv_level)
2991 hi_to->opserv_level = hi_from->opserv_level;
2992
2993 /* What about last seen time? */
2994 if (hi_from->lastseen > hi_to->lastseen)
2995 hi_to->lastseen = hi_from->lastseen;
2996
2997 /* Notify of success. */
2998 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2999 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3000 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3001
3002 /* Unregister the "from" handle. */
3003 nickserv_unregister_handle(hi_from, NULL);
3004
3005 return 1;
3006}
3007
3008struct nickserv_discrim {
3009 unsigned int limit, min_level, max_level;
3010 unsigned long flags_on, flags_off;
3011 time_t min_registered, max_registered;
3012 time_t lastseen;
3013 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3014 const char *nickmask;
3015 const char *hostmask;
3016 const char *handlemask;
3017 const char *emailmask;
3018};
3019
3020typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3021
3022struct discrim_apply_info {
3023 struct nickserv_discrim *discrim;
3024 discrim_search_func func;
3025 struct userNode *source;
3026 unsigned int matched;
3027};
3028
3029static struct nickserv_discrim *
3030nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3031{
3032 unsigned int i;
3033 struct nickserv_discrim *discrim;
3034
3035 discrim = malloc(sizeof(*discrim));
3036 memset(discrim, 0, sizeof(*discrim));
3037 discrim->min_level = 0;
3038 discrim->max_level = ~0;
3039 discrim->limit = 50;
3040 discrim->min_registered = 0;
3041 discrim->max_registered = INT_MAX;
3042 discrim->lastseen = now;
3043
3044 for (i=0; i<argc; i++) {
3045 if (i == argc - 1) {
3046 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3047 goto fail;
3048 }
3049 if (!irccasecmp(argv[i], "limit")) {
3050 discrim->limit = strtoul(argv[++i], NULL, 0);
3051 } else if (!irccasecmp(argv[i], "flags")) {
3052 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3053 } else if (!irccasecmp(argv[i], "registered")) {
3054 const char *cmp = argv[++i];
3055 if (cmp[0] == '<') {
3056 if (cmp[1] == '=') {
3057 discrim->min_registered = now - ParseInterval(cmp+2);
3058 } else {
3059 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3060 }
3061 } else if (cmp[0] == '=') {
3062 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3063 } else if (cmp[0] == '>') {
3064 if (cmp[1] == '=') {
3065 discrim->max_registered = now - ParseInterval(cmp+2);
3066 } else {
3067 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3068 }
3069 } else {
3070 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3071 }
3072 } else if (!irccasecmp(argv[i], "seen")) {
3073 discrim->lastseen = now - ParseInterval(argv[++i]);
3074 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3075 discrim->nickmask = argv[++i];
3076 } else if (!irccasecmp(argv[i], "hostmask")) {
3077 i++;
3078 if (!irccasecmp(argv[i], "exact")) {
3079 if (i == argc - 1) {
3080 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3081 goto fail;
3082 }
3083 discrim->hostmask_type = EXACT;
3084 } else if (!irccasecmp(argv[i], "subset")) {
3085 if (i == argc - 1) {
3086 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3087 goto fail;
3088 }
3089 discrim->hostmask_type = SUBSET;
3090 } else if (!irccasecmp(argv[i], "superset")) {
3091 if (i == argc - 1) {
3092 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3093 goto fail;
3094 }
3095 discrim->hostmask_type = SUPERSET;
3096 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3097 if (i == argc - 1) {
3098 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3099 goto fail;
3100 }
3101 discrim->hostmask_type = LASTQUIT;
3102 } else {
3103 i--;
3104 discrim->hostmask_type = SUPERSET;
3105 }
3106 discrim->hostmask = argv[++i];
3107 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3108 if (!irccasecmp(argv[++i], "*")) {
3109 discrim->handlemask = 0;
3110 } else {
3111 discrim->handlemask = argv[i];
3112 }
3113 } else if (!irccasecmp(argv[i], "email")) {
3114 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3115 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3116 goto fail;
3117 } else if (!irccasecmp(argv[++i], "*")) {
3118 discrim->emailmask = 0;
3119 } else {
3120 discrim->emailmask = argv[i];
3121 }
3122 } else if (!irccasecmp(argv[i], "access")) {
3123 const char *cmp = argv[++i];
3124 if (cmp[0] == '<') {
3125 if (discrim->min_level == 0) discrim->min_level = 1;
3126 if (cmp[1] == '=') {
3127 discrim->max_level = strtoul(cmp+2, NULL, 0);
3128 } else {
3129 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3130 }
3131 } else if (cmp[0] == '=') {
3132 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3133 } else if (cmp[0] == '>') {
3134 if (cmp[1] == '=') {
3135 discrim->min_level = strtoul(cmp+2, NULL, 0);
3136 } else {
3137 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3138 }
3139 } else {
3140 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3141 }
3142 } else {
3143 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3144 goto fail;
3145 }
3146 }
3147 return discrim;
3148 fail:
3149 free(discrim);
3150 return NULL;
3151}
3152
3153static int
3154nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3155{
3156 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3157 || (discrim->flags_off & hi->flags)
3158 || (discrim->min_registered > hi->registered)
3159 || (discrim->max_registered < hi->registered)
3160 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3161 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3162 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3163 || (discrim->min_level > hi->opserv_level)
3164 || (discrim->max_level < hi->opserv_level)) {
3165 return 0;
3166 }
3167 if (discrim->hostmask) {
3168 unsigned int i;
3169 for (i=0; i<hi->masks->used; i++) {
3170 const char *mask = hi->masks->list[i];
3171 if ((discrim->hostmask_type == SUBSET)
3172 && (match_ircglobs(discrim->hostmask, mask))) break;
3173 else if ((discrim->hostmask_type == EXACT)
3174 && !irccasecmp(discrim->hostmask, mask)) break;
3175 else if ((discrim->hostmask_type == SUPERSET)
3176 && (match_ircglobs(mask, discrim->hostmask))) break;
3177 else if ((discrim->hostmask_type == LASTQUIT)
3178 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3179 }
3180 if (i==hi->masks->used) return 0;
3181 }
3182 if (discrim->nickmask) {
3183 struct nick_info *nick = hi->nicks;
3184 while (nick) {
3185 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3186 nick = nick->next;
3187 }
3188 if (!nick) return 0;
3189 }
3190 return 1;
3191}
3192
3193static unsigned int
3194nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3195{
3196 dict_iterator_t it, next;
3197 unsigned int matched;
3198
3199 for (it = dict_first(nickserv_handle_dict), matched = 0;
3200 it && (matched < discrim->limit);
3201 it = next) {
3202 next = iter_next(it);
3203 if (nickserv_discrim_match(discrim, iter_data(it))) {
3204 dsf(source, iter_data(it));
3205 matched++;
3206 }
3207 }
3208 return matched;
3209}
3210
3211static void
3212search_print_func(struct userNode *source, struct handle_info *match)
3213{
3214 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3215}
3216
3217static void
3218search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3219{
3220}
3221
3222static void
3223search_unregister_func (struct userNode *source, struct handle_info *match)
3224{
3225 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3226 nickserv_unregister_handle(match, source);
3227}
3228
3229static int
3230nickserv_sort_accounts_by_access(const void *a, const void *b)
3231{
3232 const struct handle_info *hi_a = *(const struct handle_info**)a;
3233 const struct handle_info *hi_b = *(const struct handle_info**)b;
3234 if (hi_a->opserv_level != hi_b->opserv_level)
3235 return hi_b->opserv_level - hi_a->opserv_level;
3236 return irccasecmp(hi_a->handle, hi_b->handle);
3237}
3238
3239void
3240nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3241{
3242 struct handle_info_list hil;
3243 struct helpfile_table tbl;
3244 unsigned int ii;
3245 dict_iterator_t it;
3246 const char **ary;
3247
3248 memset(&hil, 0, sizeof(hil));
3249 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3250 struct handle_info *hi = iter_data(it);
3251 if (hi->opserv_level)
3252 handle_info_list_append(&hil, hi);
3253 }
3254 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3255 tbl.length = hil.used + 1;
3256 tbl.width = 2;
3257 tbl.flags = TABLE_NO_FREE;
3258 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3259 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3260 ary[0] = "Account";
3261 ary[1] = "Level";
3262 for (ii = 0; ii < hil.used; ) {
3263 ary = malloc(tbl.width * sizeof(ary[0]));
3264 ary[0] = hil.list[ii]->handle;
3265 ary[1] = strtab(hil.list[ii]->opserv_level);
3266 tbl.contents[++ii] = ary;
3267 }
3268 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3269 reply("MSG_MATCH_COUNT", hil.used);
3270 for (ii = 0; ii < hil.used; ii++)
3271 free(tbl.contents[ii]);
3272 free(tbl.contents);
3273 free(hil.list);
3274}
3275
3276static NICKSERV_FUNC(cmd_search)
3277{
3278 struct nickserv_discrim *discrim;
3279 discrim_search_func action;
3280 struct svccmd *subcmd;
3281 unsigned int matches;
3282 char buf[MAXLEN];
3283
3284 NICKSERV_MIN_PARMS(3);
3285 sprintf(buf, "search %s", argv[1]);
3286 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3287 if (!irccasecmp(argv[1], "print"))
3288 action = search_print_func;
3289 else if (!irccasecmp(argv[1], "count"))
3290 action = search_count_func;
3291 else if (!irccasecmp(argv[1], "unregister"))
3292 action = search_unregister_func;
3293 else {
3294 reply("NSMSG_INVALID_ACTION", argv[1]);
3295 return 0;
3296 }
3297
3298 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3299 return 0;
3300
3301 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3302 if (!discrim)
3303 return 0;
3304
3305 if (action == search_print_func)
3306 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3307 else if (action == search_count_func)
3308 discrim->limit = INT_MAX;
3309
3310 matches = nickserv_discrim_search(discrim, action, user);
3311
3312 if (matches)
3313 reply("MSG_MATCH_COUNT", matches);
3314 else
3315 reply("MSG_NO_MATCHES");
3316
3317 free(discrim);
3318 return 0;
3319}
3320
3321static MODCMD_FUNC(cmd_checkpass)
3322{
3323 struct handle_info *hi;
3324
3325 NICKSERV_MIN_PARMS(3);
3326 if (!(hi = get_handle_info(argv[1]))) {
3327 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3328 return 0;
3329 }
3330 if (checkpass(argv[2], hi->passwd))
3331 reply("CHECKPASS_YES");
3332 else
3333 reply("CHECKPASS_NO");
3334 argv[2] = "****";
3335 return 1;
3336}
3337
3338static void
3339nickserv_db_read_handle(const char *handle, dict_t obj)
3340{
3341 const char *str;
3342 struct string_list *masks, *slist;
3343 struct handle_info *hi;
3344 struct userNode *authed_users;
3345 unsigned long int id;
3346 unsigned int ii;
3347 dict_t subdb;
3348
3349 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3350 id = str ? strtoul(str, NULL, 0) : 0;
3351 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3352 if (!str) {
3353 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3354 return;
3355 }
3356 if ((hi = get_handle_info(handle))) {
3357 authed_users = hi->users;
3358 hi->users = NULL;
3359 dict_remove(nickserv_handle_dict, hi->handle);
3360 } else {
3361 authed_users = NULL;
3362 }
3363 hi = register_handle(handle, str, id);
3364 if (authed_users) {
3365 hi->users = authed_users;
3366 while (authed_users) {
3367 authed_users->handle_info = hi;
3368 authed_users = authed_users->next_authed;
3369 }
3370 }
3371 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3372 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3373 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3374 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3375 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3376 hi->language = language_find(str ? str : "C");
3377 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3378 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3379 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3380 if (str)
3381 hi->infoline = strdup(str);
3382 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3383 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3384 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3385 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3386 /* We want to read the nicks even if disable_nicks is set. This is so
3387 * that we don't lose the nick data entirely. */
3388 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3389 if (slist) {
3390 for (ii=0; ii<slist->used; ii++)
3391 register_nick(slist->list[ii], hi);
3392 }
3393 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3394 if (str) {
3395 for (ii=0; str[ii]; ii++)
3396 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3397 }
3398 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3399 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3400 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3401 hi->announcements = str ? str[0] : '?';
3402 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3403 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3404 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3405 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3406 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3407 if (!str)
3408 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3409 if (str)
3410 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3411 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3412 if (str)
3413 nickserv_set_email_addr(hi, str);
3414 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3415 if (str)
3416 hi->epithet = strdup(str);
3417 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3418 if (str)
3419 hi->fakehost = strdup(str);
3420 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3421 if (subdb) {
3422 const char *data, *type, *expires, *cookie_str;
3423 struct handle_cookie *cookie;
3424
3425 cookie = calloc(1, sizeof(*cookie));
3426 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3427 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3428 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3429 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3430 if (!type || !expires || !cookie_str) {
3431 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3432 goto cookie_out;
3433 }
3434 if (!irccasecmp(type, KEY_ACTIVATION))
3435 cookie->type = ACTIVATION;
3436 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3437 cookie->type = PASSWORD_CHANGE;
3438 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3439 cookie->type = EMAIL_CHANGE;
3440 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3441 cookie->type = ALLOWAUTH;
3442 else {
3443 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3444 goto cookie_out;
3445 }
3446 cookie->expires = strtoul(expires, NULL, 0);
3447 if (cookie->expires < now)
3448 goto cookie_out;
3449 if (data)
3450 cookie->data = strdup(data);
3451 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3452 cookie->hi = hi;
3453 cookie_out:
3454 if (cookie->hi)
3455 nickserv_bake_cookie(cookie);
3456 else
3457 nickserv_free_cookie(cookie);
3458 }
3459}
3460
3461static int
3462nickserv_saxdb_read(dict_t db) {
3463 dict_iterator_t it;
3464 struct record_data *rd;
3465
3466 for (it=dict_first(db); it; it=iter_next(it)) {
3467 rd = iter_data(it);
3468 nickserv_db_read_handle(iter_key(it), rd->d.object);
3469 }
3470 return 0;
3471}
3472
3473static NICKSERV_FUNC(cmd_mergedb)
3474{
3475 struct timeval start, stop;
3476 dict_t db;
3477
3478 NICKSERV_MIN_PARMS(2);
3479 gettimeofday(&start, NULL);
3480 if (!(db = parse_database(argv[1]))) {
3481 reply("NSMSG_DB_UNREADABLE", argv[1]);
3482 return 0;
3483 }
3484 nickserv_saxdb_read(db);
3485 free_database(db);
3486 gettimeofday(&stop, NULL);
3487 stop.tv_sec -= start.tv_sec;
3488 stop.tv_usec -= start.tv_usec;
3489 if (stop.tv_usec < 0) {
3490 stop.tv_sec -= 1;
3491 stop.tv_usec += 1000000;
3492 }
3493 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3494 return 1;
3495}
3496
3497static void
3498expire_handles(UNUSED_ARG(void *data))
3499{
3500 dict_iterator_t it, next;
3501 time_t expiry;
3502 struct handle_info *hi;
3503
3504 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3505 next = iter_next(it);
3506 hi = iter_data(it);
3507 if ((hi->opserv_level > 0)
3508 || hi->users
3509 || HANDLE_FLAGGED(hi, FROZEN)
3510 || HANDLE_FLAGGED(hi, NODELETE)) {
3511 continue;
3512 }
3513 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3514 if ((now - hi->lastseen) > expiry) {
3515 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3516 nickserv_unregister_handle(hi, NULL);
3517 }
3518 }
3519
3520 if (nickserv_conf.handle_expire_frequency)
3521 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3522}
3523
3524static void
3525nickserv_load_dict(const char *fname)
3526{
3527 FILE *file;
3528 char line[128];
3529 if (!(file = fopen(fname, "r"))) {
3530 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3531 return;
3532 }
3533 while (!feof(file)) {
3534 fgets(line, sizeof(line), file);
3535 if (!line[0])
3536 continue;
3537 if (line[strlen(line)-1] == '\n')
3538 line[strlen(line)-1] = 0;
3539 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3540 }
3541 fclose(file);
3542 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3543}
3544
3545static enum reclaim_action
3546reclaim_action_from_string(const char *str) {
3547 if (!str)
3548 return RECLAIM_NONE;
3549 else if (!irccasecmp(str, "warn"))
3550 return RECLAIM_WARN;
3551 else if (!irccasecmp(str, "svsnick"))
3552 return RECLAIM_SVSNICK;
3553 else if (!irccasecmp(str, "kill"))
3554 return RECLAIM_KILL;
3555 else
3556 return RECLAIM_NONE;
3557}
3558
3559static void
3560nickserv_conf_read(void)
3561{
3562 dict_t conf_node, child;
3563 const char *str;
3564 dict_iterator_t it;
3565
3566 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3567 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3568 return;
3569 }
3570 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3571 if (!str)
3572 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3573 if (nickserv_conf.valid_handle_regex_set)
3574 regfree(&nickserv_conf.valid_handle_regex);
3575 if (str) {
3576 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3577 nickserv_conf.valid_handle_regex_set = !err;
3578 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3579 } else {
3580 nickserv_conf.valid_handle_regex_set = 0;
3581 }
3582 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3583 if (nickserv_conf.valid_nick_regex_set)
3584 regfree(&nickserv_conf.valid_nick_regex);
3585 if (str) {
3586 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3587 nickserv_conf.valid_nick_regex_set = !err;
3588 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3589 } else {
3590 nickserv_conf.valid_nick_regex_set = 0;
3591 }
3592 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3593 if (!str)
3594 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3595 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3596 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3597 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3598 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3599 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3600 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3601 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3602 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3603 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3604 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3605 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3606 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3607 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3608 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3609 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3610 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3611 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3612 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3613 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3614 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3615 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3616 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3617 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3618 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3619 if (!str)
3620 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3621 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3622 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3623 if (!str)
3624 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3625 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3626 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3627 if (!str)
3628 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3629 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3630 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3631 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3632 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3633 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3634 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3635 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3636 if (!nickserv_conf.disable_nicks) {
3637 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3638 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3639 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3640 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3641 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3642 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3643 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3644 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3645 }
3646 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3647 for (it=dict_first(child); it; it=iter_next(it)) {
3648 const char *key = iter_key(it), *value;
3649 unsigned char flag;
3650 int pos;
3651
3652 if (!strncasecmp(key, "uc_", 3))
3653 flag = toupper(key[3]);
3654 else if (!strncasecmp(key, "lc_", 3))
3655 flag = tolower(key[3]);
3656 else
3657 flag = key[0];
3658
3659 if ((pos = handle_inverse_flags[flag])) {
3660 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3661 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3662 }
3663 }
3664 if (nickserv_conf.weak_password_dict)
3665 dict_delete(nickserv_conf.weak_password_dict);
3666 nickserv_conf.weak_password_dict = dict_new();
3667 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3668 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3669 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3670 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3671 if (str)
3672 nickserv_load_dict(str);
3673 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3674 if (nickserv && str)
3675 NickChange(nickserv, str, 0);
3676 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3677 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3678 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3679 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3680 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3681 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3682 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3683 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
8dc1d9ae 3684 str = database_get_data(conf_node, KEY_SYNC_LOG, RECDB_QSTRING);
3685 nickserv_conf.sync_log = str ? enabled_string(str) : 0;
d76ed9a9 3686 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3687 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3688 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3689 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3690 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3691 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3692 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3693 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3694 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3695 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3696 str = conf_get_data("server/network", RECDB_QSTRING);
3697 nickserv_conf.network_name = str ? str : "some IRC network";
3698 if (!nickserv_conf.auth_policer_params) {
3699 nickserv_conf.auth_policer_params = policer_params_new();
3700 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3701 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3702 }
3703 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3704 for (it=dict_first(child); it; it=iter_next(it))
3705 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3706}
3707
3708static void
3709nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3710 const char *msg;
3711 char newnick[NICKLEN+1];
3712
3713 assert(user);
3714 assert(ni);
3715 switch (action) {
3716 case RECLAIM_NONE:
3717 /* do nothing */
3718 break;
3719 case RECLAIM_WARN:
3720 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3721 break;
3722 case RECLAIM_SVSNICK:
3723 do {
3724 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3725 } while (GetUserH(newnick));
3726 irc_svsnick(nickserv, user, newnick);
3727 break;
3728 case RECLAIM_KILL:
3729 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3730 irc_kill(nickserv, user, msg);
3731 break;
3732 }
3733}
3734
3735static void
3736nickserv_reclaim_p(void *data) {
3737 struct userNode *user = data;
3738 struct nick_info *ni = get_nick_info(user->nick);
3739 if (ni)
3740 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3741}
3742
3743static int
3744check_user_nick(struct userNode *user) {
3745 struct nick_info *ni;
3746 user->modes &= ~FLAGS_REGNICK;
3747 if (!(ni = get_nick_info(user->nick)))
3748 return 0;
3749 if (user->handle_info == ni->owner) {
3750 user->modes |= FLAGS_REGNICK;
3751 irc_regnick(user);
3752 return 0;
3753 }
3754 if (nickserv_conf.warn_nick_owned)
3755 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3756 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3757 return 0;
3758 if (nickserv_conf.auto_reclaim_delay)
3759 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3760 else
3761 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3762 return 0;
3763}
3764
3765int
3766handle_new_user(struct userNode *user)
3767{
3768 return check_user_nick(user);
3769}
3770
3771void
3772handle_account(struct userNode *user, const char *stamp)
3773{
3774 struct handle_info *hi;
b21e2cfe 3775 char *colon;
d76ed9a9 3776
3777#ifdef WITH_PROTOCOL_P10
a9b5e3de 3778 time_t timestamp = 0;
3779
3780 colon = strchr(stamp, ':');
3781 if(colon && colon[1])
3782 {
3783 *colon = 0;
3784 timestamp = atoi(colon+1);
3785 }
d76ed9a9 3786 hi = dict_find(nickserv_handle_dict, stamp, NULL);
c0e47c86 3787 if(hi && timestamp && hi->registered != timestamp)
a9b5e3de 3788 {
3789 log_module(MAIN_LOG, LOG_WARNING, "%s using account %s but timestamp does not match %lu is not %lu.", user->nick, stamp, timestamp, hi->registered);
3790 return;
3791 }
d76ed9a9 3792#else
3793 hi = dict_find(nickserv_id_dict, stamp, NULL);
9b2d838a 3794 log_module(MAIN_LOG, LOG_WARNING, "Using non-P10 code in accounts, not tested at all!");
d76ed9a9 3795#endif
3796
3797 if (hi) {
3798 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3799 return;
3800 }
3801 set_user_handle_info(user, hi, 0);
3802 } else {
3803 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3804 }
3805}
3806
3807void
3808handle_nick_change(struct userNode *user, const char *old_nick)
3809{
3810 struct handle_info *hi;
3811
3812 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3813 dict_remove(nickserv_allow_auth_dict, old_nick);
3814 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3815 }
3816 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3817 check_user_nick(user);
3818}
3819
3820void
3821nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3822{
3823 dict_remove(nickserv_allow_auth_dict, user->nick);
3824 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3825 set_user_handle_info(user, NULL, 0);
3826}
3827
3828static struct modcmd *
3829nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3830{
3831 if (min_level > 0) {
3832 char buf[16];
3833 sprintf(buf, "%u", min_level);
3834 if (must_be_qualified) {
3835 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3836 } else {
3837 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3838 }
3839 } else if (min_level == 0) {
3840 if (must_be_qualified) {
3841 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3842 } else {
3843 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3844 }
3845 } else {
3846 if (must_be_qualified) {
3847 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3848 } else {
3849 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3850 }
3851 }
3852}
3853
3854static void
3855nickserv_db_cleanup(void)
3856{
3857 unreg_del_user_func(nickserv_remove_user);
3858 userList_clean(&curr_helpers);
3859 policer_params_delete(nickserv_conf.auth_policer_params);
3860 dict_delete(nickserv_handle_dict);
3861 dict_delete(nickserv_nick_dict);
3862 dict_delete(nickserv_opt_dict);
3863 dict_delete(nickserv_allow_auth_dict);
3864 dict_delete(nickserv_email_dict);
3865 dict_delete(nickserv_id_dict);
3866 dict_delete(nickserv_conf.weak_password_dict);
3867 free(auth_func_list);
3868 free(unreg_func_list);
3869 free(rf_list);
3870 free(allowauth_func_list);
3871 free(handle_merge_func_list);
3872 free(failpw_func_list);
3873 if (nickserv_conf.valid_handle_regex_set)
3874 regfree(&nickserv_conf.valid_handle_regex);
3875 if (nickserv_conf.valid_nick_regex_set)
3876 regfree(&nickserv_conf.valid_nick_regex);
3877}
3878
3879void
3880init_nickserv(const char *nick)
3881{
3882 unsigned int i;
3883 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3884 reg_new_user_func(handle_new_user);
3885 reg_nick_change_func(handle_nick_change);
3886 reg_del_user_func(nickserv_remove_user);
3887 reg_account_func(handle_account);
3888
3889 /* set up handle_inverse_flags */
3890 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3891 for (i=0; handle_flags[i]; i++) {
3892 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3893 flag_access_levels[i] = 0;
3894 }
3895
3896 conf_register_reload(nickserv_conf_read);
3897 nickserv_opt_dict = dict_new();
3898 nickserv_email_dict = dict_new();
3899 dict_set_free_keys(nickserv_email_dict, free);
3900 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3901
3902 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4048352e 3903/* Removed qualified_host as default requirement for AUTH, REGISTER, PASS, etc. nets
3904 * can enable it per command using modcmd. (its a shitty default IMO, and now in 1.3
3905 * a big pain to disable since its nolonger in the config file. ) -Rubin
3906 */
3907 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+loghostmask", NULL);
d76ed9a9 3908 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4048352e 3909 nickserv_define_func("REGISTER", cmd_register, -1, 0, 0);
d76ed9a9 3910 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4048352e 3911 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 0);
d76ed9a9 3912 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3913 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3914 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3915 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3916 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4048352e 3917 nickserv_define_func("PASS", cmd_pass, -1, 1, 0);
d76ed9a9 3918 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3919 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3920 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3921 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3922 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3923 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3924 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3925 if (!nickserv_conf.disable_nicks) {
3926 /* nick management commands */
3927 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3928 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3929 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3930 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3931 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3932 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3933 }
3934 if (nickserv_conf.email_enabled) {
3935 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4048352e 3936 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 0);
3937 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 0);
d76ed9a9 3938 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3939 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3940 }
3941 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3942 /* miscellaneous commands */
3943 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3944 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3945 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3946 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3947 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3948 /* other options */
3949 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3950 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3951 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3952 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3953 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3954 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3955 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3956 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3957 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3958 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3959 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3960 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3961 if (nickserv_conf.titlehost_suffix) {
3962 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
3963 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
3964 }
3965 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3966 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3967 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3968
3969 nickserv_handle_dict = dict_new();
3970 dict_set_free_keys(nickserv_handle_dict, free);
3971 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3972
3973 nickserv_id_dict = dict_new();
3974 dict_set_free_keys(nickserv_id_dict, free);
3975
3976 nickserv_nick_dict = dict_new();
1117fc5a 3977 dict_set_free_data(nickserv_nick_dict, free);
d76ed9a9 3978
3979 nickserv_allow_auth_dict = dict_new();
3980
3981 userList_init(&curr_helpers);
3982
3983 if (nick) {
a32da4c7 3984 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
3985 nickserv = AddService(nick, modes ? modes : NULL, "Nick Services", NULL);
d76ed9a9 3986 nickserv_service = service_register(nickserv);
3987 }
3988 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3989 reg_exit_func(nickserv_db_cleanup);
3990 if(nickserv_conf.handle_expire_frequency)
3991 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3992 message_register_table(msgtab);
3993}