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