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