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