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