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