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