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