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