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