]> jfr.im git - irc/evilnet/x3.git/blame - src/nickserv.c
Fix for 'password too weak' error when using SSL fingerprint auth instead of password...
[irc/evilnet/x3.git] / src / nickserv.c
CommitLineData
d76ed9a9
AS
1/* nickserv.c - Nick/authentication service
2 * Copyright 2000-2004 srvx Development Team
3 *
83ff05c3 4 * This file is part of x3.
d76ed9a9 5 *
d0f04f71 6 * x3 is free software; you can redistribute it and/or modify
d76ed9a9 7 * it under the terms of the GNU General Public License as published by
348683aa 8 * the Free Software Foundation; either version 3 of the License, or
d76ed9a9
AS
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
c8b4b87b 21#include "base64.h"
d76ed9a9
AS
22#include "chanserv.h"
23#include "conf.h"
e166c31b 24#include "config.h"
d76ed9a9
AS
25#include "global.h"
26#include "modcmd.h"
27#include "opserv.h" /* for gag_create(), opserv_bad_channel() */
28#include "saxdb.h"
1136f709 29#include "mail.h"
d76ed9a9 30#include "timeq.h"
e166c31b 31#include "x3ldap.h"
d76ed9a9 32
35305a49 33#include <tre/regex.h>
d76ed9a9 34
e166c31b 35#ifdef WITH_LDAP
8a729617 36#include <ldap.h>
e166c31b
AS
37#endif
38
d76ed9a9
AS
39#define NICKSERV_CONF_NAME "services/nickserv"
40
41#define KEY_DISABLE_NICKS "disable_nicks"
42#define KEY_DEFAULT_HOSTMASK "default_hostmask"
43#define KEY_NICKS_PER_HANDLE "nicks_per_handle"
44#define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
45#define KEY_PASSWORD_MIN_LENGTH "password_min_length"
46#define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
47#define KEY_PASSWORD_MIN_UPPER "password_min_upper"
48#define KEY_PASSWORD_MIN_LOWER "password_min_lower"
49#define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
50#define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
51#define KEY_VALID_NICK_REGEX "valid_nick_regex"
bf93ca8d 52#define KEY_VALID_FAKEHOST_REGEX "valid_fakehost_regex"
d76ed9a9
AS
53#define KEY_DB_BACKUP_FREQ "db_backup_freq"
54#define KEY_MODOPER_LEVEL "modoper_level"
55#define KEY_SET_EPITHET_LEVEL "set_epithet_level"
56#define KEY_SET_TITLE_LEVEL "set_title_level"
57#define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
7637f48f 58#define KEY_DENIED_FAKEHOST_WORDS "denied_fakehost_words"
d76ed9a9 59#define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
5a1daaab
AS
60#define KEY_AUTO_OPER "auto_oper"
61#define KEY_AUTO_ADMIN "auto_admin"
69517d70 62#define KEY_AUTO_OPER_PRIVS "auto_oper_privs"
63#define KEY_AUTO_ADMIN_PRIVS "auto_admin_privs"
d76ed9a9
AS
64#define KEY_FLAG_LEVELS "flag_levels"
65#define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
66#define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
67#define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
ae275267
MB
68#define KEY_NICK_EXPIRE_FREQ "nick_expire_freq"
69#define KEY_NICK_EXPIRE_DELAY "nick_expire_delay"
d76ed9a9
AS
70#define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
71#define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
72#define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
73#define KEY_DICT_FILE "dict_file"
74#define KEY_NICK "nick"
75#define KEY_LANGUAGE "language"
76#define KEY_AUTOGAG_ENABLED "autogag_enabled"
77#define KEY_AUTOGAG_DURATION "autogag_duration"
78#define KEY_AUTH_POLICER "auth_policer"
79#define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
80#define KEY_EMAIL_ENABLED "email_enabled"
81#define KEY_EMAIL_REQUIRED "email_required"
8dc1d9ae 82#define KEY_SYNC_LOG "sync_log"
d76ed9a9
AS
83#define KEY_COOKIE_TIMEOUT "cookie_timeout"
84#define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
85#define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
338a82b5 86#define KEY_DEFAULT_STYLE "default_style"
1136f709 87#define KEY_OUNREGISTER_INACTIVE "ounregister_inactive"
88#define KEY_OUNREGISTER_FLAGS "ounregister_flags"
d76ed9a9
AS
89
90#define KEY_ID "id"
91#define KEY_PASSWD "passwd"
92#define KEY_NICKS "nicks"
ae275267 93#define KEY_NICKS_EX "nicks_ex"
d76ed9a9 94#define KEY_MASKS "masks"
2fa83595 95#define KEY_SSLFPS "sslfps"
5177fd21 96#define KEY_IGNORES "ignores"
d76ed9a9
AS
97#define KEY_OPSERV_LEVEL "opserv_level"
98#define KEY_FLAGS "flags"
99#define KEY_REGISTER_ON "register"
100#define KEY_LAST_SEEN "lastseen"
101#define KEY_INFO "info"
102#define KEY_USERLIST_STYLE "user_style"
103#define KEY_SCREEN_WIDTH "screen_width"
104#define KEY_LAST_AUTHED_HOST "last_authed_host"
105#define KEY_LAST_QUIT_HOST "last_quit_host"
106#define KEY_EMAIL_ADDR "email_addr"
107#define KEY_COOKIE "cookie"
108#define KEY_COOKIE_DATA "data"
109#define KEY_COOKIE_TYPE "type"
110#define KEY_COOKIE_EXPIRES "expires"
111#define KEY_ACTIVATION "activation"
112#define KEY_PASSWORD_CHANGE "password change"
113#define KEY_EMAIL_CHANGE "email change"
114#define KEY_ALLOWAUTH "allowauth"
115#define KEY_EPITHET "epithet"
116#define KEY_TABLE_WIDTH "table_width"
0f6fe38c 117#define KEY_ANNOUNCEMENTS "announcements"
d76ed9a9
AS
118#define KEY_MAXLOGINS "maxlogins"
119#define KEY_FAKEHOST "fakehost"
ff3b058a 120#define KEY_NOTE_NOTE "note"
0f6fe38c 121#define KEY_NOTE_SETTER "setter"
122#define KEY_NOTE_DATE "date"
1136f709 123#define KEY_KARMA "karma"
acb142f0 124#define KEY_FORCE_HANDLES_LOWERCASE "force_handles_lowercase"
2362161a 125
e166c31b 126#define KEY_LDAP_ENABLE "ldap_enable"
39edf54a
AS
127
128#ifdef WITH_LDAP
bec5dd26 129#define KEY_LDAP_URI "ldap_uri"
e166c31b
AS
130#define KEY_LDAP_BASE "ldap_base"
131#define KEY_LDAP_DN_FMT "ldap_dn_fmt"
132#define KEY_LDAP_VERSION "ldap_version"
133#define KEY_LDAP_AUTOCREATE "ldap_autocreate"
ea02341b
AS
134#define KEY_LDAP_ADMIN_DN "ldap_admin_dn"
135#define KEY_LDAP_ADMIN_PASS "ldap_admin_pass"
136#define KEY_LDAP_FIELD_ACCOUNT "ldap_field_account"
137#define KEY_LDAP_FIELD_PASSWORD "ldap_field_password"
138#define KEY_LDAP_FIELD_EMAIL "ldap_field_email"
35ea100f 139#define KEY_LDAP_FIELD_OSLEVEL "ldap_field_oslevel"
73d4cc91 140#define KEY_LDAP_OBJECT_CLASSES "ldap_object_classes"
8a729617 141#define KEY_LDAP_OPER_GROUP_DN "ldap_oper_group_dn"
17d4a698 142#define KEY_LDAP_OPER_GROUP_LEVEL "ldap_oper_group_level"
8a729617 143#define KEY_LDAP_FIELD_GROUP_MEMBER "ldap_field_group_member"
ddcb3eb3 144#define KEY_LDAP_TIMEOUT "ldap_timeout"
39edf54a 145#endif
d76ed9a9
AS
146
147#define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
148
149#define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
886bca7c 150#define OPTION_FUNC(NAME) int NAME(UNUSED_ARG(struct svccmd *cmd), struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), int noreply, unsigned int argc, char *argv[])
d76ed9a9
AS
151typedef OPTION_FUNC(option_func_t);
152
1136f709 153DEFINE_LIST(handle_info_list, struct handle_info*)
d76ed9a9
AS
154
155#define NICKSERV_MIN_PARMS(N) do { \
156 if (argc < N) { \
157 reply("MSG_MISSING_PARAMS", argv[0]); \
180e0971 158 svccmd_send_help_brief(user, nickserv, cmd); \
d76ed9a9
AS
159 return 0; \
160 } } while (0)
161
162struct userNode *nickserv;
163struct userList curr_helpers;
164const char *handle_flags = HANDLE_FLAGS;
165
7637f48f 166extern struct string_list *autojoin_channels;
d76ed9a9
AS
167static struct module *nickserv_module;
168static struct service *nickserv_service;
169static struct log_type *NS_LOG;
39d37f27 170dict_t nickserv_handle_dict; /* contains struct handle_info* */
d76ed9a9
AS
171static dict_t nickserv_id_dict; /* contains struct handle_info* */
172static dict_t nickserv_nick_dict; /* contains struct nick_info* */
173static dict_t nickserv_opt_dict; /* contains option_func_t* */
174static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
175static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
176static char handle_inverse_flags[256];
177static unsigned int flag_access_levels[32];
178static const struct message_entry msgtab[] = {
69566a5b 179 { "NSMSG_NO_ANGLEBRACKETS", "The < and > in help indicate that that word is a required parameter, but DO NOT actually type them in messages to me." },
d76ed9a9 180 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
8a729617 181 { "NSMSG_HANDLE_TOLONG", "The account name %s is too long. Account names must be %lu characters or less."},
d76ed9a9
AS
182 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
183 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
4bffb7bd 184 { "NSMSG_PASSWORD_DICTIONARY", "Your password is too simple. You must choose a password that is not just a word or name." },
d76ed9a9 185 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
a5a8a781 186 { "NSMSG_LDAP_FAIL", "There was a problem in contacting the account server (ldap): %s. Please try again later." },
b96027ad 187 { "NSMSG_LDAP_FAIL_ADD", "There was a problem in adding account %s to ldap: %s." },
8dc17ddf
AS
188 { "NSMSG_LDAP_FAIL_SEND_EMAIL", "There was a problem in storing your email address in the account server (ldap): %s. Please try again later." },
189 { "NSMSG_LDAP_FAIL_GET_EMAIL", "There was a problem in retrieving your email address from the account server (ldap): %s. Please try again later." },
d76ed9a9
AS
190 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
191 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
192 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
193 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
194 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
195 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
196 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
197 { "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." },
198 { "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." },
199 { "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." },
200 { "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." },
201 { "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." },
202 { "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." },
203 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
204 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
34938510 205 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
d76ed9a9
AS
206 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
207 { "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." },
208 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
209 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
210 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
b1bf690d 211 { "NSMSG_EMAIL_OVERUSED", "That email address already has an account. Use RESETPASS if you forgot your password." },
d76ed9a9
AS
212 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
213 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
214 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
215 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
216 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
34938510 217 { "NSMSG_ATE_FOREIGN_COOKIE", "I ate the cookie for account $b%s$b. It may now have another." },
d76ed9a9
AS
218 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
219 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
d762299d 220 { "NSMSG_REGISTER_BAD_NICKMASK", "You must provide a hostmask, or online nick to generate one automatically. (or set a default hostmask in the config such as *@*)." },
d76ed9a9
AS
221 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
222 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
223 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
fdfc23ae 224 { "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 $N authcookie %1$s)" },
d76ed9a9
AS
225 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
226 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
227 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
228 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
229 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
0b587959 230 { "NSMSG_BAD_ADVANCED", "Advanced must be either 1 to enable it or 0 to disable it." },
d76ed9a9
AS
231 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
232 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
233 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
234 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
235 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
236 { "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 237 { "NSMSG_TITLE_INVALID", "Titles may contain only a-z, A-Z, 0-9, and '-'. Please choose another." },
a32da4c7 238 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
d76ed9a9 239 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
de9510bc
AS
240 { "NSMSG_HANDLEINFO_ON", "$bAccount Information for %s$b" },
241 { "NSMSG_HANDLEINFO_END", "----------End of Account Info-----------" },
242 { "NSMSG_HANDLEINFO_ID", "Account ID: %lu" },
243 { "NSMSG_HANDLEINFO_REGGED", "Registered on: %s" },
244 { "NSMSG_HANDLEINFO_LASTSEEN", "Last seen: %s" },
245 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", "Last seen: Right now!" },
736517fb 246 { "NSMSG_HANDLEINFO_KARMA", "Karma: %d" },
de9510bc
AS
247 { "NSMSG_HANDLEINFO_VACATION", "On vacation." },
248 { "NSMSG_HANDLEINFO_EMAIL_ADDR", "Email address: %s" },
249 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", "Cookie: There is currently an activation cookie issued for this account" },
250 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", "Cookie: There is currently a password change cookie issued for this account" },
251 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", "Cookie: There is currently an email change cookie issued for this account" },
252 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", "Cookie: There is currently an allowauth cookie issued for this account" },
253 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", "Cookie: There is currently an unknown cookie issued for this account" },
0ae1e260 254 { "NSMSG_HANDLEINFO_COOKIE_EMAIL_DATA", "Cookie: New email address: %s" },
de9510bc
AS
255 { "NSMSG_HANDLEINFO_INFOLINE", "Infoline: %s" },
256 { "NSMSG_HANDLEINFO_FLAGS", "Flags: %s" },
257 { "NSMSG_HANDLEINFO_EPITHET", "Epithet: %s" },
0f6fe38c 258 { "NSMSG_HANDLEINFO_NOTE", "Note (by %s on %s): %s " },
de9510bc 259 { "NSMSG_HANDLEINFO_FAKEHOST", "Fake host: %s" },
1136f709 260 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
261 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
de9510bc
AS
262 { "NSMSG_HANDLEINFO_LAST_HOST", "Last quit hostmask: %s" },
263 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", "Last quit hostmask: Unknown" },
264 { "NSMSG_HANDLEINFO_NICKS", "Nickname(s): %s" },
265 { "NSMSG_HANDLEINFO_MASKS", "Hostmask(s): %s" },
2fa83595 266 { "NSMSG_HANDLEINFO_SSLFPS", "SSL Fingerprints(s): %s" },
5177fd21 267 { "NSMSG_HANDLEINFO_IGNORES", "Ignore(s): %s" },
de9510bc
AS
268 { "NSMSG_HANDLEINFO_CHANNELS", "Channel(s): %s" },
269 { "NSMSG_HANDLEINFO_CURRENT", "Current nickname(s): %s" },
270 { "NSMSG_HANDLEINFO_DNR", "Do-not-register (by %s): %s" },
d76ed9a9
AS
271 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
272 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
ae275267
MB
273 { "NSMSG_NICKINFO_ON", "$bNick Information for %s$b" },
274 { "NSMSG_NICKINFO_END", "----------End of Nick Info-----------" },
275 { "NSMSG_NICKINFO_REGGED", "Registered on: %s" },
276 { "NSMSG_NICKINFO_LASTSEEN", "Last seen: %s" },
277 { "NSMSG_NICKINFO_LASTSEEN_NOW", "Last seen: Right now!" },
278 { "NSMSG_NICKINFO_OWNER", "Account: %s." },
d76ed9a9
AS
279 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
280 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
281 { "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)." },
282 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
283 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
284 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
285 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
286 { "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." },
287 { "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." },
288 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
289 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
290 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
291 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
292 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
293 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
294 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
295 { "NSMSG_PASS_SUCCESS", "Password changed." },
296 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
297 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
298 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
5177fd21 299 { "NSMSG_ADDIGNORE_ALREADY", "$b%s$b is already an ignored hostmask in your account." },
300 { "NSMSG_ADDIGNORE_SUCCESS", "Hostmask %s added." },
2fa83595
MB
301 { "NSMSG_ADDSSLFP_ALREADY", "$b%s$b is already an SSL fingerprint in your account." },
302 { "NSMSG_ADDSSLFP_SUCCESS", "SSL fingerprint %s added." },
d76ed9a9
AS
303 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
304 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
305 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
2fa83595
MB
306 { "NSMSG_DELSSLFP_SUCCESS", "SSL fingerprint %s deleted." },
307 { "NSMSG_DELSSLFP_NOT_FOUND", "Unable to find SSL fingerprint to be deleted." },
d76ed9a9
AS
308 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
309 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
310 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
311 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
312 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
313 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
314 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
315 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
1136f709 316 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
317 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
318 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
d76ed9a9
AS
319 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
320 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
321 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
322 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
323 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
324 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
325 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
326 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
327 { "NSMSG_NO_ACCESS", "Access denied." },
328 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
329 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
330 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
331 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
332 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
333 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
69566a5b 334 { "NSMSG_BAD_HANDLE", "Account $b%s$b is not allowed because it is reserved, is too long, or contains invalid characters." },
d76ed9a9
AS
335 { "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." },
336 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
337 { "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." },
338 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
339 { "NSMSG_SEARCH_MATCH", "Match: %s" },
340 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
341 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
342 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
343 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
72971fc8 344 { "NSMSG_RECLAIM_HOWTO", "To auth to account %s you must use /msg %s@%s AUTH %s <password>" },
d76ed9a9
AS
345 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
346 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
347 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
348 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
349 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
350 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
4bffb7bd
AS
351 { "NSMSG_SETTING_LIST", "$b$N account settings$b" },
352 { "NSMSG_SETTING_LIST_HEADER", "----------------------------------------" },
353 { "NSMSG_SETTING_LIST_END", "-------------End Of Settings------------" },
d76ed9a9 354 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
0f6fe38c 355 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
d76ed9a9
AS
356 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
357 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
358 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
359 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
360 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
361 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
0f6fe38c 362 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
0b587959 363 { "NSMSG_SET_AUTOHIDE", "$bAUTOHIDE: $b%s" },
d76ed9a9
AS
364 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
365 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
366 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
367 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
6118970a 368 { "NSMSG_SET_ADVANCED", "$bADVANCED: $b%s" },
d76ed9a9
AS
369 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
370 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
371 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
0f6fe38c 372 { "NSMSG_SET_NOTE", "$bNOTE: $b%s"},
d76ed9a9 373 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
6118970a 374 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
5a1daaab
AS
375
376 { "NSMSG_AUTO_OPER", "You have been auto-opered" },
377 { "NSMSG_AUTO_OPER_ADMIN", "You have been auto-admined" },
378
d76ed9a9 379 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
1c0d5243
AS
380 { "NSEMAIL_ACTIVATION_BODY",
381 "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"
382 "%2$s\n"
383 "To verify your email address and complete the account registration, log on to %1$s and type the following command:\n"
384 "/msg %3$s@%4$s COOKIE %5$s %2$s\n"
385 "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"
386 "/msg %3$s@%4$s AUTH %5$s your-password\n"
387 "(Please remember to fill in 'your-password' with the actual password you gave to us when you registered.)\n"
388 "OR configure Login-On-Connect (see http://www.afternet.org/login-on-connect for instructions) to connect pre-logged in every time.\n"
389 "\n"
390 "If you did NOT request this account, you do not need to do anything.\n"
391 "Please contact the %1$s staff if you have questions, and be sure to check our website." },
392 { "NSEMAIL_ACTIVATION_BODY_WEB",
393 "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"
394 "%2$s\n"
395 "To verify your email address and complete the account registration, visit the following URL:\n"
51db18e0 396 "http://www.afternet.org/index.php?option=com_registration&task=activate&username=%5$s&cookie=%2$s\n"
1c0d5243
AS
397 "\n"
398 "If you did NOT request this account, you do not need to do anything.\n"
399 "Please contact the %1$s staff if you have questions, and be sure to check our website." },
d76ed9a9 400 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
1c0d5243
AS
401 { "NSEMAIL_PASSWORD_CHANGE_BODY",
402 "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"
403 "To complete the password change, log on to %1$s and type the following command:\n"
404 "/msg %3$s@%4$s COOKIE %5$s %2$s\n"
405 "If you did NOT request your password to be changed, you do not need to do anything.\n"
406 "Please contact the %1$s staff if you have questions." },
407 { "NSEMAIL_PASSWORD_CHANGE_BODY_WEB",
408 "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"
409 "To complete the password change, click the following URL:\n"
51db18e0 410 "http://www.afternet.org/index.php?option=com_registration&task=passcookie&username=%5$s&cookie=%2$s\n"
1c0d5243
AS
411 "If you did NOT request your password to be changed, you do not need to do anything.\n"
412 "Please contact the %1$s staff if you have questions." },
d76ed9a9 413 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
0212adab 414#ifdef stupid_verify_old_email
d76ed9a9
AS
415 { "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." },
416 { "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 417#endif
d76ed9a9
AS
418 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
419 { "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." },
420 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
421 { "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 422 { "NSMSG_NOT_VALID_FAKEHOST_DOT", "$b%s$b is not a valid vhost. (needs at least one dot)" },
423 { "NSMSG_NOT_VALID_FAKEHOST_AT", "$b%s$b is not a valid vhost. (it can not have a '@')" },
424 { "NSMSG_DENIED_FAKEHOST_WORD", "Access denied because there's a prohibited word in $b%s$b (%s)." },
425 { "NSMSG_NOT_VALID_FAKEHOST_LEN", "$b%s$b is not a valid vhost. (can only be 63 characters)" },
426 { "NSMSG_NOT_VALID_FAKEHOST_TLD_LEN", "$b%s$b is not a valid vhost. (TLD can only be 4 characters and less)" },
bf93ca8d 427 { "NSMSG_NOT_VALID_FAKEHOST_REGEX", "$b%s$b is not allowed by the admin, consult the valid vhost regex pattern in the config file under nickserv/valid_fakehost_regex." },
d76ed9a9
AS
428 { "CHECKPASS_YES", "Yes." },
429 { "CHECKPASS_NO", "No." },
1136f709 430 { "CHECKEMAIL_NOT_SET", "No email set." },
431 { "CHECKEMAIL_YES", "Yes." },
432 { "CHECKEMAIL_NO", "No." },
08895577 433 { "NSMSG_DEFCON_NO_NEW_NICKS", "You cannot register new %s at this time, please try again soon" },
d76ed9a9
AS
434 { NULL, NULL }
435};
436
d76ed9a9
AS
437static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
438static void nickserv_reclaim_p(void *data);
1136f709 439static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
d76ed9a9 440
e166c31b 441struct nickserv_config nickserv_conf;
d76ed9a9
AS
442
443/* We have 2^32 unique account IDs to use. */
444unsigned long int highest_id = 0;
445
446static char *
447canonicalize_hostmask(char *mask)
448{
449 char *out = mask, *temp;
450 if ((temp = strchr(mask, '!'))) {
451 temp++;
452 while (*temp) *out++ = *temp++;
453 *out++ = 0;
454 }
455 return mask;
456}
457
0f6fe38c 458static struct handle_note *
459nickserv_add_note(const char *setter, time_t date, const char *text)
460{
461 struct handle_note *note = calloc(1, sizeof(*note) + strlen(text));
462
463 strncpy(note->setter, setter, sizeof(note->setter)-1);
464 note->date = date;
465 memcpy(note->note, text, strlen(text));
466 return note;
467}
468
d76ed9a9
AS
469static struct handle_info *
470register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
471{
472 struct handle_info *hi;
473
d76ed9a9 474 hi = calloc(1, sizeof(*hi));
c5b279ed 475 hi->userlist_style = nickserv_conf.default_style ? nickserv_conf.default_style : HI_DEFAULT_STYLE;
0f6fe38c 476 hi->announcements = '?';
d76ed9a9
AS
477 hi->handle = strdup(handle);
478 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
479 hi->infoline = NULL;
480 dict_insert(nickserv_handle_dict, hi->handle, hi);
481
d76ed9a9
AS
482 return hi;
483}
484
485static void
486register_nick(const char *nick, struct handle_info *owner)
487{
488 struct nick_info *ni;
489 ni = malloc(sizeof(struct nick_info));
490 safestrncpy(ni->nick, nick, sizeof(ni->nick));
ae275267
MB
491 ni->registered = now;
492 ni->lastseen = now;
d76ed9a9
AS
493 ni->owner = owner;
494 ni->next = owner->nicks;
495 owner->nicks = ni;
496 dict_insert(nickserv_nick_dict, ni->nick, ni);
497}
498
d76ed9a9
AS
499static void
500delete_nick(struct nick_info *ni)
501{
502 struct nick_info *last, *next;
503 struct userNode *user;
504 /* Check to see if we should mark a user as unregistered. */
505 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
506 user->modes &= ~FLAGS_REGNICK;
507 irc_regnick(user);
508 }
509 /* Remove ni from the nick_info linked list. */
510 if (ni == ni->owner->nicks) {
511 ni->owner->nicks = ni->next;
512 } else {
513 last = ni->owner->nicks;
514 next = last->next;
515 while (next != ni) {
516 last = next;
517 next = last->next;
518 }
519 last->next = next->next;
520 }
521 dict_remove(nickserv_nick_dict, ni->nick);
522}
523
524static unreg_func_t *unreg_func_list;
974d3831 525static void **unreg_func_list_extra;
d76ed9a9
AS
526static unsigned int unreg_func_size = 0, unreg_func_used = 0;
527
528void
974d3831 529reg_unreg_func(unreg_func_t func, void *extra)
d76ed9a9
AS
530{
531 if (unreg_func_used == unreg_func_size) {
532 if (unreg_func_size) {
533 unreg_func_size <<= 1;
534 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
974d3831 535 unreg_func_list_extra = realloc(unreg_func_list_extra, unreg_func_size*sizeof(void*));
d76ed9a9
AS
536 } else {
537 unreg_func_size = 8;
538 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
974d3831 539 unreg_func_list_extra = malloc(unreg_func_size*sizeof(void*));
d76ed9a9
AS
540 }
541 }
974d3831 542 unreg_func_list[unreg_func_used] = func;
543 unreg_func_list_extra[unreg_func_used++] = extra;
d76ed9a9
AS
544}
545
546static void
547nickserv_free_cookie(void *data)
548{
549 struct handle_cookie *cookie = data;
550 if (cookie->hi) cookie->hi->cookie = NULL;
551 if (cookie->data) free(cookie->data);
552 free(cookie);
553}
554
555static void
556free_handle_info(void *vhi)
557{
558 struct handle_info *hi = vhi;
559
d76ed9a9 560 free_string_list(hi->masks);
2fa83595 561 free_string_list(hi->sslfps);
5177fd21 562 free_string_list(hi->ignores);
d76ed9a9
AS
563 assert(!hi->users);
564
565 while (hi->nicks)
566 delete_nick(hi->nicks);
567 free(hi->infoline);
568 free(hi->epithet);
0f6fe38c 569 free(hi->note);
d76ed9a9
AS
570 free(hi->fakehost);
571 if (hi->cookie) {
572 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
573 nickserv_free_cookie(hi->cookie);
574 }
575 if (hi->email_addr) {
576 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
577 handle_info_list_remove(hil, hi);
578 if (!hil->used)
579 dict_remove(nickserv_email_dict, hi->email_addr);
580 }
581 free(hi);
582}
583
584static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
585
73d4cc91 586static int
258d1427 587nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify, struct userNode *bot)
d76ed9a9
AS
588{
589 unsigned int n;
a45e6ec7 590 struct userNode *uNode;
d76ed9a9 591
73d4cc91 592#ifdef WITH_LDAP
73d4cc91 593 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
3c607a5f
AS
594 int rc;
595 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
596 if( (rc = ldap_delete_account(hi->handle)) != LDAP_SUCCESS) {
597 if(notify) {
598 send_message(notify, bot, "NSMSG_LDAP_FAIL", ldap_err2string(rc));
599 }
600 if(rc != LDAP_NO_SUCH_OBJECT)
601 return false; /* if theres noone there to delete, its kinda ok, right ?:) */
602 }
73d4cc91
AS
603 }
604 }
605#endif
d76ed9a9 606 for (n=0; n<unreg_func_used; n++)
974d3831 607 unreg_func_list[n](notify, hi, unreg_func_list_extra[n]);
a45e6ec7 608 while (hi->users) {
609 if (nickserv_conf.sync_log) {
610 uNode = GetUserH(hi->users->nick);
611 if (uNode)
612 irc_delete(uNode);
613 }
d76ed9a9 614 set_user_handle_info(hi->users, NULL, 0);
a45e6ec7 615 }
d76ed9a9
AS
616 if (notify) {
617 if (nickserv_conf.disable_nicks)
258d1427 618 send_message(notify, bot, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
d76ed9a9 619 else
258d1427 620 send_message(notify, bot, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
d76ed9a9 621 }
8dc1d9ae 622
623 if (nickserv_conf.sync_log)
a45e6ec7 624 SyncLog("UNREGISTER %s", hi->handle);
8dc1d9ae 625
d76ed9a9 626 dict_remove(nickserv_handle_dict, hi->handle);
73d4cc91 627 return true;
d76ed9a9
AS
628}
629
630struct handle_info*
631get_handle_info(const char *handle)
632{
633 return dict_find(nickserv_handle_dict, handle, 0);
634}
635
636struct nick_info*
637get_nick_info(const char *nick)
638{
639 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
640}
641
642struct modeNode *
643find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
644{
645 unsigned int nn;
646 struct modeNode *mn;
647
648 for (nn=0; nn<channel->members.used; ++nn) {
649 mn = channel->members.list[nn];
650 if ((mn->user != except) && (mn->user->handle_info == handle))
651 return mn;
652 }
653 return NULL;
654}
655
656int
657oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
658 if (!user->handle_info) {
659 if (!quiet)
660 send_message(user, bot, "MSG_AUTHENTICATE");
661 return 0;
662 }
663
664 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
665 if (!quiet)
666 send_message(user, bot, "NSMSG_NO_ACCESS");
667 return 0;
668 }
669
670 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
671 if (!quiet)
672 send_message(user, bot, "MSG_OPER_SUSPENDED");
673 return 0;
674 }
675
676 if (user->handle_info->opserv_level < min_level) {
677 if (!quiet)
678 send_message(user, bot, "NSMSG_NO_ACCESS");
679 return 0;
680 }
681
682 return 1;
683}
684
685static int
686is_valid_handle(const char *handle)
687{
688 struct userNode *user;
689 /* cant register a juped nick/service nick as handle, to prevent confusion */
690 user = GetUserH(handle);
691 if (user && IsLocal(user))
692 return 0;
693 /* check against maximum length */
694 if (strlen(handle) > NICKSERV_HANDLE_LEN)
695 return 0;
696 /* for consistency, only allow account names that could be nicks */
697 if (!is_valid_nick(handle))
698 return 0;
699 /* disallow account names that look like bad words */
700 if (opserv_bad_channel(handle))
701 return 0;
702 /* test either regex or containing all valid chars */
703 if (nickserv_conf.valid_handle_regex_set) {
704 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
705 if (err) {
706 char buff[256];
707 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
708 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
709 }
710 return !err;
711 } else {
712 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
713 }
714}
715
716static int
717is_registerable_nick(const char *nick)
718{
dc46f772
AS
719 struct userNode *user;
720 /* cant register a juped nick/service nick as nick, to prevent confusion */
721 user = GetUserH(nick);
722 if (user && IsLocal(user))
723 return 0;
724 /* for consistency, only allow nicks names that could be nicks */
725 if (!is_valid_nick(nick))
726 return 0;
727 /* disallow nicks that look like bad words */
728 if (opserv_bad_channel(nick))
d76ed9a9
AS
729 return 0;
730 /* check length */
731 if (strlen(nick) > NICKLEN)
732 return 0;
733 /* test either regex or as valid handle */
734 if (nickserv_conf.valid_nick_regex_set) {
735 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
736 if (err) {
737 char buff[256];
738 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
739 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
740 }
741 return !err;
742 }
743 return 1;
744}
4c26ef3e 745/* this has been replaced with one in tools.c
d76ed9a9
AS
746
747static int
748is_valid_email_addr(const char *email)
749{
750 return strchr(email, '@') != NULL;
751}
752
4c26ef3e
AS
753*/
754
d76ed9a9
AS
755static const char *
756visible_email_addr(struct userNode *user, struct handle_info *hi)
757{
758 if (hi->email_addr) {
759 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
760 return hi->email_addr;
761 } else {
762 return "Set.";
763 }
764 } else {
765 return "Not set.";
766 }
767}
768
769struct handle_info *
770smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
771{
772 struct handle_info *hi;
773 struct userNode *target;
774
775 switch (*name) {
776 case '*':
777 if (!(hi = get_handle_info(++name))) {
778 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
779 return 0;
780 }
781 return hi;
782 default:
783 if (!(target = GetUserH(name))) {
784 send_message(user, service, "MSG_NICK_UNKNOWN", name);
785 return 0;
786 }
787 if (IsLocal(target)) {
788 if (IsService(target))
789 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
790 else
791 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
792 return 0;
793 }
794 if (!(hi = target->handle_info)) {
795 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
796 return 0;
797 }
798 return hi;
799 }
800}
801
802int
1136f709 803oper_outranks(struct userNode *user, struct handle_info *hi) {
d76ed9a9
AS
804 if (user->handle_info->opserv_level > hi->opserv_level)
805 return 1;
806 if (user->handle_info->opserv_level == hi->opserv_level) {
807 if ((user->handle_info->opserv_level == 1000)
808 || (user->handle_info == hi)
809 || ((user->handle_info->opserv_level == 0)
810 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
811 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
812 return 1;
813 }
814 }
1136f709 815 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
d76ed9a9
AS
816 return 0;
817}
818
e3e5ba49 819struct handle_info *
1136f709 820get_victim_oper(struct userNode *user, const char *target)
d76ed9a9
AS
821{
822 struct handle_info *hi;
823 if (!(hi = smart_get_handle_info(nickserv, user, target)))
824 return 0;
825 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
826 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
827 return 0;
828 }
1136f709 829 return oper_outranks(user, hi) ? hi : NULL;
d76ed9a9
AS
830}
831
832static int
833valid_user_for(struct userNode *user, struct handle_info *hi)
834{
835 unsigned int ii;
836
837 /* If no hostmasks on the account, allow it. */
838 if (!hi->masks->used)
839 return 1;
840 /* If any hostmask matches, allow it. */
841 for (ii=0; ii<hi->masks->used; ii++)
277ad996 842 if (user_matches_glob(user, hi->masks->list[ii], 0, 0))
d76ed9a9
AS
843 return 1;
844 /* If they are allowauthed to this account, allow it (removing the aa). */
845 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
846 dict_remove(nickserv_allow_auth_dict, user->nick);
847 return 2;
848 }
849 /* The user is not allowed to use this account. */
850 return 0;
851}
852
2fa83595
MB
853static int
854valid_user_sslfp(struct userNode *user, struct handle_info *hi)
855{
856 unsigned int ii;
857
858 if (!hi->sslfps->used)
859 return 0;
860 if (!(user->sslfp))
861 return 0;
862
863 /* If any SSL fingerprint matches, allow it. */
864 for (ii=0; ii<hi->sslfps->used; ii++)
865 if (!irccasecmp(user->sslfp, hi->sslfps->list[ii]))
866 return 1;
867
868 /* No valid SSL fingerprint found. */
869 return 0;
870}
871
d76ed9a9
AS
872static int
873is_secure_password(const char *handle, const char *pass, struct userNode *user)
874{
875 unsigned int i, len;
876 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
697f4c9a 877 int p;
878
d76ed9a9
AS
879 len = strlen(pass);
880 if (len < nickserv_conf.password_min_length) {
881 if (user)
882 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
883 return 0;
884 }
885 if (!irccasecmp(pass, handle)) {
886 if (user)
887 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
888 return 0;
889 }
697f4c9a 890 dict_find(nickserv_conf.weak_password_dict, pass, &p);
891 if (p) {
d76ed9a9
AS
892 if (user)
893 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
894 return 0;
895 }
896 for (i=0; i<len; i++) {
897 if (isdigit(pass[i]))
898 cnt_digits++;
899 if (isupper(pass[i]))
900 cnt_upper++;
901 if (islower(pass[i]))
902 cnt_lower++;
903 }
904 if ((cnt_lower < nickserv_conf.password_min_lower)
905 || (cnt_upper < nickserv_conf.password_min_upper)
906 || (cnt_digits < nickserv_conf.password_min_digits)) {
907 if (user)
908 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
909 return 0;
910 }
911 return 1;
912}
913
914static auth_func_t *auth_func_list;
81ac4787 915static void **auth_func_list_extra;
d76ed9a9
AS
916static unsigned int auth_func_size = 0, auth_func_used = 0;
917
918void
81ac4787 919reg_auth_func(auth_func_t func, void *extra)
d76ed9a9
AS
920{
921 if (auth_func_used == auth_func_size) {
922 if (auth_func_size) {
923 auth_func_size <<= 1;
924 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
81ac4787 925 auth_func_list_extra = realloc(auth_func_list_extra, auth_func_size*sizeof(void*));
d76ed9a9
AS
926 } else {
927 auth_func_size = 8;
928 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
81ac4787 929 auth_func_list_extra = malloc(auth_func_size*sizeof(void*));
d76ed9a9
AS
930 }
931 }
81ac4787 932 auth_func_list[auth_func_used] = func;
933 auth_func_list_extra[auth_func_used++] = extra;
d76ed9a9
AS
934}
935
936static handle_rename_func_t *rf_list;
3070719a 937static void **rf_list_extra;
d76ed9a9
AS
938static unsigned int rf_list_size, rf_list_used;
939
940void
3070719a 941reg_handle_rename_func(handle_rename_func_t func, void *extra)
d76ed9a9
AS
942{
943 if (rf_list_used == rf_list_size) {
944 if (rf_list_size) {
945 rf_list_size <<= 1;
946 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
3070719a 947 rf_list_extra = realloc(rf_list_extra, rf_list_size*sizeof(void*));
d76ed9a9
AS
948 } else {
949 rf_list_size = 8;
950 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
3070719a 951 rf_list_extra = malloc(rf_list_size*sizeof(void*));
d76ed9a9
AS
952 }
953 }
3070719a 954 rf_list[rf_list_used] = func;
955 rf_list_extra[rf_list_used++] = extra;
d76ed9a9
AS
956}
957
958static char *
959generate_fakehost(struct handle_info *handle)
960{
f16ad9e7 961 struct userNode *target;
d76ed9a9
AS
962 extern const char *hidden_host_suffix;
963 static char buffer[HOSTLEN+1];
f16ad9e7 964 char *data;
965 int style = 1;
d76ed9a9
AS
966
967 if (!handle->fakehost) {
f16ad9e7 968 data = conf_get_data("server/hidden_host_type", RECDB_QSTRING);
969 if (data)
970 style = atoi(data);
971
5b360094 972 if ((style == 1) || (style == 3))
f16ad9e7 973 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
974 else if (style == 2) {
975 /* Due to the way fakehost is coded theres no way i can
976 get the exact user, so for now ill just take the first
977 authed user. */
978 for (target = handle->users; target; target = target->next_authed)
979 break;
980
6118970a
MB
981 if (target)
982 snprintf(buffer, sizeof(buffer), "%s", target->crypthost);
983 else
984 strncpy(buffer, "none", sizeof(buffer));
f16ad9e7 985 }
d76ed9a9
AS
986 return buffer;
987 } else if (handle->fakehost[0] == '.') {
988 /* A leading dot indicates the stored value is actually a title. */
989 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
990 return buffer;
991 }
992 return handle->fakehost;
993}
994
995static void
996apply_fakehost(struct handle_info *handle)
997{
998 struct userNode *target;
999 char *fake;
1000
1001 if (!handle->users)
1002 return;
1003 fake = generate_fakehost(handle);
1004 for (target = handle->users; target; target = target->next_authed)
1005 assign_fakehost(target, fake, 1);
1006}
1007
3fdd6a74 1008void send_func_list(struct userNode *user)
1009{
1010 unsigned int n;
1011 struct handle_info *old_info;
1012
1013 old_info = user->handle_info;
1014
1015 for (n=0; n<auth_func_used; n++)
81ac4787 1016 auth_func_list[n](user, old_info, auth_func_list_extra[n]);
3fdd6a74 1017}
1018
d76ed9a9
AS
1019static void
1020set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
1021{
1022 unsigned int n;
1023 struct handle_info *old_info;
1024
1025 /* This can happen if somebody uses COOKIE while authed, or if
1026 * they re-auth to their current handle (which is silly, but users
1027 * are like that). */
1028 if (user->handle_info == hi)
1029 return;
1030
1031 if (user->handle_info) {
1032 struct userNode *other;
ae275267 1033 struct nick_info* ni;
d76ed9a9
AS
1034
1035 if (IsHelper(user))
1036 userList_remove(&curr_helpers, user);
1037
1038 /* remove from next_authed linked list */
1039 if (user->handle_info->users == user) {
1040 user->handle_info->users = user->next_authed;
1136f709 1041 } else if (user->handle_info->users != NULL) {
d76ed9a9
AS
1042 for (other = user->handle_info->users;
1043 other->next_authed != user;
1044 other = other->next_authed) ;
1045 other->next_authed = user->next_authed;
1136f709 1046 } else {
1047 /* No users authed to the account - can happen if they get
1048 * killed for authing. */
d76ed9a9
AS
1049 }
1050 /* if nobody left on old handle, and they're not an oper, remove !god */
1051 if (!user->handle_info->users && !user->handle_info->opserv_level)
1052 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
1053 /* record them as being last seen at this time */
1054 user->handle_info->lastseen = now;
ae275267
MB
1055 if ((ni = get_nick_info(user->nick)))
1056 ni->lastseen = now;
d76ed9a9
AS
1057 /* and record their hostmask */
1058 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
1059 }
1060 old_info = user->handle_info;
1061 user->handle_info = hi;
1062 if (hi && !hi->users && !hi->opserv_level)
1063 HANDLE_CLEAR_FLAG(hi, HELPING);
c0b25bda 1064
ffb204b6
AS
1065 /* Call auth handlers */
1066 if (!GetUserH(user->nick))
3fdd6a74 1067 user->loc = 1;
c0b25bda 1068
d76ed9a9
AS
1069 if (hi) {
1070 struct nick_info *ni;
1071
1072 HANDLE_CLEAR_FLAG(hi, FROZEN);
1073 if (nickserv_conf.warn_clone_auth) {
1074 struct userNode *other;
1075 for (other = hi->users; other; other = other->next_authed)
1076 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
1077 }
c0b25bda 1078
ffb204b6 1079 /* Add this auth to users list of current auths */
d76ed9a9
AS
1080 user->next_authed = hi->users;
1081 hi->users = user;
1082 hi->lastseen = now;
ffb204b6 1083 /* Add to helpers list */
1136f709 1084 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
d76ed9a9
AS
1085 userList_append(&curr_helpers, user);
1086
ffb204b6 1087 /* Set the fakehost */
d76ed9a9
AS
1088 if (hi->fakehost || old_info)
1089 apply_fakehost(hi);
1090
1091 if (stamp) {
7827220c 1092#ifdef WITH_PROTOCOL_P10
d76ed9a9
AS
1093 /* Stamp users with their account name. */
1094 char *id = hi->handle;
1095#else
1096 const char *id = "???";
1097#endif
ffb204b6
AS
1098 /* Mark all the nicks registered to this
1099 * account as registered nicks
1100 * - Why not just this one? -rubin */
d76ed9a9 1101 if (!nickserv_conf.disable_nicks) {
1136f709 1102 struct nick_info *ni2;
1103 for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
1104 if (!irccasecmp(user->nick, ni2->nick)) {
d76ed9a9
AS
1105 user->modes |= FLAGS_REGNICK;
1106 break;
1107 }
1108 }
1109 }
ffb204b6 1110 /* send the account to the ircd */
b21e2cfe 1111 StampUser(user, id, hi->registered);
d76ed9a9
AS
1112 }
1113
ffb204b6 1114 /* Stop trying to kick this user off their nick */
ae275267 1115 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi)) {
d76ed9a9 1116 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
ae275267
MB
1117 ni->lastseen = now;
1118 }
d76ed9a9
AS
1119 } else {
1120 /* We cannot clear the user's account ID, unfortunately. */
1121 user->next_authed = NULL;
1122 }
ffb204b6
AS
1123
1124 /* Call auth handlers */
1125 if (GetUserH(user->nick)) {
1136f709 1126 for (n=0; n<auth_func_used; n++) {
81ac4787 1127 auth_func_list[n](user, old_info, auth_func_list_extra[n]);
1136f709 1128 if (user->dead)
1129 return;
1130 }
ffb204b6 1131 }
d76ed9a9
AS
1132}
1133
1134static struct handle_info*
1135nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
1136{
1137 struct handle_info *hi;
1138 struct nick_info *ni;
da207c62 1139 char crypted[MD5_CRYPT_LENGTH] = "";
d76ed9a9
AS
1140
1141 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
bec5dd26
AS
1142 if(user)
1143 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
d76ed9a9
AS
1144 return 0;
1145 }
1146
b4065650 1147 if(strlen(handle) > 30)
d0cb2fb6 1148 {
bec5dd26 1149 if(user)
b4065650 1150 send_message(user, nickserv, "NSMSG_HANDLE_TOLONG", handle, 30);
d0cb2fb6
AS
1151 return 0;
1152 }
1153
da207c62
MB
1154 if (passwd)
1155 {
1156 if (!is_secure_password(handle, passwd, user))
1157 return 0;
d76ed9a9 1158
da207c62
MB
1159 cryptpass(passwd, crypted);
1160 }
a5a8a781 1161#ifdef WITH_LDAP
af16f748
AS
1162 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
1163 int rc;
da207c62 1164 rc = ldap_do_add(handle, (no_auth || !passwd ? NULL : crypted), NULL);
af16f748 1165 if(LDAP_SUCCESS != rc && LDAP_ALREADY_EXISTS != rc ) {
bec5dd26
AS
1166 if(user)
1167 send_message(user, nickserv, "NSMSG_LDAP_FAIL", ldap_err2string(rc));
af16f748
AS
1168 return 0;
1169 }
a5a8a781
AS
1170 }
1171#endif
d76ed9a9
AS
1172 hi = register_handle(handle, crypted, 0);
1173 hi->masks = alloc_string_list(1);
2fa83595 1174 hi->sslfps = alloc_string_list(1);
5177fd21 1175 hi->ignores = alloc_string_list(1);
d76ed9a9
AS
1176 hi->users = NULL;
1177 hi->language = lang_C;
1178 hi->registered = now;
1179 hi->lastseen = now;
1180 hi->flags = HI_DEFAULT_FLAGS;
1181 if (settee && !no_auth)
1182 set_user_handle_info(settee, hi, 1);
1183
ddcb3eb3
AS
1184 if (user != settee) {
1185 if(user)
d76ed9a9 1186 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
ddcb3eb3
AS
1187 }
1188 else if (nickserv_conf.disable_nicks) {
1189 if(user) {
d76ed9a9 1190 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
ddcb3eb3
AS
1191 }
1192 }
7b3aa3a8 1193 else if (user && (ni = dict_find(nickserv_nick_dict, user->nick, NULL))) {
ddcb3eb3 1194 if(user) {
d76ed9a9 1195 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
ddcb3eb3
AS
1196 }
1197 }
d76ed9a9 1198 else {
ddcb3eb3 1199 if(user) {
f2a8c1ea
MB
1200 if (is_registerable_nick(user->nick)) {
1201 register_nick(user->nick, hi);
1202 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1203 }
ddcb3eb3 1204 }
38bff5f0
MB
1205 else {
1206 if (is_registerable_nick(handle)) {
1207 register_nick(handle, hi);
1208 }
1209 }
d76ed9a9 1210 }
ddcb3eb3
AS
1211 if (settee && (user != settee)) {
1212 if(user) {
d76ed9a9 1213 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
ddcb3eb3
AS
1214 }
1215 }
d76ed9a9
AS
1216 return hi;
1217}
1218
1219static void
1220nickserv_bake_cookie(struct handle_cookie *cookie)
1221{
1222 cookie->hi->cookie = cookie;
1223 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1224}
1225
51db18e0
AS
1226/* Contributed by the great sneep of afternet ;) */
1227/* Since this gets used in a URL, we want to avoid stuff that confuses
1228 * email clients such as ] and ?. a-z, 0-9 only.
1229 */
1230void genpass(char *str, int len)
1231{
1232 int i = 0;
1233 char c = 0;
1234
1235 for(i = 0; i < len; i++)
1236 {
1237 do
1238 {
1239 c = (char)((float)rand() / (float)RAND_MAX * (float)256);
1240 } while(!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')));
1241 str[i] = c;
1242 }
1243 str[i] = '\0';
1244 return;
1245}
1246
d76ed9a9 1247static void
1c0d5243 1248nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data, int weblink)
d76ed9a9
AS
1249{
1250 struct handle_cookie *cookie;
1251 char subject[128], body[4096], *misc;
1252 const char *netname, *fmt;
1253 int first_time = 0;
1254
1255 if (hi->cookie) {
1256 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1257 return;
1258 }
1259
1260 cookie = calloc(1, sizeof(*cookie));
1261 cookie->hi = hi;
1262 cookie->type = type;
1263 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
51db18e0 1264
d76ed9a9 1265 cookie->expires = now + nickserv_conf.cookie_timeout;
51db18e0
AS
1266 /* Adding dedicated password gen function for more control -Rubin */
1267 genpass(cookie->cookie, 10);
1268 /*
1269 *inttobase64(cookie->cookie, rand(), 5);
1270 *inttobase64(cookie->cookie+5, rand(), 5);
1271 */
d76ed9a9
AS
1272
1273 netname = nickserv_conf.network_name;
1274 subject[0] = 0;
1275
1276 switch (cookie->type) {
1277 case ACTIVATION:
1278 hi->passwd[0] = 0; /* invalidate password */
1279 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1280 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1281 snprintf(subject, sizeof(subject), fmt, netname);
1c0d5243
AS
1282
1283 if(weblink)
1284 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY_WEB");
1285 else
1286 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1287
d76ed9a9
AS
1288 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1289 first_time = 1;
1290 break;
1291 case PASSWORD_CHANGE:
1292 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1293 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1294 snprintf(subject, sizeof(subject), fmt, netname);
1c0d5243
AS
1295 if(weblink)
1296 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY_WEB");
1297 else
1298 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
d76ed9a9 1299 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
2f7421b5 1300 first_time = 0;
d76ed9a9
AS
1301 break;
1302 case EMAIL_CHANGE:
1303 misc = hi->email_addr;
1304 hi->email_addr = cookie->data;
0212adab 1305#ifdef stupid_verify_old_email
d76ed9a9
AS
1306 if (misc) {
1307 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1308 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1309 snprintf(subject, sizeof(subject), fmt, netname);
1310 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1311 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1136f709 1312 mail_send(nickserv, hi, subject, body, 1);
d76ed9a9
AS
1313 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1314 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
2f7421b5 1315 first_time = 1;
d76ed9a9 1316 } else {
0212adab 1317#endif
d76ed9a9
AS
1318 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1319 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1320 snprintf(subject, sizeof(subject), fmt, netname);
1321 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1322 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1136f709 1323 mail_send(nickserv, hi, subject, body, 1);
d76ed9a9 1324 subject[0] = 0;
0212adab 1325#ifdef stupid_verify_old_email
d76ed9a9 1326 }
0212adab 1327#endif
d76ed9a9
AS
1328 hi->email_addr = misc;
1329 break;
1330 case ALLOWAUTH:
1331 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1332 snprintf(subject, sizeof(subject), fmt, netname);
1333 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1334 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1335 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1336 break;
1337 default:
1338 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1339 break;
1340 }
1341 if (subject[0])
1136f709 1342 mail_send(nickserv, hi, subject, body, first_time);
d76ed9a9
AS
1343 nickserv_bake_cookie(cookie);
1344}
1345
1346static void
1347nickserv_eat_cookie(struct handle_cookie *cookie)
1348{
1349 cookie->hi->cookie = NULL;
1350 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1351 nickserv_free_cookie(cookie);
1352}
1353
1354static void
1355nickserv_free_email_addr(void *data)
1356{
1357 handle_info_list_clean(data);
1358 free(data);
1359}
1360
1361static void
1362nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1363{
1364 struct handle_info_list *hil;
1365 /* Remove from old handle_info_list ... */
1366 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1367 handle_info_list_remove(hil, hi);
1368 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1369 hi->email_addr = NULL;
1370 }
1371 /* Add to the new list.. */
1372 if (new_email_addr) {
1373 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1374 hil = calloc(1, sizeof(*hil));
1375 hil->tag = strdup(new_email_addr);
1376 handle_info_list_init(hil);
1377 dict_insert(nickserv_email_dict, hil->tag, hil);
1378 }
1379 handle_info_list_append(hil, hi);
1380 hi->email_addr = hil->tag;
1381 }
1382}
1383
1384static NICKSERV_FUNC(cmd_register)
1385{
2f61d1d7 1386 irc_in_addr_t ip;
d76ed9a9
AS
1387 struct handle_info *hi;
1388 const char *email_addr, *password;
eb5d6b73 1389 char syncpass[MD5_CRYPT_LENGTH];
1c0d5243 1390 int no_auth, weblink;
d76ed9a9 1391
08895577 1392 if (checkDefCon(DEFCON_NO_NEW_NICKS) && !IsOper(user)) {
1393 reply("NSMSG_DEFCON_NO_NEW_NICKS", nickserv_conf.disable_nicks ? "accounts" : "nicknames");
1394 return 0;
1395 }
1396
d76ed9a9
AS
1397 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1398 /* Require the first handle registered to belong to someone +o. */
1399 reply("NSMSG_REQUIRE_OPER");
1400 return 0;
1401 }
1402
1403 if (user->handle_info) {
1404 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1405 return 0;
1406 }
1407
1408 if (IsRegistering(user)) {
1409 reply("NSMSG_ALREADY_REGISTERING");
1410 return 0;
1411 }
1412
1413 if (IsStamped(user)) {
1414 /* Unauthenticated users might still have been stamped
1415 previously and could therefore have a hidden host;
1416 do not allow them to register a new account. */
1417 reply("NSMSG_STAMPED_REGISTER");
1418 return 0;
1419 }
1420
1421 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1422
acb142f0
AS
1423 if(nickserv_conf.force_handles_lowercase)
1424 irc_strtolower(argv[1]);
d76ed9a9
AS
1425 if (!is_valid_handle(argv[1])) {
1426 reply("NSMSG_BAD_HANDLE", argv[1]);
1427 return 0;
1428 }
1429
1c0d5243 1430
d76ed9a9
AS
1431 if ((argc >= 4) && nickserv_conf.email_enabled) {
1432 struct handle_info_list *hil;
1433 const char *str;
1434
1435 /* Remember email address. */
1436 email_addr = argv[3];
1437
1438 /* Check that the email address looks valid.. */
4c26ef3e 1439 if (!valid_email(email_addr)) {
d76ed9a9
AS
1440 reply("NSMSG_BAD_EMAIL_ADDR");
1441 return 0;
1442 }
1443
1444 /* .. and that we are allowed to send to it. */
1136f709 1445 if ((str = mail_prohibited_address(email_addr))) {
d76ed9a9
AS
1446 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1447 return 0;
1448 }
1449
1450 /* If we do email verify, make sure we don't spam the address. */
1451 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1452 unsigned int nn;
1453 for (nn=0; nn<hil->used; nn++) {
1454 if (hil->list[nn]->cookie) {
1455 reply("NSMSG_EMAIL_UNACTIVATED");
1456 return 0;
1457 }
1458 }
1459 if (hil->used >= nickserv_conf.handles_per_email) {
1460 reply("NSMSG_EMAIL_OVERUSED");
1461 return 0;
1462 }
1463 }
1464
1465 no_auth = 1;
1466 } else {
1467 email_addr = 0;
1468 no_auth = 0;
1469 }
1470
1471 password = argv[2];
1472 argv[2] = "****";
1c0d5243
AS
1473 /* Webregister hack - send URL instead of IRC cookie
1474 * commands in email
1475 */
1476 if((argc >= 5) && !strcmp(argv[4],"WEBLINK"))
1477 weblink = 1;
1478 else
1479 weblink = 0;
d76ed9a9
AS
1480 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1481 return 0;
1482 /* Add any masks they should get. */
1483 if (nickserv_conf.default_hostmask) {
1484 string_list_append(hi->masks, strdup("*@*"));
1485 } else {
1486 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
2f61d1d7 1487 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
d76ed9a9
AS
1488 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1489 }
1490
1491 /* If they're the first to register, give them level 1000. */
1492 if (dict_size(nickserv_handle_dict) == 1) {
1493 hi->opserv_level = 1000;
1494 reply("NSMSG_ROOT_HANDLE", argv[1]);
1495 }
1496
1497 /* Set their email address. */
24e9e6c3
AS
1498 if (email_addr) {
1499#ifdef WITH_LDAP
1500 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
1501 int rc;
a3ad3ee3 1502 if((rc = ldap_do_modify(hi->handle, NULL, email_addr)) != LDAP_SUCCESS) {
24e9e6c3
AS
1503 /* Falied to update email in ldap, but still
1504 * updated it here.. what should we do? */
1505 reply("NSMSG_LDAP_FAIL_EMAIL", ldap_err2string(rc));
1506 } else {
1507 nickserv_set_email_addr(hi, email_addr);
1508 }
1509 }
1510 else {
1511 nickserv_set_email_addr(hi, email_addr);
1512 }
1513#else
d76ed9a9 1514 nickserv_set_email_addr(hi, email_addr);
24e9e6c3
AS
1515#endif
1516 }
d76ed9a9
AS
1517
1518 /* If they need to do email verification, tell them. */
1519 if (no_auth)
1c0d5243 1520 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd, weblink);
d76ed9a9
AS
1521
1522 /* Set registering flag.. */
1523 user->modes |= FLAGS_REGISTERING;
1524
8dc1d9ae 1525 if (nickserv_conf.sync_log) {
1526 cryptpass(password, syncpass);
d8d3ee73 1527 /*
a45e6ec7 1528 * An 0 is only sent if theres no email address. Thios should only happen if email functions are
d8d3ee73 1529 * disabled which they wont be for us. Email Required MUST be set on if you are using this.
1530 * -SiRVulcaN
1531 */
1532 SyncLog("REGISTER %s %s %s %s", hi->handle, syncpass, email_addr ? email_addr : "0", user->info);
8dc1d9ae 1533 }
a03d6c77
AS
1534
1535 /* this wont work if email is required .. */
ac3bdc8d 1536 process_adduser_pending(user);
eb5d6b73 1537
d76ed9a9
AS
1538 return 1;
1539}
1540
1541static NICKSERV_FUNC(cmd_oregister)
1542{
d762299d 1543 struct userNode *settee = NULL;
d76ed9a9 1544 struct handle_info *hi;
d762299d
AS
1545 char* account = NULL;
1546 char* pass = NULL;
1547 char* email = NULL;
1548 char* mask = NULL;
1549 char* nick = NULL;
1550
8cd2cc5b 1551 NICKSERV_MIN_PARMS(3);
d762299d
AS
1552
1553 account = argv[1];
1554 pass = argv[2];
acb142f0
AS
1555 if(nickserv_conf.force_handles_lowercase)
1556 irc_strtolower(account);
50c7ed1b
MB
1557 if (!is_valid_handle(argv[1])) {
1558 reply("NSMSG_BAD_HANDLE", argv[1]);
1559 return 0;
1560 }
ac5cb8c5 1561 if (nickserv_conf.email_required) {
1136f709 1562 NICKSERV_MIN_PARMS(3);
d762299d 1563 email = argv[3];
2dddcd74
MB
1564 if (argc > 4) {/* take: "acct pass email mask nick" or "acct pass email mask" or "acct pass email nick" */
1565 if (strchr(argv[4], '@'))
d762299d
AS
1566 mask = argv[4];
1567 else
1568 nick = argv[4];
1569 }
1570 if (argc >= 6) {
1571 nick = argv[5];
ac5cb8c5 1572 }
1573 }
d762299d 1574 else {
2dddcd74
MB
1575 if (argc > 3) {/* take: "account pass mask nick" or "account pass mask" or "account pass nick" */
1576 if (strchr(argv[3], '@'))
d762299d
AS
1577 mask = argv[3];
1578 else
1579 nick = argv[3];
1580 }
1581 if (argc >= 5) {
1582 nick = argv[4];
1583 }
d76ed9a9 1584 }
d762299d
AS
1585 /* If they passed a nick, look for that user.. */
1586 if (nick && !(settee = GetUserH(nick))) {
1587 reply("MSG_NICK_UNKNOWN", argv[4]);
1588 return 0;
1589 }
1590 /* If the setee is already authed, we cant add a 2nd account for them.. */
d76ed9a9
AS
1591 if (settee && settee->handle_info) {
1592 reply("NSMSG_USER_PREV_AUTH", settee->nick);
d76ed9a9
AS
1593 return 0;
1594 }
d762299d
AS
1595 /* If there is no default mask in the conf, and they didn't pass a mask,
1596 * but we did find a user by nick, generate the mask */
1597 if (!mask) {
1598 if (nickserv_conf.default_hostmask)
1599 mask = "*@*";
1600 else if (settee)
1601 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1602 else {
1603 reply("NSMSG_REGISTER_BAD_NICKMASK");
1604 return 0;
ac5cb8c5 1605 }
d76ed9a9 1606 }
d762299d
AS
1607
1608 if (!(hi = nickserv_register(user, settee, account, pass, 0))) {
1609 return 0; /* error reply handled by above */
1610 }
1611 if (email) {
24e9e6c3
AS
1612#ifdef WITH_LDAP
1613 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
1614 int rc;
a3ad3ee3 1615 if((rc = ldap_do_modify(hi->handle, NULL, email)) != LDAP_SUCCESS) {
24e9e6c3
AS
1616 /* Falied to update email in ldap, but still
1617 * updated it here.. what should we do? */
1618 reply("NSMSG_LDAP_FAIL_EMAIL", ldap_err2string(rc));
1619 } else {
1620 nickserv_set_email_addr(hi, email);
1621 }
1622 }
1623 else {
1624 nickserv_set_email_addr(hi, email);
1625 }
1626#else
d762299d 1627 nickserv_set_email_addr(hi, email);
24e9e6c3 1628#endif
d762299d
AS
1629 }
1630 if (mask) {
1631 char* mask_canonicalized = canonicalize_hostmask(strdup(mask));
1136f709 1632 if (mask_canonicalized)
1633 string_list_append(hi->masks, mask_canonicalized);
d762299d
AS
1634 }
1635
67fd744c
MB
1636 argv[2] = "****";
1637
d762299d
AS
1638 if (nickserv_conf.sync_log)
1639 SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, email ? email : "@", user->info); /* Send just @ for email if none */
d76ed9a9
AS
1640 return 1;
1641}
1642
5177fd21 1643static int
c092fcad 1644nickserv_ignore(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, char *mask)
5177fd21 1645{
1646 unsigned int i;
92fac64c 1647 struct userNode *target;
d07e3fff 1648 char *new_mask = strdup(pretty_mask(mask));
5177fd21 1649 for (i=0; i<hi->ignores->used; i++) {
1650 if (!irccasecmp(new_mask, hi->ignores->list[i])) {
c092fcad 1651 reply("NSMSG_ADDIGNORE_ALREADY", new_mask);
d07e3fff 1652 free(new_mask);
5177fd21 1653 return 0;
1654 }
1655 }
1656 string_list_append(hi->ignores, new_mask);
c092fcad 1657 reply("NSMSG_ADDIGNORE_SUCCESS", new_mask);
5177fd21 1658
92fac64c
AS
1659 for (target = hi->users; target; target = target->next_authed) {
1660 irc_silence(target, new_mask, 1);
1661 }
5177fd21 1662 return 1;
1663}
1664
1665static NICKSERV_FUNC(cmd_addignore)
1666{
1667 NICKSERV_MIN_PARMS(2);
1668
c092fcad 1669 return nickserv_ignore(cmd, user, user->handle_info, argv[1]);
5177fd21 1670}
1671
1672static NICKSERV_FUNC(cmd_oaddignore)
1673{
1674 struct handle_info *hi;
1675
1676 NICKSERV_MIN_PARMS(3);
1136f709 1677 if (!(hi = get_victim_oper(user, argv[1])))
5177fd21 1678 return 0;
668dc38e 1679
c092fcad 1680 return nickserv_ignore(cmd, user, hi, argv[2]);
5177fd21 1681}
1682
1683static int
c092fcad 1684nickserv_delignore(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, char *del_mask)
5177fd21 1685{
1686 unsigned int i;
92fac64c 1687 struct userNode *target;
d07e3fff 1688 char *pmask = strdup(pretty_mask(del_mask));
5177fd21 1689 for (i=0; i<hi->ignores->used; i++) {
d07e3fff 1690 if (!strcmp(pmask, hi->ignores->list[i]) || !strcmp(del_mask, hi->ignores->list[i])) {
5177fd21 1691 char *old_mask = hi->ignores->list[i];
1692 hi->ignores->list[i] = hi->ignores->list[--hi->ignores->used];
c092fcad 1693 reply("NSMSG_DELMASK_SUCCESS", old_mask);
92fac64c 1694 for (target = hi->users; target; target = target->next_authed) {
3f5b8801 1695 irc_silence(target, old_mask, 0);
92fac64c 1696 }
d07e3fff
AS
1697 free(old_mask);
1698 free(pmask);
5177fd21 1699 return 1;
1700 }
1701 }
c092fcad 1702 reply("NSMSG_DELMASK_NOT_FOUND");
5177fd21 1703 return 0;
1704}
1705
1706static NICKSERV_FUNC(cmd_delignore)
1707{
1708 NICKSERV_MIN_PARMS(2);
c092fcad 1709 return nickserv_delignore(cmd, user, user->handle_info, argv[1]);
5177fd21 1710}
1711
1712static NICKSERV_FUNC(cmd_odelignore)
1713{
1714 struct handle_info *hi;
1715 NICKSERV_MIN_PARMS(3);
1136f709 1716 if (!(hi = get_victim_oper(user, argv[1])))
5177fd21 1717 return 0;
c092fcad 1718 return nickserv_delignore(cmd, user, hi, argv[2]);
5177fd21 1719}
1720
d76ed9a9
AS
1721static NICKSERV_FUNC(cmd_handleinfo)
1722{
1723 char buff[400];
1724 unsigned int i, pos=0, herelen;
1725 struct userNode *target, *next_un;
1726 struct handle_info *hi;
1727 const char *nsmsg_none;
1728
1729 if (argc < 2) {
1730 if (!(hi = user->handle_info)) {
1731 reply("NSMSG_MUST_AUTH");
1732 return 0;
1733 }
1734 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1735 return 0;
1736 }
1737
1738 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1739 reply("NSMSG_HANDLEINFO_ON", hi->handle);
de9510bc 1740 reply("MSG_BAR");
d76ed9a9
AS
1741 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1742
1743 if (!hi->users) {
1744 intervalString(buff, now - hi->lastseen, user->handle_info);
1745 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1746 } else {
1747 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1748 }
1749
1750 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1751 if (HANDLE_FLAGGED(hi, FROZEN))
1752 reply("NSMSG_HANDLEINFO_VACATION");
1753
1754 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1755 struct do_not_register *dnr;
1756 if ((dnr = chanserv_is_dnr(NULL, hi)))
1757 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1136f709 1758 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
d76ed9a9 1759 return 1;
684e2f02
AS
1760 } else if (hi != user->handle_info) {
1761 reply("NSMSG_HANDLEINFO_END");
d76ed9a9 1762 return 1;
684e2f02 1763 }
d76ed9a9 1764
1136f709 1765 if (IsOper(user))
1766 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1767
d76ed9a9
AS
1768 if (nickserv_conf.email_enabled)
1769 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1770
1771 if (hi->cookie) {
1772 const char *type;
1773 switch (hi->cookie->type) {
1774 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1775 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1776 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1777 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1778 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1779 }
1780 reply(type);
0ae1e260
MB
1781 if (IsOper(user) && (hi->cookie->type == EMAIL_CHANGE))
1782 reply("NSMSG_HANDLEINFO_COOKIE_EMAIL_DATA", hi->cookie->data);
d76ed9a9
AS
1783 }
1784
1785 if (hi->flags) {
1786 unsigned long flen = 1;
1787 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1788 flags[0] = '+';
1789 for (i=0, flen=1; handle_flags[i]; i++)
1790 if (hi->flags & 1 << i)
1791 flags[flen++] = handle_flags[i];
1792 flags[flen] = 0;
1793 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1794 } else {
1795 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1796 }
1797
1798 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1799 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1800 || (hi->opserv_level > 0)) {
1801 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1802 }
1803
0f6fe38c 1804 if (IsHelping(user) || IsOper(user))
1805 {
1806 if (hi->note)
1807 {
1808 char date[64];
1809 strftime(date, 64, "%b %d %Y", localtime(&hi->note->date));
1810 reply("NSMSG_HANDLEINFO_NOTE", hi->note->setter, date, hi->note->note);
1811 }
1812 }
1813
d76ed9a9
AS
1814 if (hi->fakehost)
1815 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1816
1817 if (hi->last_quit_host[0])
1818 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1819 else
1820 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1821
1822 if (nickserv_conf.disable_nicks) {
1823 /* nicks disabled; don't show anything about registered nicks */
1824 } else if (hi->nicks) {
1825 struct nick_info *ni, *next_ni;
1826 for (ni = hi->nicks; ni; ni = next_ni) {
1827 herelen = strlen(ni->nick);
1828 if (pos + herelen + 1 > ArrayLength(buff)) {
1829 next_ni = ni;
1830 goto print_nicks_buff;
1831 } else {
1832 next_ni = ni->next;
1833 }
1834 memcpy(buff+pos, ni->nick, herelen);
1835 pos += herelen; buff[pos++] = ' ';
1836 if (!next_ni) {
1837 print_nicks_buff:
1838 buff[pos-1] = 0;
1839 reply("NSMSG_HANDLEINFO_NICKS", buff);
1840 pos = 0;
1841 }
1842 }
1843 } else {
1844 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1845 }
1846
1847 if (hi->masks->used) {
1848 for (i=0; i < hi->masks->used; i++) {
1849 herelen = strlen(hi->masks->list[i]);
1850 if (pos + herelen + 1 > ArrayLength(buff)) {
1851 i--;
1852 goto print_mask_buff;
1853 }
1854 memcpy(buff+pos, hi->masks->list[i], herelen);
1855 pos += herelen; buff[pos++] = ' ';
1856 if (i+1 == hi->masks->used) {
1857 print_mask_buff:
1858 buff[pos-1] = 0;
1859 reply("NSMSG_HANDLEINFO_MASKS", buff);
1860 pos = 0;
1861 }
1862 }
1863 } else {
1864 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1865 }
1866
2fa83595
MB
1867 if (hi->sslfps->used) {
1868 for (i=0; i < hi->sslfps->used; i++) {
1869 herelen = strlen(hi->sslfps->list[i]);
1870 if (pos + herelen + 1 > ArrayLength(buff)) {
1871 i--;
1872 goto print_sslfp_buff;
1873 }
1874 memcpy(buff+pos, hi->sslfps->list[i], herelen);
1875 pos += herelen; buff[pos++] = ' ';
1876 if (i+1 == hi->sslfps->used) {
1877 print_sslfp_buff:
1878 buff[pos-1] = 0;
1879 reply("NSMSG_HANDLEINFO_SSLFPS", buff);
1880 pos = 0;
1881 }
1882 }
1883 } else {
1884 reply("NSMSG_HANDLEINFO_SSLFPS", nsmsg_none);
1885 }
1886
5177fd21 1887 if (hi->ignores->used) {
1888 for (i=0; i < hi->ignores->used; i++) {
1889 herelen = strlen(hi->ignores->list[i]);
1890 if (pos + herelen + 1 > ArrayLength(buff)) {
1891 i--;
1892 goto print_ignore_buff;
1893 }
1894 memcpy(buff+pos, hi->ignores->list[i], herelen);
1895 pos += herelen; buff[pos++] = ' ';
1896 if (i+1 == hi->ignores->used) {
1897 print_ignore_buff:
1898 buff[pos-1] = 0;
1899 reply("NSMSG_HANDLEINFO_IGNORES", buff);
1900 pos = 0;
1901 }
1902 }
1903 } else {
1904 reply("NSMSG_HANDLEINFO_IGNORES", nsmsg_none);
1905 }
1906
d76ed9a9 1907 if (hi->channels) {
1136f709 1908 struct userData *chan, *next;
d76ed9a9
AS
1909 char *name;
1910
1136f709 1911 for (chan = hi->channels; chan; chan = next) {
1912 next = chan->u_next;
1913 name = chan->channel->channel->name;
d76ed9a9
AS
1914 herelen = strlen(name);
1915 if (pos + herelen + 7 > ArrayLength(buff)) {
1136f709 1916 next = chan;
d76ed9a9
AS
1917 goto print_chans_buff;
1918 }
1136f709 1919 if (IsUserSuspended(chan))
d76ed9a9 1920 buff[pos++] = '-';
1136f709 1921 pos += sprintf(buff+pos, "%s:%s ", user_level_name_from_level(chan->access), name);
d76ed9a9
AS
1922 if (next == NULL) {
1923 print_chans_buff:
1924 buff[pos-1] = 0;
1925 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1926 pos = 0;
1927 }
1928 }
1929 } else {
1930 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1931 }
1932
1933 for (target = hi->users; target; target = next_un) {
1934 herelen = strlen(target->nick);
1935 if (pos + herelen + 1 > ArrayLength(buff)) {
1936 next_un = target;
1937 goto print_cnick_buff;
1938 } else {
1939 next_un = target->next_authed;
1940 }
1941 memcpy(buff+pos, target->nick, herelen);
1942 pos += herelen; buff[pos++] = ' ';
1943 if (!next_un) {
1944 print_cnick_buff:
1945 buff[pos-1] = 0;
1946 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1947 pos = 0;
1948 }
1949 }
1950
de9510bc 1951 reply("NSMSG_HANDLEINFO_END");
1136f709 1952 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
d76ed9a9
AS
1953}
1954
1955static NICKSERV_FUNC(cmd_userinfo)
1956{
1957 struct userNode *target;
1958
1959 NICKSERV_MIN_PARMS(2);
1960 if (!(target = GetUserH(argv[1]))) {
1961 reply("MSG_NICK_UNKNOWN", argv[1]);
1962 return 0;
1963 }
1964 if (target->handle_info)
1965 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1966 else
1967 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1968 return 1;
1969}
1970
1971static NICKSERV_FUNC(cmd_nickinfo)
1972{
1973 struct nick_info *ni;
ae275267 1974 char buff[400];
d76ed9a9
AS
1975
1976 NICKSERV_MIN_PARMS(2);
1977 if (!(ni = get_nick_info(argv[1]))) {
1978 reply("MSG_NICK_UNKNOWN", argv[1]);
1979 return 0;
1980 }
ae275267
MB
1981
1982 reply("NSMSG_NICKINFO_ON", ni->nick);
1983 reply("MSG_BAR");
1984 reply("NSMSG_NICKINFO_REGGED", ctime(&ni->registered));
1985
1986 if (!GetUserH(ni->nick)) {
d18d752e 1987 intervalString(buff, now - ni->lastseen, user->handle_info);
ae275267
MB
1988 reply("NSMSG_NICKINFO_LASTSEEN", buff);
1989 } else {
1990 reply("NSMSG_NICKINFO_LASTSEEN_NOW");
1991 }
1992
1993 reply("NSMSG_NICKINFO_OWNER", ni->owner->handle);
1994
1995 reply("NSMSG_NICKINFO_END");
1996
d76ed9a9
AS
1997 return 1;
1998}
1999
2000static NICKSERV_FUNC(cmd_rename_handle)
2001{
2002 struct handle_info *hi;
a45e6ec7 2003 struct userNode *uNode;
57692f5e 2004 char *old_handle;
d76ed9a9
AS
2005 unsigned int nn;
2006
2007 NICKSERV_MIN_PARMS(3);
acb142f0
AS
2008 if(nickserv_conf.force_handles_lowercase)
2009 irc_strtolower(argv[2]);
1136f709 2010 if (!(hi = get_victim_oper(user, argv[1])))
d76ed9a9
AS
2011 return 0;
2012 if (!is_valid_handle(argv[2])) {
2013 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
2014 return 0;
2015 }
2016 if (get_handle_info(argv[2])) {
2017 reply("NSMSG_HANDLE_EXISTS", argv[2]);
2018 return 0;
2019 }
b4065650 2020 if(strlen(argv[2]) > 30)
d0cb2fb6 2021 {
b4065650 2022 reply("NMSG_HANDLE_TOLONG", argv[2], 30);
d0cb2fb6
AS
2023 return 0;
2024 }
73d4cc91 2025#ifdef WITH_LDAP
73d4cc91 2026 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
3c607a5f 2027 int rc;
73d4cc91
AS
2028 if( (rc = ldap_rename_account(hi->handle, argv[2])) != LDAP_SUCCESS) {
2029 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
2030 return 0;
2031 }
2032 }
2033#endif
d76ed9a9
AS
2034
2035 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
2036 hi->handle = strdup(argv[2]);
2037 dict_insert(nickserv_handle_dict, hi->handle, hi);
2038 for (nn=0; nn<rf_list_used; nn++)
3070719a 2039 rf_list[nn](hi, old_handle, rf_list_extra[nn]);
a45e6ec7 2040
2041 if (nickserv_conf.sync_log) {
2042 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2043 irc_rename(uNode, hi->handle);
2044
4ae3fc8b 2045 SyncLog("RENAME %s %s", old_handle, hi->handle);
a45e6ec7 2046 }
2047
d76ed9a9 2048 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
57692f5e 2049 global_message_args(MESSAGE_RECIPIENT_OPERS, "NSMSG_ACCOUNT_RENAMED",
2050 user->handle_info->handle, old_handle, hi->handle);
2051
d76ed9a9
AS
2052 free(old_handle);
2053 return 1;
2054}
2055
2056static failpw_func_t *failpw_func_list;
c8b793cb 2057static void **failpw_func_list_extra;
d76ed9a9
AS
2058static unsigned int failpw_func_size = 0, failpw_func_used = 0;
2059
2060void
c8b793cb 2061reg_failpw_func(failpw_func_t func, void *extra)
d76ed9a9
AS
2062{
2063 if (failpw_func_used == failpw_func_size) {
2064 if (failpw_func_size) {
2065 failpw_func_size <<= 1;
2066 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
c8b793cb 2067 failpw_func_list_extra = realloc(failpw_func_list_extra, failpw_func_size*sizeof(void*));
d76ed9a9
AS
2068 } else {
2069 failpw_func_size = 8;
2070 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
c8b793cb 2071 failpw_func_list_extra = malloc(failpw_func_size*sizeof(void*));
d76ed9a9
AS
2072 }
2073 }
c8b793cb 2074 failpw_func_list[failpw_func_used] = func;
2075 failpw_func_list_extra[failpw_func_used++] = extra;
d76ed9a9
AS
2076}
2077
779da1ca
MB
2078/*
2079 * Return hi for the first handle that has a matching SSL fingerprint.
2080 */
2081struct handle_info *find_handleinfo_by_sslfp(char *sslfp)
2082{
2083 dict_iterator_t it;
2084 struct handle_info *hi;
2085 unsigned int ii = 0;;
2086
2087 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2088 hi = iter_data(it);
2089 for (ii=0; ii<hi->sslfps->used; ii++) {
2090 if (!irccasecmp(sslfp, hi->sslfps->list[ii])) {
2091 return hi;
2092 }
2093 }
2094 }
2095
2096 return NULL;
2097}
2098
d9cd0e9d 2099/*
b21e2cfe 2100 * Return hi if the handle/pass pair matches, NULL if it doesnt.
d9cd0e9d
AS
2101 *
2102 * called by nefariouses enhanced AC login-on-connect code
2103 *
2104 */
1679a283 2105struct handle_info *loc_auth(char *sslfp, char *handle, char *password, char *userhost)
d9cd0e9d 2106{
d57dba72
MB
2107 int wildmask = 0, auth = 0;
2108 int used, maxlogins;
b21e2cfe 2109 unsigned int ii;
779da1ca 2110 struct handle_info *hi = NULL;
d9cd0e9d 2111 struct userNode *other;
4b8ccfeb 2112#ifdef WITH_LDAP
e0ee1ed8 2113 int ldap_result = LDAP_SUCCESS;
bec5dd26 2114 char *email = NULL;
4b8ccfeb 2115#endif
d57dba72 2116
779da1ca
MB
2117 if (handle != NULL)
2118 hi = dict_find(nickserv_handle_dict, handle, NULL);
2119 if (!hi && (sslfp != NULL)) {
2120 hi = find_handleinfo_by_sslfp(sslfp);
2121 if (!handle && (hi != NULL))
2122 handle = hi->handle;
2123 }
d57dba72 2124
c814d8cd 2125#ifdef WITH_LDAP
779da1ca 2126 if (nickserv_conf.ldap_enable && (password != NULL)) {
c814d8cd 2127 ldap_result = ldap_check_auth(handle, password);
d57dba72 2128 if (!hi && (ldap_result != LDAP_SUCCESS))
1f459b76 2129 return NULL;
d57dba72
MB
2130 if (ldap_result == LDAP_SUCCESS) {
2131 /* Mark auth as successful */
2132 auth++;
1f459b76 2133 }
d57dba72
MB
2134
2135 if (!hi && (ldap_result == LDAP_SUCCESS) && nickserv_conf.ldap_autocreate) {
2136 /* user not found, but authed to ldap successfully..
2137 * create the account.
2138 */
2139 char *mask;
2140 int rc;
2141
2142 /* Add a *@* mask */
2143 /* TODO if userhost is not null, build mask based on that. */
2144 if(nickserv_conf.default_hostmask)
2145 mask = "*@*";
2146 else
2147 return NULL; /* They dont have a *@* mask so they can't loc */
2148
2149 if(!(hi = nickserv_register(NULL, NULL, handle, password, 0))) {
2150 return 0; /* couldn't add the user for some reason */
bec5dd26 2151 }
d57dba72
MB
2152
2153 if((rc = ldap_get_user_info(handle, &email) != LDAP_SUCCESS))
2154 {
2155 if(nickserv_conf.email_required) {
2156 return 0;
2157 }
2158 }
2159 if(email) {
2160 nickserv_set_email_addr(hi, email);
2161 free(email);
2162 }
2163 if(mask) {
2164 char* mask_canonicalized = canonicalize_hostmask(strdup(mask));
2165 string_list_append(hi->masks, mask_canonicalized);
2166 }
2167 if(nickserv_conf.sync_log)
2168 SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, "@", handle);
2169 }
c814d8cd
AS
2170 }
2171#endif
2172
d57dba72
MB
2173 /* hi should now be a valid handle, if not return NULL */
2174 if (!hi)
b21e2cfe 2175 return NULL;
1679a283 2176
d57dba72
MB
2177#ifdef WITH_LDAP
2178 if (password && *password && !nickserv_conf.ldap_enable) {
2179#else
2180 if (password && *password) {
2181#endif
2182 if (checkpass(password, hi->passwd))
2183 auth++;
2184 }
2185
2186 if (!auth && sslfp && *sslfp && hi->sslfps->used) {
1679a283 2187 /* If any SSL fingerprint matches, allow it. */
2188 for (ii=0; ii<hi->sslfps->used; ii++) {
2189 if (!irccasecmp(sslfp, hi->sslfps->list[ii])) {
d57dba72 2190 auth++;
1679a283 2191 break;
2192 }
2193 }
1679a283 2194 }
d57dba72
MB
2195
2196 /* Auth should have succeeded by this point */
2197 if (!auth)
2198 return NULL;
1679a283 2199
b21e2cfe
AS
2200 /* We don't know the users hostname, or anything because they
2201 * havn't registered yet. So we can only allow LOC if your
2202 * account has *@* as a hostmask.
7dd05763
AS
2203 *
2204 * UPDATE: New nefarious LOC supports u@h
b21e2cfe 2205 */
7dd05763
AS
2206 if(userhost) {
2207 char *buf;
5e7fc6b5
MB
2208 char *ident = NULL;
2209 char *realhost = NULL;
2210 char *ip = NULL;
7dd05763
AS
2211 char *uh;
2212 char *ui;
5e7fc6b5
MB
2213 char *c;
2214 int bracket = 0;
7dd05763
AS
2215
2216 buf = strdup(userhost);
5e7fc6b5
MB
2217
2218 ident = buf;
2219 for (c = buf; *c; c++) {
2220 if ((realhost == NULL) && (*c == '@')) {
2221 *c++ = '\0';
2222 if (*c == '[') {
2223 bracket = 1;
2224 *c++ = '\0';
2225 }
2226 realhost = c;
2227 } else if (bracket && (ip == NULL) && (*c == ']')) {
2228 bracket = 0;
2229 *c = '\0';
2230 } else if (!bracket && (ip == NULL) && (*c == ':')) {
2231 *c++ = '\0';
2232 ip = c;
2233 break;
2234 }
2235 }
2236
2237 log_module(NS_LOG, LOG_DEBUG, "LOC: ident=%s host=%s ip=%s", ident, realhost, ip);
2238
7dd05763
AS
2239 if(!ip || !realhost || !ident) {
2240 free(buf);
2241 return NULL; /* Invalid AC request, just quit */
2242 }
2243 uh = malloc(strlen(userhost));
2244 ui = malloc(strlen(userhost));
2245 sprintf(uh, "%s@%s", ident, realhost);
2246 sprintf(ui, "%s@%s", ident, ip);
d57dba72 2247 for (ii=0; ii<hi->masks->used; ii++)
7dd05763
AS
2248 {
2249 if(match_ircglob(uh, hi->masks->list[ii])
2250 || match_ircglob(ui, hi->masks->list[ii]))
2251 {
2252 wildmask++;
2253 break;
2254 }
2255 }
2256 free(buf);
2257 free(uh);
2258 free(ui);
2259 }
2260 else {
2261
2262 for (ii=0; ii<hi->masks->used; ii++)
2263 {
2264 if (!strcmp(hi->masks->list[ii], "*@*"))
2265 {
2266 wildmask++;
2267 break;
2268 }
2269 }
d9cd0e9d 2270 }
b21e2cfe
AS
2271 if(wildmask < 1)
2272 return NULL;
2273
d9cd0e9d 2274 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
b21e2cfe 2275 return NULL;
d9cd0e9d 2276 }
c814d8cd 2277
d9cd0e9d 2278 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
d9cd0e9d
AS
2279 for (used = 0, other = hi->users; other; other = other->next_authed) {
2280 if (++used >= maxlogins) {
b21e2cfe 2281 return NULL;
d9cd0e9d
AS
2282 }
2283 }
5aef35cf 2284 /* TODO - Add LOGGING to this function so LOC's are logged.. */
b21e2cfe 2285 return hi;
d9cd0e9d
AS
2286}
2287
d76ed9a9
AS
2288static NICKSERV_FUNC(cmd_auth)
2289{
2290 int pw_arg, used, maxlogins;
8e34267c 2291 int sslfpauth = 0;
d76ed9a9
AS
2292 struct handle_info *hi;
2293 const char *passwd;
393a3e56 2294 const char *handle;
d76ed9a9 2295 struct userNode *other;
39edf54a 2296#ifdef WITH_LDAP
a5a8a781 2297 int ldap_result = LDAP_OTHER;
39edf54a
AS
2298 char *email = NULL;
2299#endif
d76ed9a9
AS
2300
2301 if (user->handle_info) {
2302 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2303 return 0;
2304 }
2305 if (IsStamped(user)) {
2306 /* Unauthenticated users might still have been stamped
2307 previously and could therefore have a hidden host;
2308 do not allow them to authenticate. */
2309 reply("NSMSG_STAMPED_AUTH");
2310 return 0;
2311 }
2312 if (argc == 3) {
393a3e56
MB
2313 passwd = argv[2];
2314 handle = argv[1];
fe08d345 2315 pw_arg = 2;
d76ed9a9 2316 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
393a3e56
MB
2317 } else if (argc == 2) {
2318 passwd = argv[1];
fe08d345 2319 pw_arg = 1;
d76ed9a9 2320 if (nickserv_conf.disable_nicks) {
393a3e56 2321 hi = get_handle_info(user->nick);
d76ed9a9
AS
2322 } else {
2323 /* try to look up their handle from their nick */
e166c31b 2324 /* TODO: handle ldap auth on nickserv style networks, too */
d76ed9a9
AS
2325 struct nick_info *ni;
2326 ni = get_nick_info(user->nick);
2327 if (!ni) {
2328 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
2329 return 0;
2330 }
2331 hi = ni->owner;
2332 }
393a3e56
MB
2333 if (hi) {
2334 handle = hi->handle;
2335 } else {
2336 handle = user->nick;
2337 }
d76ed9a9
AS
2338 } else {
2339 reply("MSG_MISSING_PARAMS", argv[0]);
b1bf690d 2340 svccmd_send_help_brief(user, nickserv, cmd);
d76ed9a9
AS
2341 return 0;
2342 }
393a3e56
MB
2343
2344#ifdef WITH_LDAP
b011d658 2345 if(strchr(handle, '<') || strchr(handle, '>')) {
393a3e56
MB
2346 reply("NSMSG_NO_ANGLEBRACKETS");
2347 return 0;
2348 }
e7fe8840 2349 if (!is_valid_handle(handle)) {
393a3e56
MB
2350 reply("NSMSG_BAD_HANDLE", handle);
2351 return 0;
2352 }
2353
2354 if(nickserv_conf.ldap_enable) {
2355 ldap_result = ldap_check_auth(handle, passwd);
2356 /* Get the users email address and update it */
2357 if(ldap_result == LDAP_SUCCESS) {
2358 int rc;
2359 if((rc = ldap_get_user_info(handle, &email) != LDAP_SUCCESS))
2360 {
2361 if(nickserv_conf.email_required) {
2362 reply("NSMSG_LDAP_FAIL_GET_EMAIL", ldap_err2string(rc));
2363 return 0;
2364 }
2365 }
2366 }
2367 else if(ldap_result != LDAP_INVALID_CREDENTIALS) {
2368 reply("NSMSG_LDAP_FAIL", ldap_err2string(ldap_result));
2369 return 0;
2370 }
2371 }
2372#endif
2373
d76ed9a9 2374 if (!hi) {
39edf54a 2375#ifdef WITH_LDAP
a5a8a781 2376 if(nickserv_conf.ldap_enable && ldap_result == LDAP_SUCCESS && nickserv_conf.ldap_autocreate) {
e166c31b
AS
2377 /* user not found, but authed to ldap successfully..
2378 * create the account.
e166c31b 2379 */
39edf54a 2380 char *mask;
b011d658 2381 if(!(hi = nickserv_register(user, user, handle, passwd, 0))) {
39edf54a
AS
2382 reply("NSMSG_UNABLE_TO_ADD");
2383 return 0; /* couldn't add the user for some reason */
2384 }
2385 /* Add a *@* mask */
2386 if(nickserv_conf.default_hostmask)
2387 mask = "*@*";
2388 else
2389 mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2390
2391 if(mask) {
2392 char* mask_canonicalized = canonicalize_hostmask(strdup(mask));
2393 string_list_append(hi->masks, mask_canonicalized);
2394 }
2395 if(email) {
2396 nickserv_set_email_addr(hi, email);
8dc06852 2397 free(email);
39edf54a
AS
2398 }
2399 if(nickserv_conf.sync_log)
2400 SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, email ? email : "@", user->info);
e166c31b 2401 }
39edf54a
AS
2402 else {
2403#endif
2404 reply("NSMSG_HANDLE_NOT_FOUND");
2405 return 0;
2406#ifdef WITH_LDAP
2407 }
2408#endif
d76ed9a9
AS
2409 }
2410 /* Responses from here on look up the language used by the handle they asked about. */
d76ed9a9
AS
2411 if (!valid_user_for(user, hi)) {
2412 if (hi->email_addr && nickserv_conf.email_enabled)
2413 send_message_type(4, user, cmd->parent->bot,
2414 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
2415 hi->handle);
2416 else
2417 send_message_type(4, user, cmd->parent->bot,
2418 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
2419 hi->handle);
2420 argv[pw_arg] = "BADMASK";
2421 return 1;
2422 }
8e34267c
MB
2423
2424 if (valid_user_sslfp(user, hi))
2425 sslfpauth = 1;
2426
e166c31b 2427#ifdef WITH_LDAP
2fa83595 2428 if(( ( nickserv_conf.ldap_enable && ldap_result == LDAP_INVALID_CREDENTIALS ) ||
8e34267c 2429 ( (!nickserv_conf.ldap_enable) && (!checkpass(passwd, hi->passwd)) ) ) && !sslfpauth) {
e166c31b 2430#else
8e34267c 2431 if (!checkpass(passwd, hi->passwd) && !sslfpauth) {
e166c31b 2432#endif
d76ed9a9
AS
2433 unsigned int n;
2434 send_message_type(4, user, cmd->parent->bot,
2435 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
2436 argv[pw_arg] = "BADPASS";
c8b793cb 2437 for (n=0; n<failpw_func_used; n++)
2438 failpw_func_list[n](user, hi, failpw_func_list_extra[n]);
d76ed9a9
AS
2439 if (nickserv_conf.autogag_enabled) {
2440 if (!user->auth_policer.params) {
2441 user->auth_policer.last_req = now;
2442 user->auth_policer.params = nickserv_conf.auth_policer_params;
2443 }
2444 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
2445 char *hostmask;
2446 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
2447 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
2448 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
2449 free(hostmask);
2450 argv[pw_arg] = "GAGGED";
2451 }
2452 }
2453 return 1;
2454 }
2455 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2456 send_message_type(4, user, cmd->parent->bot,
2457 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
2458 argv[pw_arg] = "SUSPENDED";
2459 return 1;
2460 }
2461 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2462 for (used = 0, other = hi->users; other; other = other->next_authed) {
2463 if (++used >= maxlogins) {
2464 send_message_type(4, user, cmd->parent->bot,
2465 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
2466 maxlogins);
2467 argv[pw_arg] = "MAXLOGINS";
2468 return 1;
2469 }
2470 }
2471
2472 set_user_handle_info(user, hi, 1);
2473 if (nickserv_conf.email_required && !hi->email_addr)
2474 reply("NSMSG_PLEASE_SET_EMAIL");
8e34267c 2475 if (!sslfpauth && !is_secure_password(hi->handle, passwd, NULL))
d76ed9a9 2476 reply("NSMSG_WEAK_PASSWORD");
8e34267c 2477 if (!sslfpauth && (hi->passwd[0] != '$'))
d76ed9a9 2478 cryptpass(passwd, hi->passwd);
ac3bdc8d 2479
5a1daaab
AS
2480 /* If a channel was waiting for this user to auth,
2481 * finish adding them */
ac3bdc8d 2482 process_adduser_pending(user);
5a1daaab 2483
0f6fe38c 2484 reply("NSMSG_AUTH_SUCCESS");
2485
2486
7fdb7639
AS
2487 /* Set +x if autohide is on */
2488 if(HANDLE_FLAGGED(hi, AUTOHIDE))
2489 irc_umode(user, "+x");
2490
1136f709 2491 if (!hi->masks->used) {
2492 irc_in_addr_t ip;
2493 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
2494 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
2495 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
2496 }
2497
c3915bdc 2498 /* Wipe out the pass for the logs */
d76ed9a9
AS
2499 argv[pw_arg] = "****";
2500 return 1;
2501}
2502
2503static allowauth_func_t *allowauth_func_list;
99c332f8 2504static void **allowauth_func_list_extra;
d76ed9a9
AS
2505static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
2506
2507void
99c332f8 2508reg_allowauth_func(allowauth_func_t func, void *extra)
d76ed9a9
AS
2509{
2510 if (allowauth_func_used == allowauth_func_size) {
2511 if (allowauth_func_size) {
2512 allowauth_func_size <<= 1;
2513 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
99c332f8 2514 allowauth_func_list_extra = realloc(allowauth_func_list_extra, allowauth_func_size*sizeof(void*));
d76ed9a9
AS
2515 } else {
2516 allowauth_func_size = 8;
2517 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
99c332f8 2518 allowauth_func_list_extra = malloc(allowauth_func_size*sizeof(void*));
d76ed9a9
AS
2519 }
2520 }
99c332f8 2521 allowauth_func_list[allowauth_func_used] = func;
2522 allowauth_func_list_extra[allowauth_func_used++] = extra;
d76ed9a9
AS
2523}
2524
2525static NICKSERV_FUNC(cmd_allowauth)
2526{
2527 struct userNode *target;
2528 struct handle_info *hi;
2529 unsigned int n;
2530
2531 NICKSERV_MIN_PARMS(2);
2532 if (!(target = GetUserH(argv[1]))) {
2533 reply("MSG_NICK_UNKNOWN", argv[1]);
2534 return 0;
2535 }
2536 if (target->handle_info) {
2537 reply("NSMSG_USER_PREV_AUTH", target->nick);
2538 return 0;
2539 }
2540 if (IsStamped(target)) {
2541 /* Unauthenticated users might still have been stamped
2542 previously and could therefore have a hidden host;
2543 do not allow them to authenticate to an account. */
2544 reply("NSMSG_USER_PREV_STAMP", target->nick);
2545 return 0;
2546 }
2547 if (argc == 2)
2548 hi = NULL;
2549 else if (!(hi = get_handle_info(argv[2]))) {
2550 reply("MSG_HANDLE_UNKNOWN", argv[2]);
2551 return 0;
2552 }
2553 if (hi) {
2554 if (hi->opserv_level > user->handle_info->opserv_level) {
2555 reply("MSG_USER_OUTRANKED", hi->handle);
2556 return 0;
2557 }
2558 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
2559 || (hi->opserv_level > 0))
2560 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
2561 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
2562 return 0;
2563 }
2564 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
2565 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
2566 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
2567 if (nickserv_conf.email_enabled)
2568 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
2569 } else {
2570 if (dict_remove(nickserv_allow_auth_dict, target->nick))
2571 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
2572 else
2573 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
2574 }
2575 for (n=0; n<allowauth_func_used; n++)
99c332f8 2576 allowauth_func_list[n](user, target, hi, allowauth_func_list_extra[n]);
d76ed9a9
AS
2577 return 1;
2578}
2579
2580static NICKSERV_FUNC(cmd_authcookie)
2581{
2582 struct handle_info *hi;
2583
2584 NICKSERV_MIN_PARMS(2);
2585 if (user->handle_info) {
2586 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2587 return 0;
2588 }
2589 if (IsStamped(user)) {
2590 /* Unauthenticated users might still have been stamped
2591 previously and could therefore have a hidden host;
2592 do not allow them to authenticate to an account. */
2593 reply("NSMSG_STAMPED_AUTHCOOKIE");
2594 return 0;
2595 }
2596 if (!(hi = get_handle_info(argv[1]))) {
2597 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2598 return 0;
2599 }
2600 if (!hi->email_addr) {
2601 reply("MSG_SET_EMAIL_ADDR");
2602 return 0;
2603 }
1c0d5243 2604 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL, 0);
d76ed9a9
AS
2605 return 1;
2606}
2607
2608static NICKSERV_FUNC(cmd_delcookie)
2609{
2610 struct handle_info *hi;
2611
2612 hi = user->handle_info;
2613 if (!hi->cookie) {
2614 reply("NSMSG_NO_COOKIE");
2615 return 0;
2616 }
2617 switch (hi->cookie->type) {
2618 case ACTIVATION:
2619 case EMAIL_CHANGE:
2620 reply("NSMSG_MUST_TIME_OUT");
2621 break;
2622 default:
2623 nickserv_eat_cookie(hi->cookie);
2624 reply("NSMSG_ATE_COOKIE");
2625 break;
2626 }
2627 return 1;
2628}
2629
34938510 2630static NICKSERV_FUNC(cmd_odelcookie)
2631{
2632 struct handle_info *hi;
2633
2634 NICKSERV_MIN_PARMS(2);
2635
1136f709 2636 if (!(hi = get_victim_oper(user, argv[1])))
34938510 2637 return 0;
2638
2639 if (!hi->cookie) {
2640 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
2641 return 0;
2642 }
2643
eca6aa4f 2644 switch (hi->cookie->type) {
2645 case ACTIVATION:
2646 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
29545775
MB
2647#ifdef WITH_LDAP
2648 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
2649 int rc;
2650 if((rc = ldap_do_modify(hi->handle, hi->cookie->data, NULL)) != LDAP_SUCCESS) {
2651 /* Falied to update password in ldap, but still
2652 * updated it here.. what should we do? */
2653 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
2654 return 0;
2655 }
2656 }
2657#endif
eca6aa4f 2658 if (nickserv_conf.sync_log)
2659 SyncLog("ACCOUNTACC %s", hi->handle);
2660 break;
2661 case PASSWORD_CHANGE:
eca6aa4f 2662 break;
2663 case EMAIL_CHANGE:
eca6aa4f 2664 break;
29545775
MB
2665 case ALLOWAUTH:
2666 break;
eca6aa4f 2667 default:
2668 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2669 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2670 break;
2671 }
2672
34938510 2673 nickserv_eat_cookie(hi->cookie);
2674 reply("NSMSG_ATE_FOREIGN_COOKIE", hi->handle);
2675
2676 return 1;
2677}
2678
d76ed9a9
AS
2679static NICKSERV_FUNC(cmd_resetpass)
2680{
2681 struct handle_info *hi;
2682 char crypted[MD5_CRYPT_LENGTH];
1c0d5243 2683 int weblink;
d76ed9a9
AS
2684
2685 NICKSERV_MIN_PARMS(3);
1c0d5243
AS
2686 if(argc >= 4 && !strcmp(argv[3], "WEBLINK"))
2687 weblink = 1;
2688 else
2689 weblink = 0;
d76ed9a9
AS
2690 if (user->handle_info) {
2691 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2692 return 0;
2693 }
2694 if (IsStamped(user)) {
2695 /* Unauthenticated users might still have been stamped
2696 previously and could therefore have a hidden host;
2697 do not allow them to activate an account. */
2698 reply("NSMSG_STAMPED_RESETPASS");
2699 return 0;
2700 }
2701 if (!(hi = get_handle_info(argv[1]))) {
2702 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2703 return 0;
2704 }
2705 if (!hi->email_addr) {
2706 reply("MSG_SET_EMAIL_ADDR");
2707 return 0;
2708 }
2709 cryptpass(argv[2], crypted);
2710 argv[2] = "****";
1c0d5243 2711 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted, weblink);
d76ed9a9
AS
2712 return 1;
2713}
2714
2715static NICKSERV_FUNC(cmd_cookie)
2716{
2717 struct handle_info *hi;
2718 const char *cookie;
2719
2720 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
2721 cookie = argv[1];
2722 } else {
2723 NICKSERV_MIN_PARMS(3);
2724 if (!(hi = get_handle_info(argv[1]))) {
2725 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2726 return 0;
2727 }
2728 cookie = argv[2];
2729 }
2730
2731 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2732 reply("NSMSG_HANDLE_SUSPENDED");
2733 return 0;
2734 }
2735
2736 if (!hi->cookie) {
2737 reply("NSMSG_NO_COOKIE");
2738 return 0;
2739 }
2740
2741 /* Check validity of operation before comparing cookie to
2742 * prohibit guessing by authed users. */
2743 if (user->handle_info
2744 && (hi->cookie->type != EMAIL_CHANGE)
2745 && (hi->cookie->type != PASSWORD_CHANGE)) {
2746 reply("NSMSG_CANNOT_COOKIE");
2747 return 0;
2748 }
2749
2750 if (strcmp(cookie, hi->cookie->cookie)) {
2751 reply("NSMSG_BAD_COOKIE");
2752 return 0;
2753 }
2754
2755 switch (hi->cookie->type) {
2756 case ACTIVATION:
d6ef86e3
AS
2757#ifdef WITH_LDAP
2758 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
2759 int rc;
2760 if((rc = ldap_do_modify(hi->handle, hi->cookie->data, NULL)) != LDAP_SUCCESS) {
2761 /* Falied to update email in ldap, but still
2762 * updated it here.. what should we do? */
2763 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
2764 return 0;
2765 }
2766 }
2767#endif
d76ed9a9
AS
2768 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2769 set_user_handle_info(user, hi, 1);
2770 reply("NSMSG_HANDLE_ACTIVATED");
8dc1d9ae 2771 if (nickserv_conf.sync_log)
2772 SyncLog("ACCOUNTACC %s", hi->handle);
d76ed9a9
AS
2773 break;
2774 case PASSWORD_CHANGE:
d6ef86e3
AS
2775#ifdef WITH_LDAP
2776 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
2777 int rc;
2778 if((rc = ldap_do_modify(hi->handle, hi->cookie->data, NULL)) != LDAP_SUCCESS) {
2779 /* Falied to update email in ldap, but still
2780 * updated it here.. what should we do? */
2781 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
2782 return 0;
2783 }
2784 }
2785#endif
d76ed9a9
AS
2786 set_user_handle_info(user, hi, 1);
2787 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2788 reply("NSMSG_PASSWORD_CHANGED");
8dc1d9ae 2789 if (nickserv_conf.sync_log)
2790 SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
d76ed9a9
AS
2791 break;
2792 case EMAIL_CHANGE:
24e9e6c3
AS
2793#ifdef WITH_LDAP
2794 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
2795 int rc;
a3ad3ee3 2796 if((rc = ldap_do_modify(hi->handle, NULL, hi->cookie->data)) != LDAP_SUCCESS) {
24e9e6c3
AS
2797 /* Falied to update email in ldap, but still
2798 * updated it here.. what should we do? */
8dc17ddf 2799 reply("NSMSG_LDAP_FAIL_SEND_EMAIL", ldap_err2string(rc));
24e9e6c3
AS
2800 return 0;
2801 }
2802 }
2803#endif
d8d3ee73 2804 if (!hi->email_addr && nickserv_conf.sync_log) {
2805 /*
2806 * This should only happen if an OREGISTER was sent. Require
2807 * email must be enabled! - SiRVulcaN
2808 */
ac5cb8c5 2809 if (nickserv_conf.sync_log)
2810 SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, hi->cookie->data, user->info);
d8d3ee73 2811 }
24e9e6c3 2812
d76ed9a9
AS
2813 nickserv_set_email_addr(hi, hi->cookie->data);
2814 reply("NSMSG_EMAIL_CHANGED");
8dc1d9ae 2815 if (nickserv_conf.sync_log)
2816 SyncLog("EMAILCHANGE %s %s", hi->handle, hi->cookie->data);
d76ed9a9 2817 break;
1136f709 2818 case ALLOWAUTH: {
2819 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
d76ed9a9 2820 set_user_handle_info(user, hi, 1);
1136f709 2821 nickserv_addmask(user, hi, mask);
d76ed9a9 2822 reply("NSMSG_AUTH_SUCCESS");
1136f709 2823 free(mask);
d76ed9a9 2824 break;
1136f709 2825 }
d76ed9a9
AS
2826 default:
2827 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2828 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2829 break;
2830 }
2831
2832 nickserv_eat_cookie(hi->cookie);
2833
a03d6c77
AS
2834 process_adduser_pending(user);
2835
d76ed9a9
AS
2836 return 1;
2837}
2838
2839static NICKSERV_FUNC(cmd_oregnick) {
2840 const char *nick;
2841 struct handle_info *target;
2842 struct nick_info *ni;
2843
2844 NICKSERV_MIN_PARMS(3);
2845 if (!(target = modcmd_get_handle_info(user, argv[1])))
2846 return 0;
2847 nick = argv[2];
2848 if (!is_registerable_nick(nick)) {
2849 reply("NSMSG_BAD_NICK", nick);
2850 return 0;
2851 }
2852 ni = dict_find(nickserv_nick_dict, nick, NULL);
2853 if (ni) {
2854 reply("NSMSG_NICK_EXISTS", nick);
2855 return 0;
2856 }
2857 register_nick(nick, target);
2858 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2859 return 1;
2860}
2861
2862static NICKSERV_FUNC(cmd_regnick) {
2863 unsigned n;
2864 struct nick_info *ni;
2865
2866 if (!is_registerable_nick(user->nick)) {
2867 reply("NSMSG_BAD_NICK", user->nick);
2868 return 0;
2869 }
2870 /* count their nicks, see if it's too many */
2871 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2872 if (n >= nickserv_conf.nicks_per_handle) {
2873 reply("NSMSG_TOO_MANY_NICKS");
2874 return 0;
2875 }
2876 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2877 if (ni) {
2878 reply("NSMSG_NICK_EXISTS", user->nick);
2879 return 0;
2880 }
2881 register_nick(user->nick, user->handle_info);
2882 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2883 return 1;
2884}
2885
2886static NICKSERV_FUNC(cmd_pass)
2887{
2888 struct handle_info *hi;
c814d8cd 2889 char *old_pass, *new_pass;
8dc17ddf 2890 char crypted[MD5_CRYPT_LENGTH+1];
4b8ccfeb 2891#ifdef WITH_LDAP
c814d8cd 2892 int ldap_result;
4b8ccfeb 2893#endif
d76ed9a9
AS
2894
2895 NICKSERV_MIN_PARMS(3);
2896 hi = user->handle_info;
2897 old_pass = argv[1];
2898 new_pass = argv[2];
2899 argv[2] = "****";
2900 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
c814d8cd
AS
2901
2902#ifdef WITH_LDAP
2903 if(nickserv_conf.ldap_enable) {
2904 ldap_result = ldap_check_auth(hi->handle, old_pass);
2905 if(ldap_result != LDAP_SUCCESS) {
2906 if(ldap_result == LDAP_INVALID_CREDENTIALS)
2907 reply("NSMSG_PASSWORD_INVALID");
2908 else
2909 reply("NSMSG_LDAP_FAIL", ldap_err2string(ldap_result));
2910 return 0;
2911 }
2912 }else
2913#endif
d76ed9a9
AS
2914 if (!checkpass(old_pass, hi->passwd)) {
2915 argv[1] = "BADPASS";
2916 reply("NSMSG_PASSWORD_INVALID");
2917 return 0;
2918 }
8dc17ddf 2919 cryptpass(new_pass, crypted);
c814d8cd 2920#ifdef WITH_LDAP
73d4cc91
AS
2921 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
2922 int rc;
8dc17ddf 2923 if((rc = ldap_do_modify(hi->handle, crypted, NULL)) != LDAP_SUCCESS) {
24e9e6c3 2924 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
73d4cc91
AS
2925 return 0;
2926 }
2927 }
2928#endif
8dc17ddf
AS
2929 //cryptpass(new_pass, hi->passwd);
2930 strcpy(hi->passwd, crypted);
8dc1d9ae 2931 if (nickserv_conf.sync_log)
2932 SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
d76ed9a9
AS
2933 argv[1] = "****";
2934 reply("NSMSG_PASS_SUCCESS");
2935 return 1;
2936}
2937
2938static int
1136f709 2939nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
d76ed9a9
AS
2940{
2941 unsigned int i;
2942 char *new_mask = canonicalize_hostmask(strdup(mask));
2943 for (i=0; i<hi->masks->used; i++) {
2944 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1136f709 2945 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
d76ed9a9
AS
2946 free(new_mask);
2947 return 0;
2948 }
2949 }
2950 string_list_append(hi->masks, new_mask);
1136f709 2951 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
d76ed9a9
AS
2952 return 1;
2953}
2954
2955static NICKSERV_FUNC(cmd_addmask)
2956{
2957 if (argc < 2) {
2958 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1136f709 2959 int res = nickserv_addmask(user, user->handle_info, mask);
d76ed9a9
AS
2960 free(mask);
2961 return res;
2962 } else {
2963 if (!is_gline(argv[1])) {
2964 reply("NSMSG_MASK_INVALID", argv[1]);
2965 return 0;
2966 }
1136f709 2967 return nickserv_addmask(user, user->handle_info, argv[1]);
d76ed9a9
AS
2968 }
2969}
2970
2971static NICKSERV_FUNC(cmd_oaddmask)
2972{
2973 struct handle_info *hi;
2974
2975 NICKSERV_MIN_PARMS(3);
1136f709 2976 if (!(hi = get_victim_oper(user, argv[1])))
d76ed9a9 2977 return 0;
1136f709 2978 return nickserv_addmask(user, hi, argv[2]);
d76ed9a9
AS
2979}
2980
2981static int
1136f709 2982nickserv_delmask(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
d76ed9a9
AS
2983{
2984 unsigned int i;
2985 for (i=0; i<hi->masks->used; i++) {
2986 if (!strcmp(del_mask, hi->masks->list[i])) {
2987 char *old_mask = hi->masks->list[i];
1136f709 2988 if (hi->masks->used == 1 && !force) {
c092fcad 2989 reply("NSMSG_DELMASK_NOTLAST");
d76ed9a9
AS
2990 return 0;
2991 }
2992 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
c092fcad 2993 reply("NSMSG_DELMASK_SUCCESS", old_mask);
d76ed9a9
AS
2994 free(old_mask);
2995 return 1;
2996 }
2997 }
c092fcad 2998 reply("NSMSG_DELMASK_NOT_FOUND");
d76ed9a9
AS
2999 return 0;
3000}
3001
3002static NICKSERV_FUNC(cmd_delmask)
3003{
3004 NICKSERV_MIN_PARMS(2);
1136f709 3005 return nickserv_delmask(cmd, user, user->handle_info, argv[1], 0);
d76ed9a9
AS
3006}
3007
3008static NICKSERV_FUNC(cmd_odelmask)
3009{
3010 struct handle_info *hi;
3011 NICKSERV_MIN_PARMS(3);
1136f709 3012 if (!(hi = get_victim_oper(user, argv[1])))
d76ed9a9 3013 return 0;
1136f709 3014 return nickserv_delmask(cmd, user, hi, argv[2], 1);
d76ed9a9
AS
3015}
3016
2fa83595
MB
3017static int
3018nickserv_addsslfp(struct userNode *user, struct handle_info *hi, const char *sslfp)
3019{
3020 unsigned int i;
3021 char *new_sslfp = strdup(sslfp);
3022 for (i=0; i<hi->sslfps->used; i++) {
3023 if (!irccasecmp(new_sslfp, hi->sslfps->list[i])) {
3024 send_message(user, nickserv, "NSMSG_ADDSSLFP_ALREADY", new_sslfp);
3025 free(new_sslfp);
3026 return 0;
3027 }
3028 }
3029 string_list_append(hi->sslfps, new_sslfp);
3030 send_message(user, nickserv, "NSMSG_ADDSSLFP_SUCCESS", new_sslfp);
3031 return 1;
3032}
3033
3034static NICKSERV_FUNC(cmd_addsslfp)
3035{
3036 NICKSERV_MIN_PARMS((user->sslfp ? 1 : 2));
3037 if ((argc < 2) && (user->sslfp)) {
3038 int res = nickserv_addsslfp(user, user->handle_info, user->sslfp);
3039 return res;
3040 } else {
3041 return nickserv_addsslfp(user, user->handle_info, argv[1]);
3042 }
3043}
3044
3045static NICKSERV_FUNC(cmd_oaddsslfp)
3046{
3047 struct handle_info *hi;
3048
3049 NICKSERV_MIN_PARMS(3);
3050 if (!(hi = get_victim_oper(user, argv[1])))
3051 return 0;
3052 return nickserv_addsslfp(user, hi, argv[2]);
3053}
3054
3055static int
3056nickserv_delsslfp(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, const char *del_sslfp)
3057{
3058 unsigned int i;
3059 for (i=0; i<hi->sslfps->used; i++) {
3060 if (!irccasecmp(del_sslfp, hi->sslfps->list[i])) {
3061 char *old_sslfp = hi->sslfps->list[i];
3062 hi->sslfps->list[i] = hi->sslfps->list[--hi->sslfps->used];
3063 reply("NSMSG_DELSSLFP_SUCCESS", old_sslfp);
3064 free(old_sslfp);
3065 return 1;
3066 }
3067 }
3068 reply("NSMSG_DELSSLFP_NOT_FOUND");
3069 return 0;
3070}
3071
3072static NICKSERV_FUNC(cmd_delsslfp)
3073{
3074 NICKSERV_MIN_PARMS((user->sslfp ? 1 : 2));
3075 if ((argc < 2) && (user->sslfp)) {
3076 return nickserv_delsslfp(cmd, user, user->handle_info, user->sslfp);
3077 } else {
3078 return nickserv_delsslfp(cmd, user, user->handle_info, argv[1]);
3079 }
3080}
3081
3082static NICKSERV_FUNC(cmd_odelsslfp)
3083{
3084 struct handle_info *hi;
3085 NICKSERV_MIN_PARMS(3);
3086 if (!(hi = get_victim_oper(user, argv[1])))
3087 return 0;
3088 return nickserv_delsslfp(cmd, user, hi, argv[2]);
3089}
3090
d76ed9a9
AS
3091int
3092nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
3093 unsigned int nn, add = 1, pos;
3094 unsigned long added, removed, flag;
3095
3096 for (added=removed=nn=0; str[nn]; nn++) {
3097 switch (str[nn]) {
3098 case '+': add = 1; break;
3099 case '-': add = 0; break;
3100 default:
3101 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
3102 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
3103 return 0;
3104 }
3105 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
3106 /* cheesy avoidance of looking up the flag name.. */
3107 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
3108 return 0;
3109 }
3110 flag = 1 << (pos - 1);
3111 if (add)
3112 added |= flag, removed &= ~flag;
3113 else
3114 removed |= flag, added &= ~flag;
3115 break;
3116 }
3117 }
3118 *padded = added;
3119 *premoved = removed;
3120 return 1;
3121}
3122
3123static int
3124nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
3125{
3126 unsigned long before, after, added, removed;
3127 struct userNode *uNode;
3128
3129 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
3130 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
3131 return 0;
3132 hi->flags = (hi->flags | added) & ~removed;
3133 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
3134
3135 /* Strip helping flag if they're only a support helper and not
3136 * currently in #support. */
3137 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
3138 struct channelList *schannels;
3139 unsigned int ii;
3140 schannels = chanserv_support_channels();
1136f709 3141 for (ii = 0; ii < schannels->used; ++ii)
3142 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
d76ed9a9 3143 break;
1136f709 3144 if (ii == schannels->used)
d76ed9a9
AS
3145 HANDLE_CLEAR_FLAG(hi, HELPING);
3146 }
3147
3148 if (after && !before) {
3149 /* Add user to current helper list. */
3150 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
3151 userList_append(&curr_helpers, uNode);
3152 } else if (!after && before) {
3153 /* Remove user from current helper list. */
3154 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
3155 userList_remove(&curr_helpers, uNode);
3156 }
3157
3158 return 1;
3159}
3160
3161static void
c092fcad 3162set_list(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, int override)
d76ed9a9
AS
3163{
3164 option_func_t *opt;
3165 unsigned int i;
3166 char *set_display[] = {
338a82b5 3167 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
0f6fe38c 3168 "EMAIL", "ANNOUNCEMENTS", "AUTOHIDE", "MAXLOGINS", "LANGUAGE",
0b587959 3169 "FAKEHOST", "TITLE", "EPITHET", "ADVANCED"
d76ed9a9
AS
3170 };
3171
c092fcad
AS
3172 reply("NSMSG_SETTING_LIST");
3173 reply("NSMSG_SETTING_LIST_HEADER");
d76ed9a9
AS
3174
3175 /* Do this so options are presented in a consistent order. */
3176 for (i = 0; i < ArrayLength(set_display); ++i)
3177 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
886bca7c 3178 opt(cmd, user, hi, override, 0, 0, NULL);
c092fcad 3179 reply("NSMSG_SETTING_LIST_END");
d76ed9a9
AS
3180}
3181
3182static NICKSERV_FUNC(cmd_set)
3183{
3184 struct handle_info *hi;
3185 option_func_t *opt;
3186
3187 hi = user->handle_info;
3188 if (argc < 2) {
c092fcad 3189 set_list(cmd, user, hi, 0);
d76ed9a9
AS
3190 return 1;
3191 }
3192 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
3193 reply("NSMSG_INVALID_OPTION", argv[1]);
3194 return 0;
3195 }
886bca7c 3196 return opt(cmd, user, hi, 0, 0, argc-1, argv+1);
d76ed9a9
AS
3197}
3198
3199static NICKSERV_FUNC(cmd_oset)
3200{
3201 struct handle_info *hi;
3202 option_func_t *opt;
3203
3204 NICKSERV_MIN_PARMS(2);
3205
1136f709 3206 if (!(hi = get_victim_oper(user, argv[1])))
d76ed9a9
AS
3207 return 0;
3208
3209 if (argc < 3) {
c092fcad 3210 set_list(cmd, user, hi, 0);
d76ed9a9
AS
3211 return 1;
3212 }
3213
3214 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
3215 reply("NSMSG_INVALID_OPTION", argv[2]);
3216 return 0;
3217 }
3218
886bca7c 3219 return opt(cmd, user, hi, 1, 0, argc-2, argv+2);
d76ed9a9
AS
3220}
3221
3222static OPTION_FUNC(opt_info)
3223{
3224 const char *info;
3225 if (argc > 1) {
3226 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
3227 free(hi->infoline);
3228 hi->infoline = NULL;
3229 } else {
3230 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
3231 }
3232 }
3233
3234 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
886bca7c
MB
3235 if (!(noreply))
3236 reply("NSMSG_SET_INFO", info);
d76ed9a9
AS
3237 return 1;
3238}
3239
3240static OPTION_FUNC(opt_width)
3241{
3242 if (argc > 1)
3243 hi->screen_width = strtoul(argv[1], NULL, 0);
3244
3245 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
3246 hi->screen_width = MIN_LINE_SIZE;
3247 else if (hi->screen_width > MAX_LINE_SIZE)
3248 hi->screen_width = MAX_LINE_SIZE;
3249
886bca7c
MB
3250 if (!(noreply))
3251 reply("NSMSG_SET_WIDTH", hi->screen_width);
d76ed9a9
AS
3252 return 1;
3253}
3254
3255static OPTION_FUNC(opt_tablewidth)
3256{
3257 if (argc > 1)
3258 hi->table_width = strtoul(argv[1], NULL, 0);
3259
3260 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
3261 hi->table_width = MIN_LINE_SIZE;
3262 else if (hi->screen_width > MAX_LINE_SIZE)
3263 hi->table_width = MAX_LINE_SIZE;
3264
886bca7c
MB
3265 if (!(noreply))
3266 reply("NSMSG_SET_TABLEWIDTH", hi->table_width);
d76ed9a9
AS
3267 return 1;
3268}
3269
3270static OPTION_FUNC(opt_color)
3271{
3272 if (argc > 1) {
3273 if (enabled_string(argv[1]))
3274 HANDLE_SET_FLAG(hi, MIRC_COLOR);
3275 else if (disabled_string(argv[1]))
3276 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
3277 else {
886bca7c
MB
3278 if (!(noreply))
3279 reply("MSG_INVALID_BINARY", argv[1]);
d76ed9a9
AS
3280 return 0;
3281 }
3282 }
3283
886bca7c
MB
3284 if (!(noreply))
3285 reply("NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
d76ed9a9
AS
3286 return 1;
3287}
3288
3289static OPTION_FUNC(opt_privmsg)
3290{
3291 if (argc > 1) {
3292 if (enabled_string(argv[1]))
3293 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
3294 else if (disabled_string(argv[1]))
3295 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
3296 else {
886bca7c
MB
3297 if (!(noreply))
3298 reply("MSG_INVALID_BINARY", argv[1]);
d76ed9a9
AS
3299 return 0;
3300 }
3301 }
3302
886bca7c
MB
3303 if (!(noreply))
3304 reply("NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
d76ed9a9
AS
3305 return 1;
3306}
3307
7fdb7639
AS
3308static OPTION_FUNC(opt_autohide)
3309{
3310 if (argc > 1) {
3311 if (enabled_string(argv[1]))
3312 HANDLE_SET_FLAG(hi, AUTOHIDE);
3313 else if (disabled_string(argv[1]))
3314 HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
3315 else {
886bca7c
MB
3316 if (!(noreply))
3317 reply("MSG_INVALID_BINARY", argv[1]);
7fdb7639
AS
3318 return 0;
3319 }
3320 }
3321
886bca7c
MB
3322 if (!(noreply))
3323 reply("NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
7fdb7639
AS
3324 return 1;
3325}
3326
d76ed9a9
AS
3327static OPTION_FUNC(opt_style)
3328{
3329 char *style;
3330
3331 if (argc > 1) {
338a82b5
AS
3332 if (!irccasecmp(argv[1], "Clean"))
3333 hi->userlist_style = HI_STYLE_CLEAN;
3334 else if (!irccasecmp(argv[1], "Advanced"))
3335 hi->userlist_style = HI_STYLE_ADVANCED;
d9896a83
AS
3336 else if (!irccasecmp(argv[1], "Classic"))
3337 hi->userlist_style = HI_STYLE_CLASSIC;
338a82b5
AS
3338 else /* Default to normal */
3339 hi->userlist_style = HI_STYLE_NORMAL;
3340 } /* TODO: give error if unknow style is chosen */
d76ed9a9
AS
3341
3342 switch (hi->userlist_style) {
338a82b5
AS
3343 case HI_STYLE_ADVANCED:
3344 style = "Advanced";
3345 break;
d9896a83
AS
3346 case HI_STYLE_CLASSIC:
3347 style = "Classic";
3348 break;
338a82b5
AS
3349 case HI_STYLE_CLEAN:
3350 style = "Clean";
3351 break;
3352 case HI_STYLE_NORMAL:
3353 default:
3354 style = "Normal";
d76ed9a9
AS
3355 }
3356
886bca7c
MB
3357 if (!(noreply))
3358 reply("NSMSG_SET_STYLE", style);
d76ed9a9
AS
3359 return 1;
3360}
3361
0f6fe38c 3362static OPTION_FUNC(opt_announcements)
3363{
3364 const char *choice;
3365
3366 if (argc > 1) {
3367 if (enabled_string(argv[1]))
3368 hi->announcements = 'y';
3369 else if (disabled_string(argv[1]))
3370 hi->announcements = 'n';
3371 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
3372 hi->announcements = '?';
3373 else {
886bca7c
MB
3374 if (!(noreply))
3375 reply("NSMSG_INVALID_ANNOUNCE", argv[1]);
0f6fe38c 3376 return 0;
3377 }
3378 }
3379
3380 switch (hi->announcements) {
3381 case 'y': choice = user_find_message(user, "MSG_ON"); break;
3382 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
3383 case '?': choice = "default"; break;
3384 default: choice = "unknown"; break;
3385 }
886bca7c
MB
3386 if (!(noreply))
3387 reply("NSMSG_SET_ANNOUNCEMENTS", choice);
0f6fe38c 3388 return 1;
3389}
3390
d76ed9a9
AS
3391static OPTION_FUNC(opt_password)
3392{
8dc17ddf 3393 char crypted[MD5_CRYPT_LENGTH+1];
04cb4dfc 3394 if(argc < 2) {
d6ef86e3
AS
3395 return 0;
3396 }
d76ed9a9 3397 if (!override) {
886bca7c
MB
3398 if (!(noreply))
3399 reply("NSMSG_USE_CMD_PASS");
d76ed9a9
AS
3400 return 0;
3401 }
3402
8dc17ddf 3403 cryptpass(argv[1], crypted);
73d4cc91
AS
3404#ifdef WITH_LDAP
3405 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
3406 int rc;
8dc17ddf 3407 if((rc = ldap_do_modify(hi->handle, crypted, NULL)) != LDAP_SUCCESS) {
886bca7c
MB
3408 if (!(noreply))
3409 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
3410 return 0;
73d4cc91
AS
3411 }
3412 }
3413#endif
8dc17ddf 3414 strcpy(hi->passwd, crypted);
4ae3fc8b 3415 if (nickserv_conf.sync_log)
3416 SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
3417
886bca7c
MB
3418 if (!(noreply))
3419 reply("NSMSG_SET_PASSWORD", "***");
d76ed9a9
AS
3420 return 1;
3421}
3422
3423static OPTION_FUNC(opt_flags)
3424{
3425 char flags[33];
3426 unsigned int ii, flen;
3427
3428 if (!override) {
886bca7c
MB
3429 if (!(noreply))
3430 reply("MSG_SETTING_PRIVILEGED", argv[0]);
d76ed9a9
AS
3431 return 0;
3432 }
3433
3434 if (argc > 1)
3435 nickserv_apply_flags(user, hi, argv[1]);
3436
3437 for (ii = flen = 0; handle_flags[ii]; ii++)
3438 if (hi->flags & (1 << ii))
3439 flags[flen++] = handle_flags[ii];
3440 flags[flen] = '\0';
886bca7c
MB
3441 if (!(noreply)) {
3442 if (hi->flags)
3443 reply("NSMSG_SET_FLAGS", flags);
3444 else
3445 reply("NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
3446 }
d76ed9a9
AS
3447 return 1;
3448}
3449
3450static OPTION_FUNC(opt_email)
3451{
3452 if (argc > 1) {
3453 const char *str;
4c26ef3e 3454 if (!valid_email(argv[1])) {
886bca7c
MB
3455 if (!(noreply))
3456 reply("NSMSG_BAD_EMAIL_ADDR");
d76ed9a9
AS
3457 return 0;
3458 }
1136f709 3459 if ((str = mail_prohibited_address(argv[1]))) {
886bca7c
MB
3460 if (!(noreply))
3461 reply("NSMSG_EMAIL_PROHIBITED", argv[1], str);
d76ed9a9
AS
3462 return 0;
3463 }
886bca7c
MB
3464 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1])) {
3465 if (!(noreply))
3466 reply("NSMSG_EMAIL_SAME");
3467 } else if (!override)
1c0d5243 3468 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1], 0);
d76ed9a9 3469 else {
24e9e6c3
AS
3470#ifdef WITH_LDAP
3471 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
3472 int rc;
8dc17ddf 3473 if((rc = ldap_do_modify(hi->handle, NULL, argv[1])) != LDAP_SUCCESS) {
886bca7c
MB
3474 if (!(noreply))
3475 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
24e9e6c3
AS
3476 return 0;
3477 }
3478 }
3479#endif
d76ed9a9
AS
3480 nickserv_set_email_addr(hi, argv[1]);
3481 if (hi->cookie)
3482 nickserv_eat_cookie(hi->cookie);
886bca7c
MB
3483 if (!(noreply))
3484 reply("NSMSG_SET_EMAIL", visible_email_addr(user, hi));
d76ed9a9 3485 }
886bca7c
MB
3486 } else {
3487 if (!(noreply))
3488 reply("NSMSG_SET_EMAIL", visible_email_addr(user, hi));
3489 }
d76ed9a9
AS
3490 return 1;
3491}
3492
3493static OPTION_FUNC(opt_maxlogins)
3494{
3495 unsigned char maxlogins;
3496 if (argc > 1) {
3497 maxlogins = strtoul(argv[1], NULL, 0);
3498 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
886bca7c
MB
3499 if (!(noreply))
3500 reply("NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
d76ed9a9
AS
3501 return 0;
3502 }
3503 hi->maxlogins = maxlogins;
3504 }
3505 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
886bca7c
MB
3506 if (!(noreply))
3507 reply("NSMSG_SET_MAXLOGINS", maxlogins);
d76ed9a9
AS
3508 return 1;
3509}
3510
0b587959 3511static OPTION_FUNC(opt_advanced)
3512{
3513 if (argc > 1) {
3514 if (enabled_string(argv[1]))
3515 HANDLE_SET_FLAG(hi, ADVANCED);
3516 else if (disabled_string(argv[1]))
3517 HANDLE_CLEAR_FLAG(hi, ADVANCED);
3518 else {
886bca7c
MB
3519 if (!(noreply))
3520 reply("MSG_INVALID_BINARY", argv[1]);
0b587959 3521 return 0;
3522 }
3523 }
3524
886bca7c
MB
3525 if (!(noreply))
3526 reply("NSMSG_SET_ADVANCED", user_find_message(user, HANDLE_FLAGGED(hi, ADVANCED) ? "MSG_ON" : "MSG_OFF"));
0b587959 3527 return 1;
3528}
3529
d76ed9a9
AS
3530static OPTION_FUNC(opt_language)
3531{
3532 struct language *lang;
3533 if (argc > 1) {
3534 lang = language_find(argv[1]);
886bca7c
MB
3535 if (irccasecmp(lang->name, argv[1])) {
3536 if (!(noreply))
3537 reply("NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
3538 }
d76ed9a9
AS
3539 hi->language = lang;
3540 }
886bca7c
MB
3541 if (!(noreply))
3542 reply("NSMSG_SET_LANGUAGE", hi->language->name);
d76ed9a9
AS
3543 return 1;
3544}
3545
1136f709 3546static OPTION_FUNC(opt_karma)
3547{
3548 if (!override) {
886bca7c
MB
3549 if (!(noreply))
3550 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
1136f709 3551 return 0;
3552 }
3553
3554 if (argc > 1) {
3555 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
3556 hi->karma += strtoul(argv[1] + 1, NULL, 10);
3557 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
3558 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
3559 } else {
886bca7c
MB
3560 if (!(noreply))
3561 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
1136f709 3562 }
3563 }
3564
886bca7c
MB
3565 if (!(noreply))
3566 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
1136f709 3567 return 1;
3568}
3569
8a729617 3570/* Called from opserv from cmd_access */
d76ed9a9
AS
3571int
3572oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
3573 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
3574 return 0;
3575 if ((user->handle_info->opserv_level < target->opserv_level)
3576 || ((user->handle_info->opserv_level == target->opserv_level)
3577 && (user->handle_info->opserv_level < 1000))) {
3578 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
3579 return 0;
3580 }
3581 if ((user->handle_info->opserv_level < new_level)
3582 || ((user->handle_info->opserv_level == new_level)
3583 && (user->handle_info->opserv_level < 1000))) {
3584 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
3585 return 0;
3586 }
3587 if (user->handle_info == target) {
3588 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
3589 return 0;
3590 }
8a729617 3591#ifdef WITH_LDAP
2045ae25 3592 if(nickserv_conf.ldap_enable && *(nickserv_conf.ldap_oper_group_dn) && *(nickserv_conf.ldap_admin_dn)) {
8a729617 3593 int rc;
17d4a698 3594 if(new_level > nickserv_conf.ldap_oper_group_level)
8a729617
AS
3595 rc = ldap_add2group(target->handle, nickserv_conf.ldap_oper_group_dn);
3596 else
3597 rc = ldap_delfromgroup(target->handle, nickserv_conf.ldap_oper_group_dn);
87677bd8 3598 if(rc != LDAP_SUCCESS && rc != LDAP_TYPE_OR_VALUE_EXISTS && rc != LDAP_NO_SUCH_ATTRIBUTE) {
8a729617
AS
3599 send_message(user, bot, "NSMSG_LDAP_FAIL", ldap_err2string(rc));
3600 return 0;
3601 }
3602 }
35ea100f
MB
3603 if(nickserv_conf.ldap_enable && *(nickserv_conf.ldap_field_oslevel) && *(nickserv_conf.ldap_admin_dn)) {
3604 int rc;
f3aff201 3605 if((rc = ldap_do_oslevel(target->handle, new_level, target->opserv_level)) != LDAP_SUCCESS) {
35ea100f
MB
3606 send_message(user, bot, "NSMSG_LDAP_FAIL", ldap_err2string(rc));
3607 return 0;
3608 }
3609 }
8a729617 3610#endif
d76ed9a9
AS
3611 if (target->opserv_level == new_level)
3612 return 0;
3613 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
3614 user->handle_info->handle, target->handle, new_level, target->opserv_level);
3615 target->opserv_level = new_level;
3616 return 1;
3617}
3618
3619static OPTION_FUNC(opt_level)
3620{
3621 int res;
3622
3623 if (!override) {
886bca7c
MB
3624 if (!(noreply))
3625 reply("MSG_SETTING_PRIVILEGED", argv[0]);
d76ed9a9
AS
3626 return 0;
3627 }
3628
3629 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
886bca7c
MB
3630 if (!(noreply))
3631 reply("NSMSG_SET_LEVEL", hi->opserv_level);
d76ed9a9
AS
3632 return res;
3633}
3634
3635static OPTION_FUNC(opt_epithet)
3636{
d76ed9a9 3637 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
180e0971 3638 char *epithet;
56958740 3639 struct userNode *target, *next_un;
3640
180e0971 3641 if (!override) {
886bca7c
MB
3642 if (!(noreply))
3643 reply("MSG_SETTING_PRIVILEGED", argv[0]);
180e0971
AS
3644 return 0;
3645 }
3646
3647 epithet = unsplit_string(argv+1, argc-1, NULL);
3648
d76ed9a9
AS
3649 if (hi->epithet)
3650 free(hi->epithet);
3651 if ((epithet[0] == '*') && !epithet[1])
3652 hi->epithet = NULL;
3653 else
3654 hi->epithet = strdup(epithet);
56958740 3655
3656 for (target = hi->users; target; target = next_un) {
3657 irc_swhois(nickserv, target, hi->epithet);
3658
3659 next_un = target->next_authed;
3660 }
d76ed9a9
AS
3661 }
3662
886bca7c
MB
3663 if (!(noreply)) {
3664 if (hi->epithet)
3665 reply("NSMSG_SET_EPITHET", hi->epithet);
3666 else
3667 reply("NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
3668 }
d76ed9a9
AS
3669 return 1;
3670}
3671
3672static OPTION_FUNC(opt_title)
3673{
116d100f 3674 char *title;
886bca7c 3675 const char *none = NULL;
116d100f 3676 char *sptr;
d76ed9a9 3677
d76ed9a9 3678 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
180e0971 3679 if (!override) {
886bca7c
MB
3680 if (!(noreply))
3681 reply("MSG_SETTING_PRIVILEGED", argv[0]);
180e0971
AS
3682 return 0;
3683 }
3684
d76ed9a9 3685 title = argv[1];
574bfc14
AS
3686 if(!strcmp(title, "*")) {
3687 free(hi->fakehost);
3688 hi->fakehost = NULL;
d76ed9a9 3689 }
574bfc14
AS
3690 else {
3691 if (strchr(title, '.')) {
886bca7c
MB
3692 if (!(noreply))
3693 reply("NSMSG_TITLE_INVALID");
116d100f
AS
3694 return 0;
3695 }
574bfc14
AS
3696 /* Alphanumeric titles only. */
3697 for(sptr = title; *sptr; sptr++) {
3698 if(!isalnum(*sptr) && *sptr != '-') {
886bca7c
MB
3699 if (!(noreply))
3700 reply("NSMSG_TITLE_INVALID");
574bfc14
AS
3701 return 0;
3702 }
3703 }
3704 if ((strlen(user->handle_info->handle) + strlen(title) +
3705 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
886bca7c
MB
3706 if (!(noreply))
3707 reply("NSMSG_TITLE_TRUNCATED");
574bfc14
AS
3708 return 0;
3709 }
3710 free(hi->fakehost);
d76ed9a9
AS
3711 hi->fakehost = malloc(strlen(title)+2);
3712 hi->fakehost[0] = '.';
3713 strcpy(hi->fakehost+1, title);
3714 }
3715 apply_fakehost(hi);
3716 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
3717 title = hi->fakehost + 1;
bae4525d 3718 else {
3719 /* If theres no title set then the default title will therefore
d9ffe0e7
AS
3720 be the first part of hidden_host in x3.conf, so for
3721 consistency with opt_fakehost we will print this here.
3722 This isnt actually used in P10, its just handled to keep from crashing... */
bae4525d 3723 char *hs, *hidden_suffix, *rest;
3724
3725 hs = conf_get_data("server/hidden_host", RECDB_QSTRING);
3726 hidden_suffix = strdup(hs);
3727
3728 /* Yes we do this twice */
d9ffe0e7
AS
3729 if((rest = strchr(hidden_suffix, '.')))
3730 {
3731 *rest = '\0';
3732 title = hidden_suffix;
3733 }
3734 else
3735 {
3736 /* A lame default if someone configured hidden_host to something lame */
3737 title = strdup("users");
3738 free(hidden_suffix);
3739 }
bae4525d 3740
bae4525d 3741 }
3742
d76ed9a9 3743 if (!title)
116d100f 3744 none = user_find_message(user, "MSG_NONE");
886bca7c
MB
3745 if (!(noreply))
3746 send_message(user, nickserv, "NSMSG_SET_TITLE", title ? title : none);
d76ed9a9
AS
3747 return 1;
3748}
3749
7637f48f 3750int
c092fcad 3751check_vhost(char *vhost, struct userNode *user, struct svccmd *cmd)
7637f48f 3752{
116d100f 3753 unsigned int y;
7637f48f 3754
3755 // check for a dot in the vhost
3756 if(strchr(vhost, '.') == NULL) {
c092fcad 3757 reply("NSMSG_NOT_VALID_FAKEHOST_DOT", vhost);
7637f48f 3758 return 0;
3759 }
3760
3761 // check for a @ in the vhost
3762 if(strchr(vhost, '@') != NULL) {
c092fcad 3763 reply("NSMSG_NOT_VALID_FAKEHOST_AT", vhost);
7637f48f 3764 return 0;
3765 }
3766
3767 // check for denied words, inspired by monk at paki.sex
3768 for(y = 0; y < nickserv_conf.denied_fakehost_words->used; y++) {
3769 if(strstr(vhost, nickserv_conf.denied_fakehost_words->list[y]) != NULL) {
c092fcad 3770 reply("NSMSG_DENIED_FAKEHOST_WORD", vhost, nickserv_conf.denied_fakehost_words->list[y]);
7637f48f 3771 return 0;
3772 }
3773 }
3774
3775 // check for ircu's HOSTLEN length.
3776 if(strlen(vhost) >= HOSTLEN) {
c092fcad 3777 reply("NSMSG_NOT_VALID_FAKEHOST_LEN", vhost);
7637f48f 3778 return 0;
3779 }
3780
bf93ca8d 3781 /* This can be handled by the regex now if desired.
7637f48f 3782 if (vhost[strspn(vhost, "0123456789.")]) {
3783 hostname = vhost + strlen(vhost);
3784 for (depth = 1; depth && (hostname > vhost); depth--) {
3785 hostname--;
3786 while ((hostname > vhost) && (*hostname != '.')) hostname--;
3787 }
3788
bf93ca8d 3789 if (*hostname == '.') hostname++; * advance past last dot we saw *
7637f48f 3790 if(strlen(hostname) > 4) {
c092fcad 3791 reply("NSMSG_NOT_VALID_FAKEHOST_TLD_LEN", vhost);
7637f48f 3792 return 0;
3793 }
3794 }
bf93ca8d
AS
3795 */
3796 /* test either regex or as valid handle */
3797 if (nickserv_conf.valid_fakehost_regex_set) {
3798 int err = regexec(&nickserv_conf.valid_fakehost_regex, vhost, 0, 0, 0);
3799 if (err) {
3800 char buff[256];
3801 buff[regerror(err, &nickserv_conf.valid_fakehost_regex, buff, sizeof(buff))] = 0;
3802 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
3803 }
3804 if(err == REG_NOMATCH) {
3805 reply("NSMSG_NOT_VALID_FAKEHOST_REGEX", vhost);
3806 return 0;
3807 }
3808 }
3809
7637f48f 3810
3811 return 1;
3812}
3813
d76ed9a9
AS
3814static OPTION_FUNC(opt_fakehost)
3815{
3816 const char *fake;
3817
d76ed9a9 3818 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
180e0971 3819 if (!override) {
886bca7c
MB
3820 if (!(noreply))
3821 reply("MSG_SETTING_PRIVILEGED", argv[0]);
180e0971
AS
3822 return 0;
3823 }
3824
d76ed9a9
AS
3825 fake = argv[1];
3826 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
886bca7c
MB
3827 if (!(noreply))
3828 reply("NSMSG_FAKEHOST_INVALID", HOSTLEN);
d76ed9a9
AS
3829 return 0;
3830 }
7637f48f 3831 if (!strcmp(fake, "*")) {
bf93ca8d
AS
3832 if(hi->fakehost) {
3833 free(hi->fakehost);
3834 hi->fakehost = NULL;
3835 }
3836 }
3837 else if (!check_vhost(argv[1], user, cmd)) {
3838 /* check_vhost takes care of error reply */
3839 return 0;
3840 }
3841 else {
3842 if(hi->fakehost)
3843 free(hi->fakehost);
d76ed9a9 3844 hi->fakehost = strdup(fake);
7637f48f 3845 }
d76ed9a9 3846 apply_fakehost(hi);
bf93ca8d 3847 fake = hi->fakehost;
88b0672a 3848 } else
3849 fake = generate_fakehost(hi);
3850
bf93ca8d 3851 /* Tell them we set the host */
d76ed9a9
AS
3852 if (!fake)
3853 fake = user_find_message(user, "MSG_NONE");
886bca7c
MB
3854 if (!(noreply))
3855 reply("NSMSG_SET_FAKEHOST", fake);
d76ed9a9
AS
3856 return 1;
3857}
3858
0f6fe38c 3859static OPTION_FUNC(opt_note)
3860{
3861 if (!override) {
886bca7c
MB
3862 if (!(noreply))
3863 reply("MSG_SETTING_PRIVILEGED", argv[0]);
0f6fe38c 3864 return 0;
3865 }
3866
3867 if (argc > 1) {
3868 char *text = unsplit_string(argv + 1, argc - 1, NULL);
3869
3870 if (hi->note)
3871 free(hi->note);
3872
3873 if ((text[0] == '*') && !text[1])
3874 hi->note = NULL;
3875 else {
3876 if (!(hi->note = nickserv_add_note(user->handle_info->handle, now, text)))
3877 hi->note = NULL;
3878 }
3879 }
3880
886bca7c
MB
3881 if (!(noreply))
3882 reply("NSMSG_SET_NOTE", hi->note ? hi->note->note : user_find_message(user, "MSG_NONE"));
0f6fe38c 3883 return 1;
3884}
3885
d76ed9a9
AS
3886static NICKSERV_FUNC(cmd_reclaim)
3887{
3888 struct handle_info *hi;
3889 struct nick_info *ni;
3890 struct userNode *victim;
3891
3892 NICKSERV_MIN_PARMS(2);
3893 hi = user->handle_info;
3894 ni = dict_find(nickserv_nick_dict, argv[1], 0);
3895 if (!ni) {
3896 reply("NSMSG_UNKNOWN_NICK", argv[1]);
3897 return 0;
3898 }
3899 if (ni->owner != user->handle_info) {
3900 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
3901 return 0;
3902 }
3903 victim = GetUserH(ni->nick);
3904 if (!victim) {
3905 reply("MSG_NICK_UNKNOWN", ni->nick);
3906 return 0;
3907 }
3908 if (victim == user) {
3909 reply("NSMSG_NICK_USER_YOU");
3910 return 0;
3911 }
3912 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
3913 switch (nickserv_conf.reclaim_action) {
3914 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
3915 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
3916 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
3917 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
3918 }
3919 return 1;
3920}
3921
3922static NICKSERV_FUNC(cmd_unregnick)
3923{
3924 const char *nick;
3925 struct handle_info *hi;
3926 struct nick_info *ni;
3927
3928 hi = user->handle_info;
3929 nick = (argc < 2) ? user->nick : (const char*)argv[1];
3930 ni = dict_find(nickserv_nick_dict, nick, NULL);
3931 if (!ni) {
3932 reply("NSMSG_UNKNOWN_NICK", nick);
3933 return 0;
3934 }
3935 if (hi != ni->owner) {
3936 reply("NSMSG_NOT_YOUR_NICK", nick);
3937 return 0;
3938 }
3939 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3940 delete_nick(ni);
3941 return 1;
3942}
3943
3944static NICKSERV_FUNC(cmd_ounregnick)
3945{
3946 struct nick_info *ni;
3947
3948 NICKSERV_MIN_PARMS(2);
3949 if (!(ni = get_nick_info(argv[1]))) {
3950 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
3951 return 0;
3952 }
1136f709 3953 if (!oper_outranks(user, ni->owner))
3954 return 0;
d76ed9a9
AS
3955 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3956 delete_nick(ni);
3957 return 1;
3958}
3959
3960static NICKSERV_FUNC(cmd_unregister)
3961{
3962 struct handle_info *hi;
3963 char *passwd;
3964
3965 NICKSERV_MIN_PARMS(2);
3966 hi = user->handle_info;
3967 passwd = argv[1];
3968 argv[1] = "****";
3969 if (checkpass(passwd, hi->passwd)) {
73d4cc91
AS
3970 if(nickserv_unregister_handle(hi, user, cmd->parent->bot))
3971 return 1;
3972 else
3973 return 0;
d76ed9a9
AS
3974 } else {
3975 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3976 reply("NSMSG_PASSWORD_INVALID");
3977 return 0;
3978 }
3979}
3980
3981static NICKSERV_FUNC(cmd_ounregister)
3982{
3983 struct handle_info *hi;
1136f709 3984 char reason[MAXLEN];
3985 int force;
d76ed9a9
AS
3986
3987 NICKSERV_MIN_PARMS(2);
1136f709 3988 if (!(hi = get_victim_oper(user, argv[1])))
d76ed9a9 3989 return 0;
1136f709 3990
3991 if (HANDLE_FLAGGED(hi, NODELETE)) {
3992 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
3993 return 0;
3994 }
3995
3996 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
3997 if (!force &&
3998 ((hi->flags & nickserv_conf.ounregister_flags)
3999 || hi->users
4000 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
4001 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
4002 return 0;
4003 }
4004 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
4005 global_message(MESSAGE_RECIPIENT_STAFF, reason);
73d4cc91
AS
4006 if(nickserv_unregister_handle(hi, user, cmd->parent->bot))
4007 return 1;
4008 else
4009 return 0;
d76ed9a9
AS
4010}
4011
4012static NICKSERV_FUNC(cmd_status)
4013{
4014 if (nickserv_conf.disable_nicks) {
4015 reply("NSMSG_GLOBAL_STATS_NONICK",
4016 dict_size(nickserv_handle_dict));
4017 } else {
4018 if (user->handle_info) {
4019 int cnt=0;
4020 struct nick_info *ni;
4021 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
4022 reply("NSMSG_HANDLE_STATS", cnt);
4023 } else {
4024 reply("NSMSG_HANDLE_NONE");
4025 }
4026 reply("NSMSG_GLOBAL_STATS",
4027 dict_size(nickserv_handle_dict),
4028 dict_size(nickserv_nick_dict));
4029 }
4030 return 1;
4031}
4032
4033static NICKSERV_FUNC(cmd_ghost)
4034{
4035 struct userNode *target;
4036 char reason[MAXLEN];
4037
4038 NICKSERV_MIN_PARMS(2);
4039 if (!(target = GetUserH(argv[1]))) {
4040 reply("MSG_NICK_UNKNOWN", argv[1]);
4041 return 0;
4042 }
4043 if (target == user) {
4044 reply("NSMSG_CANNOT_GHOST_SELF");
4045 return 0;
4046 }
4047 if (!target->handle_info || (target->handle_info != user->handle_info)) {
4048 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
4049 return 0;
4050 }
4051 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
4052 DelUser(target, nickserv, 1, reason);
4053 reply("NSMSG_GHOST_KILLED", argv[1]);
4054 return 1;
4055}
4056
4057static NICKSERV_FUNC(cmd_vacation)
4058{
4059 HANDLE_SET_FLAG(user->handle_info, FROZEN);
4060 reply("NSMSG_ON_VACATION");
4061 return 1;
4062}
4063
4064static int
4065nickserv_saxdb_write(struct saxdb_context *ctx) {
4066 dict_iterator_t it;
4067 struct handle_info *hi;
4068 char flags[33];
4069
4070 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
4071 hi = iter_data(it);
d76ed9a9 4072 saxdb_start_record(ctx, iter_key(it), 0);
0f6fe38c 4073 if (hi->announcements != '?') {
4074 flags[0] = hi->announcements;
4075 flags[1] = 0;
4076 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
4077 }
d76ed9a9
AS
4078 if (hi->cookie) {
4079 struct handle_cookie *cookie = hi->cookie;
4080 char *type;
4081
4082 switch (cookie->type) {
4083 case ACTIVATION: type = KEY_ACTIVATION; break;
4084 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
4085 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
4086 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
4087 default: type = NULL; break;
4088 }
4089 if (type) {
4090 saxdb_start_record(ctx, KEY_COOKIE, 0);
4091 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
4092 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
4093 if (cookie->data)
4094 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
4095 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
4096 saxdb_end_record(ctx);
4097 }
4098 }
4099 if (hi->email_addr)
4100 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
4101 if (hi->epithet)
4102 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
0f6fe38c 4103 if (hi->note) {
4104 saxdb_start_record(ctx, KEY_NOTE_NOTE, 0);
4105 saxdb_write_string(ctx, KEY_NOTE_SETTER, hi->note->setter);
4106 saxdb_write_int(ctx, KEY_NOTE_DATE, hi->note->date);
4107 saxdb_write_string(ctx, KEY_NOTE_NOTE, hi->note->note);
4108 saxdb_end_record(ctx);
4109 }
2362161a 4110
d76ed9a9
AS
4111 if (hi->fakehost)
4112 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
4113 if (hi->flags) {
4114 int ii, flen;
4115
4116 for (ii=flen=0; handle_flags[ii]; ++ii)
4117 if (hi->flags & (1 << ii))
4118 flags[flen++] = handle_flags[ii];
4119 flags[flen] = 0;
4120 saxdb_write_string(ctx, KEY_FLAGS, flags);
4121 }
d76ed9a9
AS
4122 if (hi->infoline)
4123 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
4124 if (hi->last_quit_host[0])
4125 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
4126 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
1136f709 4127 if (hi->karma != 0)
4128 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
d76ed9a9
AS
4129 if (hi->masks->used)
4130 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2fa83595
MB
4131 if (hi->sslfps->used)
4132 saxdb_write_string_list(ctx, KEY_SSLFPS, hi->sslfps);
5177fd21 4133 if (hi->ignores->used)
4134 saxdb_write_string_list(ctx, KEY_IGNORES, hi->ignores);
d76ed9a9
AS
4135 if (hi->maxlogins)
4136 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
4137 if (hi->nicks) {
d76ed9a9
AS
4138 struct nick_info *ni;
4139
ae275267
MB
4140 saxdb_start_record(ctx, KEY_NICKS_EX, 0);
4141 for (ni = hi->nicks; ni; ni = ni->next) {
4142 saxdb_start_record(ctx, ni->nick, 0);
4143 saxdb_write_int(ctx, KEY_REGISTER_ON, ni->registered);
4144 saxdb_write_int(ctx, KEY_LAST_SEEN, ni->lastseen);
4145 saxdb_end_record(ctx);
4146 }
4147 saxdb_end_record(ctx);
d76ed9a9
AS
4148 }
4149 if (hi->opserv_level)
4150 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
4151 if (hi->language != lang_C)
4152 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
4153 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
4154 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
4155 if (hi->screen_width)
4156 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
4157 if (hi->table_width)
4158 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
4159 flags[0] = hi->userlist_style;
4160 flags[1] = 0;
4161 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
4162 saxdb_end_record(ctx);
4163 }
5177fd21 4164
d76ed9a9
AS
4165 return 0;
4166}
4167
4168static handle_merge_func_t *handle_merge_func_list;
50dafce8 4169static void **handle_merge_func_list_extra;
d76ed9a9
AS
4170static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
4171
4172void
50dafce8 4173reg_handle_merge_func(handle_merge_func_t func, void *extra)
d76ed9a9
AS
4174{
4175 if (handle_merge_func_used == handle_merge_func_size) {
4176 if (handle_merge_func_size) {
4177 handle_merge_func_size <<= 1;
4178 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
50dafce8 4179 handle_merge_func_list_extra = realloc(handle_merge_func_list_extra, handle_merge_func_size*sizeof(void*));
d76ed9a9
AS
4180 } else {
4181 handle_merge_func_size = 8;
4182 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
50dafce8 4183 handle_merge_func_list_extra = malloc(handle_merge_func_size*sizeof(void*));
d76ed9a9
AS
4184 }
4185 }
50dafce8 4186 handle_merge_func_list[handle_merge_func_used] = func;
4187 handle_merge_func_list_extra[handle_merge_func_used++] = extra;
d76ed9a9
AS
4188}
4189
4190static NICKSERV_FUNC(cmd_merge)
4191{
4192 struct handle_info *hi_from, *hi_to;
4193 struct userNode *last_user;
4194 struct userData *cList, *cListNext;
4195 unsigned int ii, jj, n;
d76ed9a9
AS
4196
4197 NICKSERV_MIN_PARMS(3);
4198
1136f709 4199 if (!(hi_from = get_victim_oper(user, argv[1])))
d76ed9a9 4200 return 0;
1136f709 4201 if (!(hi_to = get_victim_oper(user, argv[2])))
d76ed9a9
AS
4202 return 0;
4203 if (hi_to == hi_from) {
4204 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
4205 return 0;
4206 }
4207
4208 for (n=0; n<handle_merge_func_used; n++)
50dafce8 4209 handle_merge_func_list[n](user, hi_to, hi_from, handle_merge_func_list_extra[n]);
d76ed9a9
AS
4210
4211 /* Append "from" handle's nicks to "to" handle's nick list. */
4212 if (hi_to->nicks) {
4213 struct nick_info *last_ni;
4214 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
4215 last_ni->next = hi_from->nicks;
4216 }
4217 while (hi_from->nicks) {
4218 hi_from->nicks->owner = hi_to;
4219 hi_from->nicks = hi_from->nicks->next;
4220 }
4221
4222 /* Merge the hostmasks. */
4223 for (ii=0; ii<hi_from->masks->used; ii++) {
4224 char *mask = hi_from->masks->list[ii];
4225 for (jj=0; jj<hi_to->masks->used; jj++)
4226 if (match_ircglobs(hi_to->masks->list[jj], mask))
4227 break;
4228 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
4229 string_list_append(hi_to->masks, strdup(mask));
4230 }
4231
c73514ea
MB
4232 /* Merge the SSL fingerprints. */
4233 for (ii=0; ii<hi_from->sslfps->used; ii++) {
4234 char *sslfp = hi_from->sslfps->list[ii];
4235 for (jj=0; jj<hi_to->sslfps->used; jj++)
4236 if (!irccasecmp(hi_to->sslfps->list[jj], sslfp))
4237 break;
4238 if (jj==hi_to->sslfps->used) /* Nothing from the "to" handle covered this sslfp, so add it. */
4239 string_list_append(hi_to->sslfps, strdup(sslfp));
4240 }
4241
5177fd21 4242 /* Merge the ignores. */
4243 for (ii=0; ii<hi_from->ignores->used; ii++) {
4244 char *ignore = hi_from->ignores->list[ii];
4245 for (jj=0; jj<hi_to->ignores->used; jj++)
4246 if (match_ircglobs(hi_to->ignores->list[jj], ignore))
4247 break;
4248 if (jj==hi_to->ignores->used) /* Nothing from the "to" handle covered this mask, so add it. */
4249 string_list_append(hi_to->ignores, strdup(ignore));
4250 }
4251
d76ed9a9
AS
4252 /* Merge the lists of authed users. */
4253 if (hi_to->users) {
4254 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
4255 last_user->next_authed = hi_from->users;
4256 } else {
4257 hi_to->users = hi_from->users;
4258 }
4259 /* Repoint the old "from" handle's users. */
4260 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
4261 last_user->handle_info = hi_to;
4262 }
4263 hi_from->users = NULL;
4264
4265 /* Merge channel userlists. */
4266 for (cList=hi_from->channels; cList; cList=cListNext) {
4267 struct userData *cList2;
4268 cListNext = cList->u_next;
4269 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
4270 if (cList->channel == cList2->channel)
4271 break;
4272 if (cList2 && (cList2->access >= cList->access)) {
4273 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);
4274 /* keep cList2 in hi_to; remove cList from hi_from */
4275 del_channel_user(cList, 1);
4276 } else {
4277 if (cList2) {
4278 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);
4279 /* remove the lower-ranking cList2 from hi_to */
4280 del_channel_user(cList2, 1);
4281 } else {
4282 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
4283 }
4284 /* cList needs to be moved from hi_from to hi_to */
4285 cList->handle = hi_to;
4286 /* Remove from linked list for hi_from */
4287 assert(!cList->u_prev);
4288 hi_from->channels = cList->u_next;
4289 if (cList->u_next)
4290 cList->u_next->u_prev = cList->u_prev;
4291 /* Add to linked list for hi_to */
4292 cList->u_prev = NULL;
4293 cList->u_next = hi_to->channels;
4294 if (hi_to->channels)
4295 hi_to->channels->u_prev = cList;
4296 hi_to->channels = cList;
4297 }
4298 }
4299
4300 /* Do they get an OpServ level promotion? */
4301 if (hi_from->opserv_level > hi_to->opserv_level)
4302 hi_to->opserv_level = hi_from->opserv_level;
4303
4304 /* What about last seen time? */
4305 if (hi_from->lastseen > hi_to->lastseen)
4306 hi_to->lastseen = hi_from->lastseen;
4307
1136f709 4308 /* New karma is the sum of the two original karmas. */
4309 hi_to->karma += hi_from->karma;
4310
0d16e639 4311 /* Does a fakehost carry over? (This intentionally doesn't set it
4312 * for users previously attached to hi_to. They'll just have to
4313 * reconnect.)
4314 */
4315 if (hi_from->fakehost && !hi_to->fakehost)
4316 hi_to->fakehost = strdup(hi_from->fakehost);
4317
d76ed9a9 4318 /* Notify of success. */
d76ed9a9 4319 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
57692f5e 4320 global_message_args(MESSAGE_RECIPIENT_OPERS, "NSMSG_ACCOUNT_MERGED", user->nick,
4321 user->handle_info->handle, hi_from->handle, hi_to->handle);
d76ed9a9
AS
4322
4323 /* Unregister the "from" handle. */
258d1427 4324 nickserv_unregister_handle(hi_from, NULL, cmd->parent->bot);
73d4cc91
AS
4325 /* TODO: fix it so that if the ldap delete in nickserv_unregister_handle fails,
4326 * the process isn't completed.
4327 */
d76ed9a9
AS
4328
4329 return 1;
4330}
4331
4332struct nickserv_discrim {
d76ed9a9
AS
4333 unsigned long flags_on, flags_off;
4334 time_t min_registered, max_registered;
4335 time_t lastseen;
1136f709 4336 unsigned int limit;
4337 int min_level, max_level;
4338 int min_karma, max_karma;
d76ed9a9
AS
4339 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
4340 const char *nickmask;
4341 const char *hostmask;
4342 const char *handlemask;
4343 const char *emailmask;
73ac2ca6 4344 const char *titlemask;
886bca7c
MB
4345 const char *setwhat;
4346 const char *setval;
4347 struct svccmd *cmd;
b96027ad
AS
4348#ifdef WITH_LDAP
4349 unsigned int inldap;
4350#endif
d76ed9a9
AS
4351};
4352
886bca7c 4353typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi, struct nickserv_discrim *discrim);
d76ed9a9
AS
4354
4355struct discrim_apply_info {
4356 struct nickserv_discrim *discrim;
4357 discrim_search_func func;
4358 struct userNode *source;
4359 unsigned int matched;
4360};
4361
4362static struct nickserv_discrim *
c092fcad 4363nickserv_discrim_create(struct svccmd *cmd, struct userNode *user, unsigned int argc, char *argv[])
d76ed9a9
AS
4364{
4365 unsigned int i;
4366 struct nickserv_discrim *discrim;
4367
4368 discrim = malloc(sizeof(*discrim));
4369 memset(discrim, 0, sizeof(*discrim));
4370 discrim->min_level = 0;
1136f709 4371 discrim->max_level = INT_MAX;
d76ed9a9
AS
4372 discrim->limit = 50;
4373 discrim->min_registered = 0;
4374 discrim->max_registered = INT_MAX;
1136f709 4375 discrim->lastseen = LONG_MAX;
4376 discrim->min_karma = INT_MIN;
4377 discrim->max_karma = INT_MAX;
886bca7c 4378 discrim->cmd = cmd;
b96027ad
AS
4379#ifdef WITH_LDAP
4380 discrim->inldap = 2;
4381#endif
d76ed9a9
AS
4382
4383 for (i=0; i<argc; i++) {
4384 if (i == argc - 1) {
c092fcad 4385 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9
AS
4386 goto fail;
4387 }
4388 if (!irccasecmp(argv[i], "limit")) {
4389 discrim->limit = strtoul(argv[++i], NULL, 0);
4390 } else if (!irccasecmp(argv[i], "flags")) {
4391 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
4392 } else if (!irccasecmp(argv[i], "registered")) {
4393 const char *cmp = argv[++i];
4394 if (cmp[0] == '<') {
4395 if (cmp[1] == '=') {
4396 discrim->min_registered = now - ParseInterval(cmp+2);
4397 } else {
4398 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
4399 }
4400 } else if (cmp[0] == '=') {
4401 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
4402 } else if (cmp[0] == '>') {
4403 if (cmp[1] == '=') {
4404 discrim->max_registered = now - ParseInterval(cmp+2);
4405 } else {
4406 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
4407 }
4408 } else {
c092fcad 4409 reply("MSG_INVALID_CRITERIA", cmp);
d76ed9a9
AS
4410 }
4411 } else if (!irccasecmp(argv[i], "seen")) {
4412 discrim->lastseen = now - ParseInterval(argv[++i]);
4413 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
4414 discrim->nickmask = argv[++i];
886bca7c
MB
4415 } else if (!irccasecmp(argv[i], "setwhat")) {
4416 discrim->setwhat = argv[++i];
4417 if (!(dict_find(nickserv_opt_dict, discrim->setwhat, NULL))) {
4418 reply("NSMSG_INVALID_OPTION", discrim->setwhat);
4419 goto fail;
4420 }
4421 } else if (!irccasecmp(argv[i], "setvalue")) {
4422 discrim->setval = argv[++i];
d76ed9a9
AS
4423 } else if (!irccasecmp(argv[i], "hostmask")) {
4424 i++;
4425 if (!irccasecmp(argv[i], "exact")) {
4426 if (i == argc - 1) {
c092fcad 4427 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9
AS
4428 goto fail;
4429 }
4430 discrim->hostmask_type = EXACT;
4431 } else if (!irccasecmp(argv[i], "subset")) {
4432 if (i == argc - 1) {
c092fcad 4433 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9
AS
4434 goto fail;
4435 }
4436 discrim->hostmask_type = SUBSET;
4437 } else if (!irccasecmp(argv[i], "superset")) {
4438 if (i == argc - 1) {
c092fcad 4439 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9
AS
4440 goto fail;
4441 }
4442 discrim->hostmask_type = SUPERSET;
4443 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
4444 if (i == argc - 1) {
c092fcad 4445 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9
AS
4446 goto fail;
4447 }
4448 discrim->hostmask_type = LASTQUIT;
4449 } else {
4450 i--;
4451 discrim->hostmask_type = SUPERSET;
4452 }
4453 discrim->hostmask = argv[++i];
b96027ad 4454 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask") || !irccasecmp(argv[i], "account")) {
d76ed9a9
AS
4455 if (!irccasecmp(argv[++i], "*")) {
4456 discrim->handlemask = 0;
4457 } else {
4458 discrim->handlemask = argv[i];
4459 }
4460 } else if (!irccasecmp(argv[i], "email")) {
4461 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
c092fcad 4462 reply("MSG_NO_SEARCH_ACCESS", "email");
d76ed9a9
AS
4463 goto fail;
4464 } else if (!irccasecmp(argv[++i], "*")) {
4465 discrim->emailmask = 0;
4466 } else {
4467 discrim->emailmask = argv[i];
4468 }
73ac2ca6
MB
4469 } else if (!irccasecmp(argv[i], "title")) {
4470 if (!irccasecmp(argv[++i], "*")) {
4471 discrim->titlemask = 0;
4472 } else {
4473 discrim->titlemask = argv[i];
4474 }
d76ed9a9
AS
4475 } else if (!irccasecmp(argv[i], "access")) {
4476 const char *cmp = argv[++i];
4477 if (cmp[0] == '<') {
4478 if (discrim->min_level == 0) discrim->min_level = 1;
4479 if (cmp[1] == '=') {
4480 discrim->max_level = strtoul(cmp+2, NULL, 0);
4481 } else {
4482 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
4483 }
4484 } else if (cmp[0] == '=') {
4485 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
4486 } else if (cmp[0] == '>') {
4487 if (cmp[1] == '=') {
4488 discrim->min_level = strtoul(cmp+2, NULL, 0);
4489 } else {
4490 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
4491 }
4492 } else {
c092fcad 4493 reply("MSG_INVALID_CRITERIA", cmp);
d76ed9a9 4494 }
1136f709 4495 } else if (!irccasecmp(argv[i], "karma")) {
4496 const char *cmp = argv[++i];
4497 if (cmp[0] == '<') {
4498 if (cmp[1] == '=') {
4499 discrim->max_karma = strtoul(cmp+2, NULL, 0);
4500 } else {
4501 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
4502 }
4503 } else if (cmp[0] == '=') {
4504 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
4505 } else if (cmp[0] == '>') {
4506 if (cmp[1] == '=') {
4507 discrim->min_karma = strtoul(cmp+2, NULL, 0);
4508 } else {
4509 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
4510 }
4511 } else {
4512 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
4513 }
d5faccba 4514#ifdef WITH_LDAP
b96027ad
AS
4515 } else if (nickserv_conf.ldap_enable && !irccasecmp(argv[i], "inldap")) {
4516 i++;
4517 if(true_string(argv[i])) {
4518 discrim->inldap = 1;
4519 }
4520 else if (false_string(argv[i])) {
4521 discrim->inldap = 0;
4522 }
4523 else {
4524 reply("MSG_INVALID_BINARY", argv[i]);
4525 }
d5faccba 4526#endif
b96027ad 4527 } else {
c092fcad 4528 reply("MSG_INVALID_CRITERIA", argv[i]);
d76ed9a9
AS
4529 goto fail;
4530 }
4531 }
4532 return discrim;
4533 fail:
4534 free(discrim);
4535 return NULL;
4536}
4537
4538static int
4539nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
4540{
73ac2ca6
MB
4541 char *title = NULL;
4542
4543 if (hi->fakehost && (hi->fakehost[0] == '.'))
4544 title = hi->fakehost + 1;
4545
d76ed9a9
AS
4546 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
4547 || (discrim->flags_off & hi->flags)
4548 || (discrim->min_registered > hi->registered)
4549 || (discrim->max_registered < hi->registered)
4550 || (discrim->lastseen < (hi->users?now:hi->lastseen))
4551 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
4552 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
73ac2ca6 4553 || (discrim->titlemask && (!title || !match_ircglob(title, discrim->titlemask)))
d76ed9a9 4554 || (discrim->min_level > hi->opserv_level)
1136f709 4555 || (discrim->max_level < hi->opserv_level)
4556 || (discrim->min_karma > hi->karma)
4557 || (discrim->max_karma < hi->karma)
4558 ) {
d76ed9a9
AS
4559 return 0;
4560 }
4561 if (discrim->hostmask) {
4562 unsigned int i;
4563 for (i=0; i<hi->masks->used; i++) {
4564 const char *mask = hi->masks->list[i];
4565 if ((discrim->hostmask_type == SUBSET)
4566 && (match_ircglobs(discrim->hostmask, mask))) break;
4567 else if ((discrim->hostmask_type == EXACT)
4568 && !irccasecmp(discrim->hostmask, mask)) break;
4569 else if ((discrim->hostmask_type == SUPERSET)
4570 && (match_ircglobs(mask, discrim->hostmask))) break;
4571 else if ((discrim->hostmask_type == LASTQUIT)
4572 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
4573 }
4574 if (i==hi->masks->used) return 0;
4575 }
4576 if (discrim->nickmask) {
4577 struct nick_info *nick = hi->nicks;
4578 while (nick) {
4579 if (match_ircglob(nick->nick, discrim->nickmask)) break;
4580 nick = nick->next;
4581 }
4582 if (!nick) return 0;
4583 }
b96027ad
AS
4584#ifdef WITH_LDAP
4585 if(nickserv_conf.ldap_enable && discrim->inldap != 2) {
4586 int rc;
4587 rc = ldap_get_user_info(hi->handle, NULL);
4588 if(discrim->inldap == 1 && rc != LDAP_SUCCESS)
4589 return 0;
4590 if(discrim->inldap == 0 && rc == LDAP_SUCCESS)
4591 return 0;
4592 }
4593
4594#endif
d76ed9a9
AS
4595 return 1;
4596}
4597
4598static unsigned int
4599nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
4600{
4601 dict_iterator_t it, next;
4602 unsigned int matched;
4603
4604 for (it = dict_first(nickserv_handle_dict), matched = 0;
4605 it && (matched < discrim->limit);
4606 it = next) {
4607 next = iter_next(it);
4608 if (nickserv_discrim_match(discrim, iter_data(it))) {
886bca7c 4609 dsf(source, iter_data(it), discrim);
d76ed9a9
AS
4610 matched++;
4611 }
4612 }
4613 return matched;
4614}
4615
4616static void
886bca7c 4617search_print_func(struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
d76ed9a9
AS
4618{
4619 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
4620}
4621
4622static void
886bca7c 4623search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match), UNUSED_ARG(struct nickserv_discrim *discrim))
d76ed9a9
AS
4624{
4625}
4626
4627static void
886bca7c 4628search_unregister_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
d76ed9a9
AS
4629{
4630 if (oper_has_access(source, nickserv, match->opserv_level, 0))
258d1427 4631 nickserv_unregister_handle(match, source, nickserv); // XXX nickserv hard coded
d76ed9a9
AS
4632}
4633
4cb36ef0 4634#ifdef WITH_LDAP
b96027ad 4635static void
886bca7c 4636search_add2ldap_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
b96027ad 4637{
87677bd8
AS
4638 int rc;
4639 if(match->email_addr && match->passwd && match->handle) {
4640 rc = ldap_do_add(match->handle, match->passwd, match->email_addr);
4641 if(rc != LDAP_SUCCESS) {
4642 send_message(source, nickserv, "NSMSG_LDAP_FAIL_ADD", match->handle, ldap_err2string(rc));
4643 }
b96027ad 4644 }
b96027ad 4645}
4cb36ef0 4646#endif
b96027ad 4647
886bca7c
MB
4648static void
4649search_set_func (struct userNode *source, struct handle_info *match, struct nickserv_discrim *discrim)
4650{
4651 option_func_t *opt;
4652 char *oargv[2];
4653
4654 if (!(opt = dict_find(nickserv_opt_dict, discrim->setwhat, NULL))) {
4655 return;
4656 }
4657
4658 oargv[0] = (char *)discrim->setwhat;
4659 oargv[1] = (char *)discrim->setval;
4660
4661 opt(discrim->cmd, source, match, 1, 1, 2, oargv);
4662}
4663
d76ed9a9
AS
4664static int
4665nickserv_sort_accounts_by_access(const void *a, const void *b)
4666{
4667 const struct handle_info *hi_a = *(const struct handle_info**)a;
4668 const struct handle_info *hi_b = *(const struct handle_info**)b;
4669 if (hi_a->opserv_level != hi_b->opserv_level)
4670 return hi_b->opserv_level - hi_a->opserv_level;
4671 return irccasecmp(hi_a->handle, hi_b->handle);
4672}
4673
4674void
4675nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
4676{
4677 struct handle_info_list hil;
4678 struct helpfile_table tbl;
4679 unsigned int ii;
4680 dict_iterator_t it;
4681 const char **ary;
4682
4683 memset(&hil, 0, sizeof(hil));
4684 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
4685 struct handle_info *hi = iter_data(it);
4686 if (hi->opserv_level)
4687 handle_info_list_append(&hil, hi);
4688 }
4689 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
4690 tbl.length = hil.used + 1;
4691 tbl.width = 2;
a8370a20 4692 tbl.flags = TABLE_NO_FREE | TABLE_REPEAT_ROWS | TABLE_REPEAT_HEADERS;
d76ed9a9
AS
4693 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4694 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
4695 ary[0] = "Account";
4696 ary[1] = "Level";
4697 for (ii = 0; ii < hil.used; ) {
4698 ary = malloc(tbl.width * sizeof(ary[0]));
4699 ary[0] = hil.list[ii]->handle;
4700 ary[1] = strtab(hil.list[ii]->opserv_level);
4701 tbl.contents[++ii] = ary;
4702 }
4703 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
a8370a20 4704 /*reply("MSG_MATCH_COUNT", hil.used); */
d76ed9a9
AS
4705 for (ii = 0; ii < hil.used; ii++)
4706 free(tbl.contents[ii]);
4707 free(tbl.contents);
4708 free(hil.list);
4709}
4710
4711static NICKSERV_FUNC(cmd_search)
4712{
4713 struct nickserv_discrim *discrim;
4714 discrim_search_func action;
4715 struct svccmd *subcmd;
4716 unsigned int matches;
4717 char buf[MAXLEN];
4718
4719 NICKSERV_MIN_PARMS(3);
4720 sprintf(buf, "search %s", argv[1]);
4721 subcmd = dict_find(nickserv_service->commands, buf, NULL);
4722 if (!irccasecmp(argv[1], "print"))
4723 action = search_print_func;
4724 else if (!irccasecmp(argv[1], "count"))
4725 action = search_count_func;
4726 else if (!irccasecmp(argv[1], "unregister"))
4727 action = search_unregister_func;
886bca7c
MB
4728 else if (!irccasecmp(argv[1], "set"))
4729 action = search_set_func;
b96027ad
AS
4730#ifdef WITH_LDAP
4731 else if (nickserv_conf.ldap_enable && !irccasecmp(argv[1], "add2ldap"))
4732 action = search_add2ldap_func;
4733#endif
d76ed9a9
AS
4734 else {
4735 reply("NSMSG_INVALID_ACTION", argv[1]);
4736 return 0;
4737 }
4738
4739 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
4740 return 0;
4741
c092fcad 4742 discrim = nickserv_discrim_create(cmd, user, argc-2, argv+2);
d76ed9a9
AS
4743 if (!discrim)
4744 return 0;
4745
4746 if (action == search_print_func)
4747 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
4748 else if (action == search_count_func)
4749 discrim->limit = INT_MAX;
886bca7c
MB
4750 else if ((action == search_set_func) && (!(discrim->setwhat) || !(discrim->setval)))
4751 return reply("MSG_MISSING_PARAMS", argv[1]);
d76ed9a9
AS
4752
4753 matches = nickserv_discrim_search(discrim, action, user);
4754
4755 if (matches)
4756 reply("MSG_MATCH_COUNT", matches);
4757 else
4758 reply("MSG_NO_MATCHES");
4759
4760 free(discrim);
4761 return 0;
4762}
4763
4764static MODCMD_FUNC(cmd_checkpass)
4765{
4766 struct handle_info *hi;
4767
4768 NICKSERV_MIN_PARMS(3);
4769 if (!(hi = get_handle_info(argv[1]))) {
4770 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4771 return 0;
4772 }
4773 if (checkpass(argv[2], hi->passwd))
4774 reply("CHECKPASS_YES");
4775 else
4776 reply("CHECKPASS_NO");
4777 argv[2] = "****";
4778 return 1;
4779}
4780
1136f709 4781static MODCMD_FUNC(cmd_checkemail)
4782{
4783 struct handle_info *hi;
4784
4785 NICKSERV_MIN_PARMS(3);
4786 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
4787 return 0;
4788 }
4789 if (!hi->email_addr)
4790 reply("CHECKEMAIL_NOT_SET");
4791 else if (!irccasecmp(argv[2], hi->email_addr))
4792 reply("CHECKEMAIL_YES");
4793 else
4794 reply("CHECKEMAIL_NO");
4795 return 1;
4796}
4797
d76ed9a9 4798static void
82b7b0d8 4799nickserv_db_read_handle(char *handle, dict_t obj)
d76ed9a9
AS
4800{
4801 const char *str;
2fa83595 4802 struct string_list *masks, *sslfps, *slist, *ignores;
d76ed9a9
AS
4803 struct handle_info *hi;
4804 struct userNode *authed_users;
1136f709 4805 struct userData *channel_list;
ae275267
MB
4806 struct dict *obj2;
4807 dict_iterator_t it;
d76ed9a9
AS
4808 unsigned long int id;
4809 unsigned int ii;
4810 dict_t subdb;
0f6fe38c 4811 char *setter, *note;
4812 time_t date;
d76ed9a9
AS
4813
4814 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
4815 id = str ? strtoul(str, NULL, 0) : 0;
4816 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
4817 if (!str) {
4818 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
4819 return;
4820 }
4821 if ((hi = get_handle_info(handle))) {
4822 authed_users = hi->users;
1136f709 4823 channel_list = hi->channels;
d76ed9a9 4824 hi->users = NULL;
02c37249 4825 hi->channels = NULL;
d76ed9a9
AS
4826 dict_remove(nickserv_handle_dict, hi->handle);
4827 } else {
4828 authed_users = NULL;
1136f709 4829 channel_list = NULL;
d76ed9a9 4830 }
acb142f0
AS
4831 if(nickserv_conf.force_handles_lowercase)
4832 irc_strtolower(handle);
d76ed9a9
AS
4833 hi = register_handle(handle, str, id);
4834 if (authed_users) {
4835 hi->users = authed_users;
4836 while (authed_users) {
4837 authed_users->handle_info = hi;
4838 authed_users = authed_users->next_authed;
4839 }
4840 }
1136f709 4841 hi->channels = channel_list;
d76ed9a9
AS
4842 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
4843 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
2fa83595
MB
4844 sslfps = database_get_data(obj, KEY_SSLFPS, RECDB_STRING_LIST);
4845 hi->sslfps = sslfps ? string_list_copy(sslfps) : alloc_string_list(1);
5177fd21 4846 ignores = database_get_data(obj, KEY_IGNORES, RECDB_STRING_LIST);
4847 hi->ignores = ignores ? string_list_copy(ignores) : alloc_string_list(1);
d76ed9a9
AS
4848 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
4849 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
4850 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
4851 hi->language = language_find(str ? str : "C");
4852 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
4853 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
4854 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
4855 if (str)
4856 hi->infoline = strdup(str);
4857 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
4858 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
4859 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
4860 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
1136f709 4861 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
4862 hi->karma = str ? strtoul(str, NULL, 0) : 0;
d76ed9a9
AS
4863 /* We want to read the nicks even if disable_nicks is set. This is so
4864 * that we don't lose the nick data entirely. */
ae275267
MB
4865 obj2 = database_get_data(obj, KEY_NICKS_EX, RECDB_OBJECT);
4866 for(it = dict_first(obj2); it; it = iter_next(it))
4867 {
4868 struct record_data *rd = iter_data(it);
4869 struct nick_info* ni;
4870
4871 register_nick(iter_key(it), hi);
4872 ni = get_nick_info(iter_key(it));
4873
4874 if (!(ni))
4875 continue;
4876
4877 str = database_get_data(rd->d.object, KEY_REGISTER_ON, RECDB_QSTRING);
4878 ni->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
4879 str = database_get_data(rd->d.object, KEY_LAST_SEEN, RECDB_QSTRING);
4880 ni->lastseen = str ? (time_t)strtoul(str, NULL, 0) : ni->registered;
4881 }
4882 if (!obj2) {
4883 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
4884 if (slist) {
4885 for (ii=0; ii<slist->used; ii++) {
4886 struct nick_info* ni;
4887
4888 register_nick(slist->list[ii], hi);
4889 ni = get_nick_info(slist->list[ii]);
4890
4891 if (!(ni))
4892 continue;
4893
4894 ni->registered = hi->registered;
4895 ni->lastseen = ni->registered;
4896 }
4897 }
d76ed9a9
AS
4898 }
4899 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
4900 if (str) {
4901 for (ii=0; str[ii]; ii++)
4902 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
4903 }
4904 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
338a82b5 4905 hi->userlist_style = str ? str[0] : HI_DEFAULT_STYLE;
0f6fe38c 4906 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
4907 hi->announcements = str ? str[0] : '?';
d76ed9a9
AS
4908 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
4909 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
4910 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
4911 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
4912 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
4913 if (!str)
4914 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
4915 if (str)
4916 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
4917 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
4918 if (str)
4919 nickserv_set_email_addr(hi, str);
4920 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
4921 if (str)
4922 hi->epithet = strdup(str);
0f6fe38c 4923 subdb = database_get_data(obj, KEY_NOTE_NOTE, RECDB_OBJECT);
4924 if (subdb) {
4925 setter = database_get_data(subdb, KEY_NOTE_SETTER, RECDB_QSTRING);
4926 str = database_get_data(subdb, KEY_NOTE_DATE, RECDB_QSTRING);
4927 date = str ? (time_t)strtoul(str, NULL, 0) : now;
4928 note = database_get_data(subdb, KEY_NOTE_NOTE, RECDB_QSTRING);
4929 if (setter && date && note)
4930 {
4931 if (!(hi->note = nickserv_add_note(setter, date, note)))
4932 hi->note = NULL;
4933 }
4934 }
2362161a 4935
d76ed9a9
AS
4936 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
4937 if (str)
4938 hi->fakehost = strdup(str);
7637f48f 4939
d76ed9a9
AS
4940 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
4941 if (subdb) {
4942 const char *data, *type, *expires, *cookie_str;
4943 struct handle_cookie *cookie;
4944
4945 cookie = calloc(1, sizeof(*cookie));
4946 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
4947 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
4948 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
4949 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
4950 if (!type || !expires || !cookie_str) {
4951 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
4952 goto cookie_out;
4953 }
4954 if (!irccasecmp(type, KEY_ACTIVATION))
4955 cookie->type = ACTIVATION;
4956 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
4957 cookie->type = PASSWORD_CHANGE;
4958 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
4959 cookie->type = EMAIL_CHANGE;
4960 else if (!irccasecmp(type, KEY_ALLOWAUTH))
4961 cookie->type = ALLOWAUTH;
4962 else {
4963 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
4964 goto cookie_out;
4965 }
4966 cookie->expires = strtoul(expires, NULL, 0);
4967 if (cookie->expires < now)
4968 goto cookie_out;
4969 if (data)
4970 cookie->data = strdup(data);
4971 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
4972 cookie->hi = hi;
4973 cookie_out:
4974 if (cookie->hi)
4975 nickserv_bake_cookie(cookie);
4976 else
4977 nickserv_free_cookie(cookie);
4978 }
4979}
4980
4981static int
4982nickserv_saxdb_read(dict_t db) {
4983 dict_iterator_t it;
4984 struct record_data *rd;
82b7b0d8 4985 char *handle;
d76ed9a9
AS
4986
4987 for (it=dict_first(db); it; it=iter_next(it)) {
4988 rd = iter_data(it);
82b7b0d8
AS
4989 handle = strdup(iter_key(it));
4990 nickserv_db_read_handle(handle, rd->d.object);
4991 free(handle);
d76ed9a9
AS
4992 }
4993 return 0;
4994}
4995
4996static NICKSERV_FUNC(cmd_mergedb)
4997{
4998 struct timeval start, stop;
4999 dict_t db;
5000
5001 NICKSERV_MIN_PARMS(2);
5002 gettimeofday(&start, NULL);
5003 if (!(db = parse_database(argv[1]))) {
5004 reply("NSMSG_DB_UNREADABLE", argv[1]);
5005 return 0;
5006 }
5007 nickserv_saxdb_read(db);
5008 free_database(db);
5009 gettimeofday(&stop, NULL);
5010 stop.tv_sec -= start.tv_sec;
5011 stop.tv_usec -= start.tv_usec;
5012 if (stop.tv_usec < 0) {
5013 stop.tv_sec -= 1;
5014 stop.tv_usec += 1000000;
5015 }
5016 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
5017 return 1;
5018}
5019
5020static void
5021expire_handles(UNUSED_ARG(void *data))
5022{
5023 dict_iterator_t it, next;
5024 time_t expiry;
5025 struct handle_info *hi;
5026
5027 for (it=dict_first(nickserv_handle_dict); it; it=next) {
5028 next = iter_next(it);
5029 hi = iter_data(it);
5030 if ((hi->opserv_level > 0)
5031 || hi->users
5032 || HANDLE_FLAGGED(hi, FROZEN)
5033 || HANDLE_FLAGGED(hi, NODELETE)) {
5034 continue;
5035 }
5036 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
5037 if ((now - hi->lastseen) > expiry) {
5038 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
258d1427 5039 nickserv_unregister_handle(hi, NULL, NULL);
d76ed9a9
AS
5040 }
5041 }
5042
5043 if (nickserv_conf.handle_expire_frequency)
5044 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
5045}
5046
ae275267
MB
5047static void
5048expire_nicks(UNUSED_ARG(void *data))
5049{
5050 dict_iterator_t it, next;
5051 time_t expiry = nickserv_conf.nick_expire_delay;
5052 struct nick_info *ni;
5053 struct userNode *ui;
5054
5055 if (!(nickserv_conf.expire_nicks))
5056 return;
5057
5058 for (it=dict_first(nickserv_nick_dict); it; it=next) {
5059 next = iter_next(it);
5060 ni = iter_data(it);
5061 if ((ni->owner->opserv_level > 0)
5062 || ((ui = GetUserH(ni->nick)) && (ui->handle_info) && (ui->handle_info == ni->owner))
5063 || HANDLE_FLAGGED(ni->owner, FROZEN)
5064 || HANDLE_FLAGGED(ni->owner, NODELETE)) {
5065 continue;
5066 }
5067 if ((now - ni->lastseen) > expiry) {
5068 log_module(NS_LOG, LOG_INFO, "Expiring nick %s for inactivity.", ni->nick);
5069 delete_nick(ni);
5070 }
5071 }
5072
5073 if (nickserv_conf.nick_expire_frequency && nickserv_conf.expire_nicks)
5074 timeq_add(now + nickserv_conf.nick_expire_frequency, expire_nicks, NULL);
5075}
5076
d76ed9a9
AS
5077static void
5078nickserv_load_dict(const char *fname)
5079{
5080 FILE *file;
5081 char line[128];
5082 if (!(file = fopen(fname, "r"))) {
5083 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
5084 return;
5085 }
1136f709 5086 while (fgets(line, sizeof(line), file)) {
d76ed9a9
AS
5087 if (!line[0])
5088 continue;
5089 if (line[strlen(line)-1] == '\n')
5090 line[strlen(line)-1] = 0;
5091 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
5092 }
5093 fclose(file);
5094 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
5095}
5096
5097static enum reclaim_action
5098reclaim_action_from_string(const char *str) {
5099 if (!str)
5100 return RECLAIM_NONE;
5101 else if (!irccasecmp(str, "warn"))
5102 return RECLAIM_WARN;
5103 else if (!irccasecmp(str, "svsnick"))
5104 return RECLAIM_SVSNICK;
5105 else if (!irccasecmp(str, "kill"))
5106 return RECLAIM_KILL;
5107 else
5108 return RECLAIM_NONE;
5109}
5110
5111static void
5112nickserv_conf_read(void)
5113{
5114 dict_t conf_node, child;
5115 const char *str;
5116 dict_iterator_t it;
7637f48f 5117 struct string_list *strlist;
d76ed9a9
AS
5118
5119 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
5120 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
5121 return;
5122 }
5123 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
5124 if (!str)
5125 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
5126 if (nickserv_conf.valid_handle_regex_set)
5127 regfree(&nickserv_conf.valid_handle_regex);
5128 if (str) {
5129 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
5130 nickserv_conf.valid_handle_regex_set = !err;
5131 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
5132 } else {
5133 nickserv_conf.valid_handle_regex_set = 0;
5134 }
5135 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
5136 if (nickserv_conf.valid_nick_regex_set)
5137 regfree(&nickserv_conf.valid_nick_regex);
5138 if (str) {
5139 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
5140 nickserv_conf.valid_nick_regex_set = !err;
5141 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
5142 } else {
5143 nickserv_conf.valid_nick_regex_set = 0;
5144 }
bf93ca8d
AS
5145 str = database_get_data(conf_node, KEY_VALID_FAKEHOST_REGEX, RECDB_QSTRING);
5146 if (nickserv_conf.valid_fakehost_regex_set)
5147 regfree(&nickserv_conf.valid_fakehost_regex);
5148 if (str) {
5149 int err = regcomp(&nickserv_conf.valid_fakehost_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
5150 nickserv_conf.valid_fakehost_regex_set = !err;
5151 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_fakehost_regex (error %d)", err);
5152 } else {
5153 nickserv_conf.valid_fakehost_regex_set = 0;
5154 }
d76ed9a9
AS
5155 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
5156 if (!str)
5157 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
5158 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
5159 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
5160 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
5161 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
5162 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
5163 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
5164 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
5165 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
5166 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
5167 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
5168 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
5169 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
5170 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
5171 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
5172 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
5173 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
5174 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
5175 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
5176 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
5177 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
5178 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
5179 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
5180 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
5181 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
5182 if (!str)
5183 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
5184 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
5185 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
5186 if (!str)
5187 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
5188 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
5189 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
5190 if (!str)
5191 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
5192 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
5193 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
5194 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
5195 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
5196 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
5197 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3c535a4b 5198 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
1136f709 5199 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
5200 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
5201 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
5202 if (!str)
5203 str = "ShgsfnHbu";
5204 nickserv_conf.ounregister_flags = 0;
5205 while(*str) {
5206 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
5207 str++;
5208 if(pos)
5209 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
5210 }
d76ed9a9
AS
5211 if (!nickserv_conf.disable_nicks) {
5212 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
5213 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
5214 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
5215 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
5216 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
5217 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
5218 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
5219 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
ae275267
MB
5220 str = database_get_data(conf_node, KEY_NICK_EXPIRE_FREQ, RECDB_QSTRING);
5221 nickserv_conf.nick_expire_frequency = str ? ParseInterval(str) : 86400;
5222 str = database_get_data(conf_node, KEY_NICK_EXPIRE_DELAY, RECDB_QSTRING);
5223 nickserv_conf.nick_expire_delay = str ? ParseInterval(str) : 86400*30;
5224 str = database_get_data(conf_node, "expire_nicks", RECDB_QSTRING);
5225 nickserv_conf.expire_nicks = str ? enabled_string(str) : 0;
d76ed9a9
AS
5226 }
5227 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
5228 for (it=dict_first(child); it; it=iter_next(it)) {
5229 const char *key = iter_key(it), *value;
5230 unsigned char flag;
5231 int pos;
5232
5233 if (!strncasecmp(key, "uc_", 3))
5234 flag = toupper(key[3]);
5235 else if (!strncasecmp(key, "lc_", 3))
5236 flag = tolower(key[3]);
5237 else
5238 flag = key[0];
5239
5240 if ((pos = handle_inverse_flags[flag])) {
5241 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
5242 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
5243 }
5244 }
5245 if (nickserv_conf.weak_password_dict)
5246 dict_delete(nickserv_conf.weak_password_dict);
5247 nickserv_conf.weak_password_dict = dict_new();
5248 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
5249 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
5250 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
5251 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
5252 if (str)
5253 nickserv_load_dict(str);
5254 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
5255 if (nickserv && str)
5256 NickChange(nickserv, str, 0);
5257 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
5258 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
5259 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
5260 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
5261 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
5262 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
5263 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
5264 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
8dc1d9ae 5265 str = database_get_data(conf_node, KEY_SYNC_LOG, RECDB_QSTRING);
5266 nickserv_conf.sync_log = str ? enabled_string(str) : 0;
d76ed9a9
AS
5267 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
5268 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
5269 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
5270 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
5271 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
5272 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
5273 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
5274 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
5275 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
5276 nickserv_conf.titlehost_suffix = str ? str : "example.net";
5a1daaab 5277
7637f48f 5278 free_string_list(nickserv_conf.denied_fakehost_words);
5279 strlist = database_get_data(conf_node, KEY_DENIED_FAKEHOST_WORDS, RECDB_STRING_LIST);
5280 if(strlist)
5281 strlist = string_list_copy(strlist);
5282 else {
5283 strlist = alloc_string_list(4);
5284 string_list_append(strlist, strdup("sex"));
5285 string_list_append(strlist, strdup("fuck"));
5286 }
5287 nickserv_conf.denied_fakehost_words = strlist;
5288
338a82b5
AS
5289 str = database_get_data(conf_node, KEY_DEFAULT_STYLE, RECDB_QSTRING);
5290 nickserv_conf.default_style = str ? str[0] : HI_DEFAULT_STYLE;
5291
5a1daaab
AS
5292 str = database_get_data(conf_node, KEY_AUTO_OPER, RECDB_QSTRING);
5293 nickserv_conf.auto_oper = str ? str : "";
5294
5295 str = database_get_data(conf_node, KEY_AUTO_ADMIN, RECDB_QSTRING);
5296 nickserv_conf.auto_admin = str ? str : "";
5297
69517d70 5298 str = database_get_data(conf_node, KEY_AUTO_OPER_PRIVS, RECDB_QSTRING);
5299 nickserv_conf.auto_oper_privs = str ? str : "";
5300
5301 str = database_get_data(conf_node, KEY_AUTO_ADMIN_PRIVS, RECDB_QSTRING);
5302 nickserv_conf.auto_admin_privs = str ? str : "";
5303
d76ed9a9
AS
5304 str = conf_get_data("server/network", RECDB_QSTRING);
5305 nickserv_conf.network_name = str ? str : "some IRC network";
5306 if (!nickserv_conf.auth_policer_params) {
5307 nickserv_conf.auth_policer_params = policer_params_new();
5308 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
5309 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
5310 }
5311 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
5312 for (it=dict_first(child); it; it=iter_next(it))
5313 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
e166c31b
AS
5314
5315 str = database_get_data(conf_node, KEY_LDAP_ENABLE, RECDB_QSTRING);
5316 nickserv_conf.ldap_enable = str ? strtoul(str, NULL, 0) : 0;
acb142f0
AS
5317
5318 str = database_get_data(conf_node, KEY_FORCE_HANDLES_LOWERCASE, RECDB_QSTRING);
5319 nickserv_conf.force_handles_lowercase = str ? strtol(str, NULL, 0) : 0;
5320
39edf54a
AS
5321#ifndef WITH_LDAP
5322 if(nickserv_conf.ldap_enable > 0) {
5323 /* ldap is enabled but not compiled in - error out */
5324 log_module(MAIN_LOG, LOG_ERROR, "ldap is enabled in config, but not compiled in!");
5325 nickserv_conf.ldap_enable = 0;
5326 sleep(5);
5327 }
5328#endif
e166c31b 5329
39edf54a 5330#ifdef WITH_LDAP
bec5dd26
AS
5331 str = database_get_data(conf_node, KEY_LDAP_URI, RECDB_QSTRING);
5332 nickserv_conf.ldap_uri = str ? str : "";
e166c31b 5333
e166c31b
AS
5334 str = database_get_data(conf_node, KEY_LDAP_BASE, RECDB_QSTRING);
5335 nickserv_conf.ldap_base = str ? str : "";
5336
5337 str = database_get_data(conf_node, KEY_LDAP_DN_FMT, RECDB_QSTRING);
5338 nickserv_conf.ldap_dn_fmt = str ? str : "";
5339
5340 str = database_get_data(conf_node, KEY_LDAP_VERSION, RECDB_QSTRING);
5341 nickserv_conf.ldap_version = str ? strtoul(str, NULL, 0) : 3;
5342
5343 str = database_get_data(conf_node, KEY_LDAP_AUTOCREATE, RECDB_QSTRING);
5344 nickserv_conf.ldap_autocreate = str ? strtoul(str, NULL, 0) : 0;
ea02341b 5345
ddcb3eb3
AS
5346 str = database_get_data(conf_node, KEY_LDAP_TIMEOUT, RECDB_QSTRING);
5347 nickserv_conf.ldap_timeout = str ? strtoul(str, NULL, 0) : 5;
5348
ea02341b
AS
5349 str = database_get_data(conf_node, KEY_LDAP_ADMIN_DN, RECDB_QSTRING);
5350 nickserv_conf.ldap_admin_dn = str ? str : "";
5351
5352 str = database_get_data(conf_node, KEY_LDAP_ADMIN_PASS, RECDB_QSTRING);
5353 nickserv_conf.ldap_admin_pass = str ? str : "";
5354
5355 str = database_get_data(conf_node, KEY_LDAP_FIELD_ACCOUNT, RECDB_QSTRING);
5356 nickserv_conf.ldap_field_account = str ? str : "";
5357
5358 str = database_get_data(conf_node, KEY_LDAP_FIELD_PASSWORD, RECDB_QSTRING);
5359 nickserv_conf.ldap_field_password = str ? str : "";
5360
5361 str = database_get_data(conf_node, KEY_LDAP_FIELD_EMAIL, RECDB_QSTRING);
5362 nickserv_conf.ldap_field_email = str ? str : "";
5363
35ea100f
MB
5364 str = database_get_data(conf_node, KEY_LDAP_FIELD_OSLEVEL, RECDB_QSTRING);
5365 nickserv_conf.ldap_field_oslevel = str ? str : "";
5366
8a729617
AS
5367 str = database_get_data(conf_node, KEY_LDAP_OPER_GROUP_DN, RECDB_QSTRING);
5368 nickserv_conf.ldap_oper_group_dn = str ? str : "";
5369
17d4a698
AS
5370 str = database_get_data(conf_node, KEY_LDAP_OPER_GROUP_LEVEL, RECDB_QSTRING);
5371 nickserv_conf.ldap_oper_group_level = str ? strtoul(str, NULL, 0) : 99;
5372
8a729617
AS
5373 str = database_get_data(conf_node, KEY_LDAP_FIELD_GROUP_MEMBER, RECDB_QSTRING);
5374 nickserv_conf.ldap_field_group_member = str ? str : "";
5375
73d4cc91
AS
5376 free_string_list(nickserv_conf.ldap_object_classes);
5377 strlist = database_get_data(conf_node, KEY_LDAP_OBJECT_CLASSES, RECDB_STRING_LIST);
5378 if(strlist)
5379 strlist = string_list_copy(strlist);
5380 else {
5381 strlist = alloc_string_list(4);
5382 string_list_append(strlist, strdup("top"));
5383 }
5384 nickserv_conf.ldap_object_classes = strlist;
5385
39edf54a 5386#endif
e166c31b 5387
d76ed9a9
AS
5388}
5389
5390static void
5391nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
5392 const char *msg;
5393 char newnick[NICKLEN+1];
5394
5395 assert(user);
5396 assert(ni);
9ef7e8ec
MB
5397
5398 if (IsLocal(user))
5399 return;
5400
d76ed9a9
AS
5401 switch (action) {
5402 case RECLAIM_NONE:
5403 /* do nothing */
5404 break;
5405 case RECLAIM_WARN:
5406 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
72971fc8 5407 send_message(user, nickserv, "NSMSG_RECLAIM_HOWTO", ni->owner->handle, nickserv->nick, self->name, ni->owner->handle);
d76ed9a9
AS
5408 break;
5409 case RECLAIM_SVSNICK:
5410 do {
5411 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
5412 } while (GetUserH(newnick));
5413 irc_svsnick(nickserv, user, newnick);
5414 break;
5415 case RECLAIM_KILL:
5416 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
1136f709 5417 DelUser(user, nickserv, 1, msg);
d76ed9a9
AS
5418 break;
5419 }
5420}
5421
5422static void
5423nickserv_reclaim_p(void *data) {
5424 struct userNode *user = data;
5425 struct nick_info *ni = get_nick_info(user->nick);
5426 if (ni)
5427 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
5428}
5429
5430static int
f0fb2e2d 5431check_user_nick(struct userNode *user, UNUSED_ARG(void *extra)) {
d76ed9a9
AS
5432 struct nick_info *ni;
5433 user->modes &= ~FLAGS_REGNICK;
ba48b70c 5434
d76ed9a9
AS
5435 if (!(ni = get_nick_info(user->nick)))
5436 return 0;
5437 if (user->handle_info == ni->owner) {
5438 user->modes |= FLAGS_REGNICK;
5439 irc_regnick(user);
5440 return 0;
5441 }
5442 if (nickserv_conf.warn_nick_owned)
5443 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
72971fc8 5444 send_message(user, nickserv, "NSMSG_RECLAIM_HOWTO", ni->owner->handle, nickserv->nick, self->name, ni->owner->handle);
d76ed9a9
AS
5445 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
5446 return 0;
5447 if (nickserv_conf.auto_reclaim_delay)
5448 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
5449 else
5450 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
d76ed9a9 5451
1136f709 5452 return 0;
d76ed9a9
AS
5453}
5454
ba48b70c
MB
5455static int
5456new_user_event(struct userNode *user, void *extra) {
5457 /* If the user's server is not bursting,
5458 * the user is authed, the account has autohide set
5459 * and the user doesn't have user mode +x then apply
5460 * the autohide setting.
5461 */
5462 if (!user->uplink->burst && user->handle_info &&
5463 HANDLE_FLAGGED(user->handle_info, AUTOHIDE) &&
5464 !IsHiddenHost(user))
5465 irc_umode(user, "+x");
5466
5467 return check_user_nick(user, extra);
5468}
5469
d76ed9a9
AS
5470void
5471handle_account(struct userNode *user, const char *stamp)
5472{
5473 struct handle_info *hi;
b21e2cfe 5474 char *colon;
d76ed9a9
AS
5475
5476#ifdef WITH_PROTOCOL_P10
a9b5e3de
AS
5477 time_t timestamp = 0;
5478
5479 colon = strchr(stamp, ':');
5480 if(colon && colon[1])
5481 {
5482 *colon = 0;
5483 timestamp = atoi(colon+1);
5484 }
d76ed9a9 5485 hi = dict_find(nickserv_handle_dict, stamp, NULL);
c0e47c86 5486 if(hi && timestamp && hi->registered != timestamp)
a9b5e3de 5487 {
4cb36ef0 5488 log_module(MAIN_LOG, LOG_WARNING, "%s using account %s but timestamp does not match %s is not %s.", user->nick, stamp, ctime(&timestamp),
5489ctime(&hi->registered));
a9b5e3de
AS
5490 return;
5491 }
d76ed9a9
AS
5492#else
5493 hi = dict_find(nickserv_id_dict, stamp, NULL);
9b2d838a 5494 log_module(MAIN_LOG, LOG_WARNING, "Using non-P10 code in accounts, not tested at all!");
d76ed9a9
AS
5495#endif
5496
da207c62
MB
5497#ifdef WITH_LDAP
5498 if(!hi && nickserv_conf.ldap_enable && nickserv_conf.ldap_autocreate &&
5499 (ldap_user_exists(stamp) == LDAP_SUCCESS)) {
5500 int rc = 0;
5501 int cont = 1;
5502 char *email = NULL;
5503 char *mask;
5504
5505 /* First attempt to get the email address from LDAP */
5506 if((rc = ldap_get_user_info(stamp, &email) != LDAP_SUCCESS))
5507 if(nickserv_conf.email_required)
5508 cont = 0;
5509
5510 /* Now try to register the handle */
5511 if (cont && (hi = nickserv_register(user, user, stamp, NULL, 1))) {
5512 if(nickserv_conf.default_hostmask)
5513 mask = "*@*";
5514 else
5515 mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
5516
5517 if(mask) {
5518 char* mask_canonicalized = canonicalize_hostmask(strdup(mask));
5519 string_list_append(hi->masks, mask_canonicalized);
5520 }
5521
5522 if(email) {
5523 nickserv_set_email_addr(hi, email);
5524 free(email);
5525 }
5526 }
5527 }
5528#endif
5529
d76ed9a9
AS
5530 if (hi) {
5531 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
5532 return;
5533 }
5534 set_user_handle_info(user, hi, 0);
5535 } else {
5536 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
5537 }
5538}
5539
5540void
63189c10 5541handle_nick_change(struct userNode *user, const char *old_nick, UNUSED_ARG(void *extra))
d76ed9a9
AS
5542{
5543 struct handle_info *hi;
5544
5545 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
5546 dict_remove(nickserv_allow_auth_dict, old_nick);
5547 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
5548 }
5549 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
f0fb2e2d 5550 check_user_nick(user, NULL);
d76ed9a9
AS
5551}
5552
5553void
a6bcc929 5554nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why), UNUSED_ARG(void *extra))
d76ed9a9
AS
5555{
5556 dict_remove(nickserv_allow_auth_dict, user->nick);
5557 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
5558 set_user_handle_info(user, NULL, 0);
5559}
5560
5561static struct modcmd *
5562nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
5563{
5564 if (min_level > 0) {
5565 char buf[16];
5566 sprintf(buf, "%u", min_level);
5567 if (must_be_qualified) {
5568 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
5569 } else {
5570 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
5571 }
5572 } else if (min_level == 0) {
5573 if (must_be_qualified) {
5574 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
5575 } else {
5576 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
5577 }
5578 } else {
5579 if (must_be_qualified) {
5580 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
5581 } else {
5582 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
5583 }
5584 }
5585}
5586
c8b4b87b
MB
5587#define SDFLAG_STALE 0x01 /**< SASL session data is stale, delete on next pass. */
5588
5589struct SASLSession
5590{
5591 struct SASLSession *next;
5592 struct SASLSession *prev;
5593 struct server* source;
5594 char *buf, *p;
5595 int buflen;
5596 char uid[128];
5597 char mech[10];
43c60bf8 5598 char *sslclifp;
d3c48711 5599 char *hostmask;
c8b4b87b
MB
5600 int flags;
5601};
5602
5603struct SASLSession *saslsessions = NULL;
5604
5605void
5606sasl_delete_session(struct SASLSession *session)
5607{
5608 if (!session)
5609 return;
5610
5611 if (session->buf)
5612 free(session->buf);
8dc633d6 5613 session->buf = NULL;
c8b4b87b 5614
43c60bf8
MB
5615 if (session->sslclifp)
5616 free(session->sslclifp);
8dc633d6 5617 session->sslclifp = NULL;
43c60bf8 5618
d3c48711
MB
5619 if (session->hostmask)
5620 free(session->hostmask);
5621 session->hostmask = NULL;
5622
c8b4b87b
MB
5623 if (session->next)
5624 session->next->prev = session->prev;
5625 if (session->prev)
5626 session->prev->next = session->next;
5627 else
5628 saslsessions = session->next;
5629
5630 free(session);
5631}
5632
5633void
5634sasl_delete_stale(UNUSED_ARG(void *data))
5635{
5636 int delcount = 0;
5637 int remcount = 0;
5638 struct SASLSession *sess = NULL;
5639 struct SASLSession *nextsess = NULL;
5640
5641 log_module(NS_LOG, LOG_DEBUG, "SASL: Checking for stale sessions");
5642
5643 for (sess = saslsessions; sess; sess = nextsess)
5644 {
5645 nextsess = sess->next;
5646
5647 if (sess->flags & SDFLAG_STALE)
5648 {
5649 delcount++;
5650 sasl_delete_session(sess);
5651 }
5652 else
5653 {
5654 remcount++;
5655 sess->flags |= SDFLAG_STALE;
5656 }
5657 }
5658
5659 if (delcount)
5660 log_module(NS_LOG, LOG_DEBUG, "SASL: Deleted %d stale sessions, %d remaining", delcount, remcount);
5661 if (remcount)
5662 timeq_add(now + 30, sasl_delete_stale, NULL);
5663}
5664
5665struct SASLSession*
5666sasl_get_session(const char *uid)
5667{
5668 struct SASLSession *sess;
5669
5670 for (sess = saslsessions; sess; sess = sess->next)
5671 {
5672 if (!strncmp(sess->uid, uid, 128))
5673 {
5674 log_module(NS_LOG, LOG_DEBUG, "SASL: Found session for %s", sess->uid);
5675 return sess;
5676 }
5677 }
5678
5679 sess = malloc(sizeof(struct SASLSession));
5680 memset(sess, 0, sizeof(struct SASLSession));
5681
5682 strncpy(sess->uid, uid, 128);
5683
5684 if (!saslsessions)
5685 timeq_add(now + 30, sasl_delete_stale, NULL);
5686
5687 if (saslsessions)
5688 saslsessions->prev = sess;
5689 sess->next = saslsessions;
5690 saslsessions = sess;
5691
5692 log_module(NS_LOG, LOG_DEBUG, "SASL: Created session for %s", sess->uid);
5693 return sess;
5694}
5695
5696void
5697sasl_packet(struct SASLSession *session)
5698{
5699 log_module(NS_LOG, LOG_DEBUG, "SASL: Got packet containing: %s", session->buf);
5700
5701 if (!session->mech[0])
5702 {
5703 log_module(NS_LOG, LOG_DEBUG, "SASL: No mechanism stored yet, using %s", session->buf);
779da1ca
MB
5704 if (strcmp(session->buf, "PLAIN") && (strcmp(session->buf, "EXTERNAL") || !session->sslclifp)) {
5705 if (!session->sslclifp)
5706 irc_sasl(session->source, session->uid, "M", "PLAIN");
5707 else
5708 irc_sasl(session->source, session->uid, "M", "PLAIN,EXTERNAL");
c8b4b87b
MB
5709 irc_sasl(session->source, session->uid, "D", "F");
5710 sasl_delete_session(session);
5711 return;
5712 }
5713
5714 strncpy(session->mech, session->buf, 10);
5715 irc_sasl(session->source, session->uid, "C", "+");
5716 }
779da1ca
MB
5717 else if (!strcmp(session->mech, "EXTERNAL"))
5718 {
5719 char *raw = NULL;
5720 size_t rawlen = 0;
5721 char *authzid = NULL;
5722 struct handle_info *hi = NULL;
5723 static char buffer[256];
5724
5725 base64_decode_alloc(session->buf, session->buflen, &raw, &rawlen);
5726
5727 if (rawlen != 0)
5728 authzid = raw;
5729
5730 log_module(NS_LOG, LOG_DEBUG, "SASL: Checking supplied credentials");
5731
5732 if (!session->sslclifp) {
5733 log_module(NS_LOG, LOG_DEBUG, "SASL: Incomplete credentials supplied");
5734 irc_sasl(session->source, session->uid, "D", "F");
5735 } else {
d3c48711 5736 if (!(hi = loc_auth(session->sslclifp, authzid, NULL, session->hostmask)))
779da1ca
MB
5737 {
5738 log_module(NS_LOG, LOG_DEBUG, "SASL: Invalid credentials supplied");
5739 irc_sasl(session->source, session->uid, "D", "F");
5740 }
5741 else
5742 {
5743 snprintf(buffer, sizeof(buffer), "%s "FMT_TIME_T, hi->handle, hi->registered);
5744 log_module(NS_LOG, LOG_DEBUG, "SASL: Valid credentials supplied");
5745 irc_sasl(session->source, session->uid, "L", buffer);
5746 irc_sasl(session->source, session->uid, "D", "S");
5747 }
5748 }
5749
5750 sasl_delete_session(session);
5751
5752 free(raw);
5753 return;
5754 }
bea3f934 5755 else
c8b4b87b 5756 {
f06fab8a
MB
5757 char *raw = NULL;
5758 size_t rawlen = 0;
c8b4b87b
MB
5759 char *authzid = NULL;
5760 char *authcid = NULL;
5761 char *passwd = NULL;
f06fab8a 5762 char *r = NULL;
c8b4b87b 5763 unsigned int i = 0, c = 0;
f06fab8a 5764 struct handle_info *hi = NULL;
bea3f934 5765 struct handle_info *hii = NULL;
2c4ce77b 5766 static char buffer[256];
c8b4b87b
MB
5767
5768 base64_decode_alloc(session->buf, session->buflen, &raw, &rawlen);
5769
5770 raw = (char *)realloc(raw, rawlen+1);
5771 raw[rawlen] = '\0';
5772
5773 authzid = raw;
5774 r = raw;
5775 for (i=0; i<rawlen; i++)
5776 {
5777 if (!*r++)
5778 {
5779 if (c++)
5780 passwd = r;
5781 else
5782 authcid = r;
5783 }
5784 }
5785
5786 log_module(NS_LOG, LOG_DEBUG, "SASL: Checking supplied credentials");
5787
5788 if (c != 2)
5789 {
5790 log_module(NS_LOG, LOG_DEBUG, "SASL: Incomplete credentials supplied");
5791 irc_sasl(session->source, session->uid, "D", "F");
5792 }
5793 else
5794 {
d3c48711 5795 if (!(hi = loc_auth(session->sslclifp, authcid, passwd, session->hostmask)))
c8b4b87b
MB
5796 {
5797 log_module(NS_LOG, LOG_DEBUG, "SASL: Invalid credentials supplied");
5798 irc_sasl(session->source, session->uid, "D", "F");
5799 }
5800 else
5801 {
bea3f934
MB
5802 if (*authzid && irccasecmp(authzid, authcid) && HANDLE_FLAGGED(hi, IMPERSONATE))
5803 {
5804 hii = hi;
5805 hi = get_handle_info(authzid);
5806 }
5807 if (hi)
5808 {
5809 if (hii)
5810 {
5811 log_module(NS_LOG, LOG_DEBUG, "SASL: %s is ipersonating %s", hii->handle, hi->handle);
5812 snprintf(buffer, sizeof(buffer), "%s "FMT_TIME_T, hii->handle, hii->registered);
5813 irc_sasl(session->source, session->uid, "I", buffer);
5814 }
5815 log_module(NS_LOG, LOG_DEBUG, "SASL: Valid credentials supplied");
5816 snprintf(buffer, sizeof(buffer), "%s "FMT_TIME_T, hi->handle, hi->registered);
5817 irc_sasl(session->source, session->uid, "L", buffer);
5818 irc_sasl(session->source, session->uid, "D", "S");
5819 }
5820 else
5821 {
5822 log_module(NS_LOG, LOG_DEBUG, "SASL: Invalid credentials supplied");
5823 irc_sasl(session->source, session->uid, "D", "F");
5824 }
c8b4b87b
MB
5825 }
5826 }
5827
5828 sasl_delete_session(session);
5829
5830 free(raw);
5831 return;
5832 }
5833
5834 /* clear stale state */
5835 session->flags &= ~SDFLAG_STALE;
5836}
5837
5838void
43c60bf8 5839handle_sasl_input(struct server* source ,const char *uid, const char *subcmd, const char *data, const char *ext, UNUSED_ARG(void *extra))
c8b4b87b
MB
5840{
5841 struct SASLSession* sess = sasl_get_session(uid);
5842 int len = strlen(data);
5843
5844 sess->source = source;
5845
5846 if (!strcmp(subcmd, "D"))
5847 {
5848 sasl_delete_session(sess);
5849 return;
5850 }
5851
d3c48711
MB
5852 if (!strcmp(subcmd, "H")) {
5853 log_module(NS_LOG, LOG_DEBUG, "SASL: Storing host mask %s", data);
5854 sess->hostmask = strdup(data);
5855 return ;
5856 }
5857
c8b4b87b
MB
5858 if (strcmp(subcmd, "S") && strcmp(subcmd, "C"))
5859 return;
5860
5861 if (len == 0)
5862 return;
5863
5864 if (sess->p == NULL)
5865 {
5866 sess->buf = (char *)malloc(len + 1);
5867 sess->p = sess->buf;
5868 sess->buflen = len;
5869 }
5870 else
5871 {
5872 if (sess->buflen + len + 1 > 8192) /* This is a little much... */
5873 {
5874 irc_sasl(source, uid, "D", "F");
5875 sasl_delete_session(sess);
5876 return;
5877 }
5878
5879 sess->buf = (char *)realloc(sess->buf, sess->buflen + len + 1);
5880 sess->p = sess->buf + sess->buflen;
5881 sess->buflen += len;
5882 }
5883
5884 memcpy(sess->p, data, len);
f9315af0 5885 sess->buf[len] = '\0';
c8b4b87b 5886
43c60bf8
MB
5887 if (ext != NULL)
5888 sess->sslclifp = strdup(ext);
5889
c8b4b87b
MB
5890 /* Messages not exactly 400 bytes are the end of a packet. */
5891 if(len < 400)
5892 {
5893 sasl_packet(sess);
5894 sess->buflen = 0;
8dc633d6
MB
5895 if (sess->buf != NULL)
5896 free(sess->buf);
c8b4b87b
MB
5897 sess->buf = sess->p = NULL;
5898 }
5899}
5900
d76ed9a9 5901static void
30874d66 5902nickserv_db_cleanup(UNUSED_ARG(void* extra))
d76ed9a9 5903{
a6bcc929 5904 unreg_del_user_func(nickserv_remove_user, NULL);
c8b4b87b 5905 unreg_sasl_input_func(handle_sasl_input, NULL);
d76ed9a9
AS
5906 userList_clean(&curr_helpers);
5907 policer_params_delete(nickserv_conf.auth_policer_params);
5908 dict_delete(nickserv_handle_dict);
5909 dict_delete(nickserv_nick_dict);
5910 dict_delete(nickserv_opt_dict);
5911 dict_delete(nickserv_allow_auth_dict);
5912 dict_delete(nickserv_email_dict);
5913 dict_delete(nickserv_id_dict);
5914 dict_delete(nickserv_conf.weak_password_dict);
5915 free(auth_func_list);
81ac4787 5916 free(auth_func_list_extra);
d76ed9a9 5917 free(unreg_func_list);
974d3831 5918 free(unreg_func_list_extra);
d76ed9a9 5919 free(rf_list);
3070719a 5920 free(rf_list_extra);
d76ed9a9 5921 free(allowauth_func_list);
99c332f8 5922 free(allowauth_func_list_extra);
d76ed9a9 5923 free(handle_merge_func_list);
50dafce8 5924 free(handle_merge_func_list_extra);
d76ed9a9 5925 free(failpw_func_list);
c8b793cb 5926 free(failpw_func_list_extra);
d76ed9a9
AS
5927 if (nickserv_conf.valid_handle_regex_set)
5928 regfree(&nickserv_conf.valid_handle_regex);
5929 if (nickserv_conf.valid_nick_regex_set)
5930 regfree(&nickserv_conf.valid_nick_regex);
5931}
5932
81ac4787 5933void handle_loc_auth_oper(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle), UNUSED_ARG(void *extra)) {
c3915bdc 5934 char *privv[MAXNUMPARAMS];
5935 int privc, i;
5936
10be9be0 5937 if (!*nickserv_conf.auto_oper || !user->handle_info)
5938 return;
5939
5940 if (!IsOper(user)) {
5941 if (*nickserv_conf.auto_admin && user->handle_info->opserv_level >= opserv_conf_admin_level()) {
c3915bdc 5942 if (nickserv_conf.auto_admin_privs[0]) {
5943 irc_raw_privs(user, nickserv_conf.auto_admin_privs);
5944 privc = split_line(strdup(nickserv_conf.auto_admin_privs), false, MAXNUMPARAMS, privv);
5945 for (i = 0; i < privc; i++) {
5946 client_modify_priv_by_name(user, privv[i], 1);
5947 }
5948 }
10be9be0 5949 irc_umode(user, nickserv_conf.auto_admin);
5950 irc_sno(0x1, "%s (%s@%s) is now an IRC Administrator",
5951 user->nick, user->ident, user->hostname);
c3915bdc 5952 send_message(user, nickserv, "NSMSG_AUTO_OPER_ADMIN");
10be9be0 5953 } else if (*nickserv_conf.auto_oper && user->handle_info->opserv_level) {
c3915bdc 5954 if (nickserv_conf.auto_oper_privs[0]) {
5955 irc_raw_privs(user, nickserv_conf.auto_oper_privs);
5956 privc = split_line(strdup(nickserv_conf.auto_oper_privs), false, MAXNUMPARAMS, privv);
5957 for (i = 0; i < privc; i++) {
5958 client_modify_priv_by_name(user, privv[i], 1);
5959 }
5960 }
10be9be0 5961 irc_umode(user, nickserv_conf.auto_oper);
5962 irc_sno(0x1, "%s (%s@%s) is now an IRC Operator",
5963 user->nick, user->ident, user->hostname);
c3915bdc 5964 send_message(user, nickserv, "NSMSG_AUTO_OPER");
10be9be0 5965 }
5966 }
5967}
5968
d76ed9a9
AS
5969void
5970init_nickserv(const char *nick)
5971{
7637f48f 5972 struct chanNode *chan;
d76ed9a9
AS
5973 unsigned int i;
5974 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
ba48b70c 5975 reg_new_user_func(new_user_event, NULL);
63189c10 5976 reg_nick_change_func(handle_nick_change, NULL);
a6bcc929 5977 reg_del_user_func(nickserv_remove_user, NULL);
d76ed9a9 5978 reg_account_func(handle_account);
81ac4787 5979 reg_auth_func(handle_loc_auth_oper, NULL);
c8b4b87b 5980 reg_sasl_input_func(handle_sasl_input, NULL);
d76ed9a9
AS
5981
5982 /* set up handle_inverse_flags */
5983 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
5984 for (i=0; handle_flags[i]; i++) {
5985 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
5986 flag_access_levels[i] = 0;
bea3f934
MB
5987 /* ensure flag I requires a minimum of 999 if not set in the config */
5988 if ((unsigned char)handle_flags[i] == 'I')
5989 flag_access_levels[i] = 999;
d76ed9a9
AS
5990 }
5991
5992 conf_register_reload(nickserv_conf_read);
5993 nickserv_opt_dict = dict_new();
5994 nickserv_email_dict = dict_new();
5177fd21 5995
d76ed9a9
AS
5996 dict_set_free_keys(nickserv_email_dict, free);
5997 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
5998
5999 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4048352e
AS
6000/* Removed qualified_host as default requirement for AUTH, REGISTER, PASS, etc. nets
6001 * can enable it per command using modcmd. (its a shitty default IMO, and now in 1.3
6002 * a big pain to disable since its nolonger in the config file. ) -Rubin
6003 */
6004 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+loghostmask", NULL);
d76ed9a9 6005 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4048352e 6006 nickserv_define_func("REGISTER", cmd_register, -1, 0, 0);
d76ed9a9 6007 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4048352e 6008 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 0);
d76ed9a9
AS
6009 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
6010 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
6011 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
6012 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
6013 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
2fa83595
MB
6014 nickserv_define_func("ADDSSLFP", cmd_addsslfp, -1, 1, 0);
6015 nickserv_define_func("OADDSSLFP", cmd_oaddsslfp, 0, 1, 0);
6016 nickserv_define_func("DELSSLFP", cmd_delsslfp, -1, 1, 0);
6017 nickserv_define_func("ODELSSLFP", cmd_odelsslfp, 0, 1, 0);
4048352e 6018 nickserv_define_func("PASS", cmd_pass, -1, 1, 0);
d76ed9a9
AS
6019 nickserv_define_func("SET", cmd_set, -1, 1, 0);
6020 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
6021 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
6022 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
6023 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
6024 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
1136f709 6025 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
d76ed9a9
AS
6026 if (!nickserv_conf.disable_nicks) {
6027 /* nick management commands */
6028 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
6029 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
6030 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
6031 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
6032 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
6033 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
6034 }
6035 if (nickserv_conf.email_enabled) {
6036 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4048352e
AS
6037 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 0);
6038 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 0);
d76ed9a9 6039 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
34938510 6040 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
d76ed9a9
AS
6041 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
6042 }
6043 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
5177fd21 6044 /* ignore commands */
6045 nickserv_define_func("ADDIGNORE", cmd_addignore, -1, 1, 0);
6046 nickserv_define_func("OADDIGNORE", cmd_oaddignore, 0, 1, 0);
6047 nickserv_define_func("DELIGNORE", cmd_delignore, -1, 1, 0);
6048 nickserv_define_func("ODELIGNORE", cmd_odelignore, 0, 1, 0);
d76ed9a9
AS
6049 /* miscellaneous commands */
6050 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
6051 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
6052 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
6053 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
6054 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
1136f709 6055 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
d76ed9a9
AS
6056 /* other options */
6057 dict_insert(nickserv_opt_dict, "INFO", opt_info);
6058 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
6059 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
6060 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
6061 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
7fdb7639 6062 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
338a82b5 6063 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
d76ed9a9
AS
6064 dict_insert(nickserv_opt_dict, "PASS", opt_password);
6065 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
6066 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
6067 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
6068 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
6069 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
0f6fe38c 6070 dict_insert(nickserv_opt_dict, "NOTE", opt_note);
d76ed9a9
AS
6071 if (nickserv_conf.titlehost_suffix) {
6072 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
6073 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
6074 }
0f6fe38c 6075 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
d76ed9a9 6076 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
0b587959 6077 dict_insert(nickserv_opt_dict, "ADVANCED", opt_advanced);
d76ed9a9 6078 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
1136f709 6079 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
d76ed9a9
AS
6080
6081 nickserv_handle_dict = dict_new();
6082 dict_set_free_keys(nickserv_handle_dict, free);
6083 dict_set_free_data(nickserv_handle_dict, free_handle_info);
6084
6085 nickserv_id_dict = dict_new();
6086 dict_set_free_keys(nickserv_id_dict, free);
6087
6088 nickserv_nick_dict = dict_new();
1117fc5a 6089 dict_set_free_data(nickserv_nick_dict, free);
d76ed9a9
AS
6090
6091 nickserv_allow_auth_dict = dict_new();
6092
6093 userList_init(&curr_helpers);
6094
6095 if (nick) {
a32da4c7 6096 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
1136f709 6097 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
d76ed9a9
AS
6098 nickserv_service = service_register(nickserv);
6099 }
6100 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
30874d66 6101 reg_exit_func(nickserv_db_cleanup, NULL);
d76ed9a9
AS
6102 if(nickserv_conf.handle_expire_frequency)
6103 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
ae275267
MB
6104 if(nickserv_conf.nick_expire_frequency && nickserv_conf.expire_nicks)
6105 timeq_add(now + nickserv_conf.nick_expire_frequency, expire_nicks, NULL);
7637f48f 6106
6107 if(autojoin_channels && nickserv) {
6108 for (i = 0; i < autojoin_channels->used; i++) {
6109 chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
6110 AddChannelUser(nickserv, chan)->modes |= MODE_CHANOP;
6111 }
6112 }
c8b4b87b 6113
e166c31b
AS
6114#ifdef WITH_LDAP
6115 ldap_do_init(nickserv_conf);
6116#endif
7637f48f 6117
d76ed9a9
AS
6118 message_register_table(msgtab);
6119}