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