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