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