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