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