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