]> jfr.im git - irc/evilnet/x3.git/blame - src/nickserv.c
fix a couple oversights in building ldap add structs
[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{
2194 int pw_arg, used, maxlogins;
2195 struct handle_info *hi;
2196 const char *passwd;
393a3e56 2197 const char *handle;
d76ed9a9 2198 struct userNode *other;
39edf54a 2199#ifdef WITH_LDAP
a5a8a781 2200 int ldap_result = LDAP_OTHER;
39edf54a 2201 char *email = NULL;
2202#endif
d76ed9a9 2203
2204 if (user->handle_info) {
2205 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2206 return 0;
2207 }
2208 if (IsStamped(user)) {
2209 /* Unauthenticated users might still have been stamped
2210 previously and could therefore have a hidden host;
2211 do not allow them to authenticate. */
2212 reply("NSMSG_STAMPED_AUTH");
2213 return 0;
2214 }
2215 if (argc == 3) {
393a3e56 2216 passwd = argv[2];
2217 handle = argv[1];
fe08d345 2218 pw_arg = 2;
d76ed9a9 2219 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
393a3e56 2220 } else if (argc == 2) {
2221 passwd = argv[1];
fe08d345 2222 pw_arg = 1;
d76ed9a9 2223 if (nickserv_conf.disable_nicks) {
393a3e56 2224 hi = get_handle_info(user->nick);
d76ed9a9 2225 } else {
2226 /* try to look up their handle from their nick */
e166c31b 2227 /* TODO: handle ldap auth on nickserv style networks, too */
d76ed9a9 2228 struct nick_info *ni;
2229 ni = get_nick_info(user->nick);
2230 if (!ni) {
2231 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
2232 return 0;
2233 }
2234 hi = ni->owner;
2235 }
393a3e56 2236 if (hi) {
2237 handle = hi->handle;
2238 } else {
2239 handle = user->nick;
2240 }
d76ed9a9 2241 } else {
2242 reply("MSG_MISSING_PARAMS", argv[0]);
b1bf690d 2243 svccmd_send_help_brief(user, nickserv, cmd);
d76ed9a9 2244 return 0;
2245 }
393a3e56 2246
2247#ifdef WITH_LDAP
2248 if(strchr(argv[1], '<') || strchr(handle, '>')) {
2249 reply("NSMSG_NO_ANGLEBRACKETS");
2250 return 0;
2251 }
e7fe8840 2252 if (!is_valid_handle(handle)) {
393a3e56 2253 reply("NSMSG_BAD_HANDLE", handle);
2254 return 0;
2255 }
2256
2257 if(nickserv_conf.ldap_enable) {
2258 ldap_result = ldap_check_auth(handle, passwd);
2259 /* Get the users email address and update it */
2260 if(ldap_result == LDAP_SUCCESS) {
2261 int rc;
2262 if((rc = ldap_get_user_info(handle, &email) != LDAP_SUCCESS))
2263 {
2264 if(nickserv_conf.email_required) {
2265 reply("NSMSG_LDAP_FAIL_GET_EMAIL", ldap_err2string(rc));
2266 return 0;
2267 }
2268 }
2269 }
2270 else if(ldap_result != LDAP_INVALID_CREDENTIALS) {
2271 reply("NSMSG_LDAP_FAIL", ldap_err2string(ldap_result));
2272 return 0;
2273 }
2274 }
2275#endif
2276
d76ed9a9 2277 if (!hi) {
39edf54a 2278#ifdef WITH_LDAP
a5a8a781 2279 if(nickserv_conf.ldap_enable && ldap_result == LDAP_SUCCESS && nickserv_conf.ldap_autocreate) {
e166c31b 2280 /* user not found, but authed to ldap successfully..
2281 * create the account.
e166c31b 2282 */
39edf54a 2283 char *mask;
2284 if(!(hi = nickserv_register(user, NULL, argv[1], argv[2], 0))) {
2285 reply("NSMSG_UNABLE_TO_ADD");
2286 return 0; /* couldn't add the user for some reason */
2287 }
2288 /* Add a *@* mask */
2289 if(nickserv_conf.default_hostmask)
2290 mask = "*@*";
2291 else
2292 mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2293
2294 if(mask) {
2295 char* mask_canonicalized = canonicalize_hostmask(strdup(mask));
2296 string_list_append(hi->masks, mask_canonicalized);
2297 }
2298 if(email) {
2299 nickserv_set_email_addr(hi, email);
8dc06852 2300 free(email);
39edf54a 2301 }
2302 if(nickserv_conf.sync_log)
2303 SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, email ? email : "@", user->info);
e166c31b 2304 }
39edf54a 2305 else {
2306#endif
2307 reply("NSMSG_HANDLE_NOT_FOUND");
2308 return 0;
2309#ifdef WITH_LDAP
2310 }
2311#endif
d76ed9a9 2312 }
2313 /* Responses from here on look up the language used by the handle they asked about. */
d76ed9a9 2314 if (!valid_user_for(user, hi)) {
2315 if (hi->email_addr && nickserv_conf.email_enabled)
2316 send_message_type(4, user, cmd->parent->bot,
2317 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
2318 hi->handle);
2319 else
2320 send_message_type(4, user, cmd->parent->bot,
2321 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
2322 hi->handle);
2323 argv[pw_arg] = "BADMASK";
2324 return 1;
2325 }
e166c31b 2326#ifdef WITH_LDAP
2fa83595 2327 if(( ( nickserv_conf.ldap_enable && ldap_result == LDAP_INVALID_CREDENTIALS ) ||
2328 ( (!nickserv_conf.ldap_enable) && (!checkpass(passwd, hi->passwd)) ) ) && !valid_user_sslfp(user, hi)) {
e166c31b 2329#else
2fa83595 2330 if (!checkpass(passwd, hi->passwd) && !valid_user_sslfp(user, hi)) {
e166c31b 2331#endif
d76ed9a9 2332 unsigned int n;
2333 send_message_type(4, user, cmd->parent->bot,
2334 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
2335 argv[pw_arg] = "BADPASS";
c8b793cb 2336 for (n=0; n<failpw_func_used; n++)
2337 failpw_func_list[n](user, hi, failpw_func_list_extra[n]);
d76ed9a9 2338 if (nickserv_conf.autogag_enabled) {
2339 if (!user->auth_policer.params) {
2340 user->auth_policer.last_req = now;
2341 user->auth_policer.params = nickserv_conf.auth_policer_params;
2342 }
2343 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
2344 char *hostmask;
2345 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
2346 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
2347 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
2348 free(hostmask);
2349 argv[pw_arg] = "GAGGED";
2350 }
2351 }
2352 return 1;
2353 }
2354 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2355 send_message_type(4, user, cmd->parent->bot,
2356 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
2357 argv[pw_arg] = "SUSPENDED";
2358 return 1;
2359 }
2360 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2361 for (used = 0, other = hi->users; other; other = other->next_authed) {
2362 if (++used >= maxlogins) {
2363 send_message_type(4, user, cmd->parent->bot,
2364 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
2365 maxlogins);
2366 argv[pw_arg] = "MAXLOGINS";
2367 return 1;
2368 }
2369 }
2370
2371 set_user_handle_info(user, hi, 1);
2372 if (nickserv_conf.email_required && !hi->email_addr)
2373 reply("NSMSG_PLEASE_SET_EMAIL");
2374 if (!is_secure_password(hi->handle, passwd, NULL))
2375 reply("NSMSG_WEAK_PASSWORD");
2376 if (hi->passwd[0] != '$')
2377 cryptpass(passwd, hi->passwd);
ac3bdc8d 2378
5a1daaab 2379 /* If a channel was waiting for this user to auth,
2380 * finish adding them */
ac3bdc8d 2381 process_adduser_pending(user);
5a1daaab 2382
0f6fe38c 2383 reply("NSMSG_AUTH_SUCCESS");
2384
2385
7fdb7639 2386 /* Set +x if autohide is on */
2387 if(HANDLE_FLAGGED(hi, AUTOHIDE))
2388 irc_umode(user, "+x");
2389
1136f709 2390 if (!hi->masks->used) {
2391 irc_in_addr_t ip;
2392 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
2393 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
2394 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
2395 }
2396
c3915bdc 2397 /* Wipe out the pass for the logs */
d76ed9a9 2398 argv[pw_arg] = "****";
2399 return 1;
2400}
2401
2402static allowauth_func_t *allowauth_func_list;
99c332f8 2403static void **allowauth_func_list_extra;
d76ed9a9 2404static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
2405
2406void
99c332f8 2407reg_allowauth_func(allowauth_func_t func, void *extra)
d76ed9a9 2408{
2409 if (allowauth_func_used == allowauth_func_size) {
2410 if (allowauth_func_size) {
2411 allowauth_func_size <<= 1;
2412 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
99c332f8 2413 allowauth_func_list_extra = realloc(allowauth_func_list_extra, allowauth_func_size*sizeof(void*));
d76ed9a9 2414 } else {
2415 allowauth_func_size = 8;
2416 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
99c332f8 2417 allowauth_func_list_extra = malloc(allowauth_func_size*sizeof(void*));
d76ed9a9 2418 }
2419 }
99c332f8 2420 allowauth_func_list[allowauth_func_used] = func;
2421 allowauth_func_list_extra[allowauth_func_used++] = extra;
d76ed9a9 2422}
2423
2424static NICKSERV_FUNC(cmd_allowauth)
2425{
2426 struct userNode *target;
2427 struct handle_info *hi;
2428 unsigned int n;
2429
2430 NICKSERV_MIN_PARMS(2);
2431 if (!(target = GetUserH(argv[1]))) {
2432 reply("MSG_NICK_UNKNOWN", argv[1]);
2433 return 0;
2434 }
2435 if (target->handle_info) {
2436 reply("NSMSG_USER_PREV_AUTH", target->nick);
2437 return 0;
2438 }
2439 if (IsStamped(target)) {
2440 /* Unauthenticated users might still have been stamped
2441 previously and could therefore have a hidden host;
2442 do not allow them to authenticate to an account. */
2443 reply("NSMSG_USER_PREV_STAMP", target->nick);
2444 return 0;
2445 }
2446 if (argc == 2)
2447 hi = NULL;
2448 else if (!(hi = get_handle_info(argv[2]))) {
2449 reply("MSG_HANDLE_UNKNOWN", argv[2]);
2450 return 0;
2451 }
2452 if (hi) {
2453 if (hi->opserv_level > user->handle_info->opserv_level) {
2454 reply("MSG_USER_OUTRANKED", hi->handle);
2455 return 0;
2456 }
2457 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
2458 || (hi->opserv_level > 0))
2459 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
2460 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
2461 return 0;
2462 }
2463 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
2464 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
2465 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
2466 if (nickserv_conf.email_enabled)
2467 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
2468 } else {
2469 if (dict_remove(nickserv_allow_auth_dict, target->nick))
2470 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
2471 else
2472 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
2473 }
2474 for (n=0; n<allowauth_func_used; n++)
99c332f8 2475 allowauth_func_list[n](user, target, hi, allowauth_func_list_extra[n]);
d76ed9a9 2476 return 1;
2477}
2478
2479static NICKSERV_FUNC(cmd_authcookie)
2480{
2481 struct handle_info *hi;
2482
2483 NICKSERV_MIN_PARMS(2);
2484 if (user->handle_info) {
2485 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2486 return 0;
2487 }
2488 if (IsStamped(user)) {
2489 /* Unauthenticated users might still have been stamped
2490 previously and could therefore have a hidden host;
2491 do not allow them to authenticate to an account. */
2492 reply("NSMSG_STAMPED_AUTHCOOKIE");
2493 return 0;
2494 }
2495 if (!(hi = get_handle_info(argv[1]))) {
2496 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2497 return 0;
2498 }
2499 if (!hi->email_addr) {
2500 reply("MSG_SET_EMAIL_ADDR");
2501 return 0;
2502 }
1c0d5243 2503 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL, 0);
d76ed9a9 2504 return 1;
2505}
2506
2507static NICKSERV_FUNC(cmd_delcookie)
2508{
2509 struct handle_info *hi;
2510
2511 hi = user->handle_info;
2512 if (!hi->cookie) {
2513 reply("NSMSG_NO_COOKIE");
2514 return 0;
2515 }
2516 switch (hi->cookie->type) {
2517 case ACTIVATION:
2518 case EMAIL_CHANGE:
2519 reply("NSMSG_MUST_TIME_OUT");
2520 break;
2521 default:
2522 nickserv_eat_cookie(hi->cookie);
2523 reply("NSMSG_ATE_COOKIE");
2524 break;
2525 }
2526 return 1;
2527}
2528
34938510 2529static NICKSERV_FUNC(cmd_odelcookie)
2530{
2531 struct handle_info *hi;
2532
2533 NICKSERV_MIN_PARMS(2);
2534
1136f709 2535 if (!(hi = get_victim_oper(user, argv[1])))
34938510 2536 return 0;
2537
2538 if (!hi->cookie) {
2539 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
2540 return 0;
2541 }
2542
eca6aa4f 2543 switch (hi->cookie->type) {
2544 case ACTIVATION:
2545 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
29545775 2546#ifdef WITH_LDAP
2547 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
2548 int rc;
2549 if((rc = ldap_do_modify(hi->handle, hi->cookie->data, NULL)) != LDAP_SUCCESS) {
2550 /* Falied to update password in ldap, but still
2551 * updated it here.. what should we do? */
2552 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
2553 return 0;
2554 }
2555 }
2556#endif
eca6aa4f 2557 if (nickserv_conf.sync_log)
2558 SyncLog("ACCOUNTACC %s", hi->handle);
2559 break;
2560 case PASSWORD_CHANGE:
eca6aa4f 2561 break;
2562 case EMAIL_CHANGE:
eca6aa4f 2563 break;
29545775 2564 case ALLOWAUTH:
2565 break;
eca6aa4f 2566 default:
2567 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2568 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2569 break;
2570 }
2571
34938510 2572 nickserv_eat_cookie(hi->cookie);
2573 reply("NSMSG_ATE_FOREIGN_COOKIE", hi->handle);
2574
2575 return 1;
2576}
2577
d76ed9a9 2578static NICKSERV_FUNC(cmd_resetpass)
2579{
2580 struct handle_info *hi;
2581 char crypted[MD5_CRYPT_LENGTH];
1c0d5243 2582 int weblink;
d76ed9a9 2583
2584 NICKSERV_MIN_PARMS(3);
1c0d5243 2585 if(argc >= 4 && !strcmp(argv[3], "WEBLINK"))
2586 weblink = 1;
2587 else
2588 weblink = 0;
d76ed9a9 2589 if (user->handle_info) {
2590 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2591 return 0;
2592 }
2593 if (IsStamped(user)) {
2594 /* Unauthenticated users might still have been stamped
2595 previously and could therefore have a hidden host;
2596 do not allow them to activate an account. */
2597 reply("NSMSG_STAMPED_RESETPASS");
2598 return 0;
2599 }
2600 if (!(hi = get_handle_info(argv[1]))) {
2601 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2602 return 0;
2603 }
2604 if (!hi->email_addr) {
2605 reply("MSG_SET_EMAIL_ADDR");
2606 return 0;
2607 }
2608 cryptpass(argv[2], crypted);
2609 argv[2] = "****";
1c0d5243 2610 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted, weblink);
d76ed9a9 2611 return 1;
2612}
2613
2614static NICKSERV_FUNC(cmd_cookie)
2615{
2616 struct handle_info *hi;
2617 const char *cookie;
2618
2619 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
2620 cookie = argv[1];
2621 } else {
2622 NICKSERV_MIN_PARMS(3);
2623 if (!(hi = get_handle_info(argv[1]))) {
2624 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2625 return 0;
2626 }
2627 cookie = argv[2];
2628 }
2629
2630 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2631 reply("NSMSG_HANDLE_SUSPENDED");
2632 return 0;
2633 }
2634
2635 if (!hi->cookie) {
2636 reply("NSMSG_NO_COOKIE");
2637 return 0;
2638 }
2639
2640 /* Check validity of operation before comparing cookie to
2641 * prohibit guessing by authed users. */
2642 if (user->handle_info
2643 && (hi->cookie->type != EMAIL_CHANGE)
2644 && (hi->cookie->type != PASSWORD_CHANGE)) {
2645 reply("NSMSG_CANNOT_COOKIE");
2646 return 0;
2647 }
2648
2649 if (strcmp(cookie, hi->cookie->cookie)) {
2650 reply("NSMSG_BAD_COOKIE");
2651 return 0;
2652 }
2653
2654 switch (hi->cookie->type) {
2655 case ACTIVATION:
d6ef86e3 2656#ifdef WITH_LDAP
2657 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
2658 int rc;
2659 if((rc = ldap_do_modify(hi->handle, hi->cookie->data, NULL)) != LDAP_SUCCESS) {
2660 /* Falied to update email in ldap, but still
2661 * updated it here.. what should we do? */
2662 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
2663 return 0;
2664 }
2665 }
2666#endif
d76ed9a9 2667 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2668 set_user_handle_info(user, hi, 1);
2669 reply("NSMSG_HANDLE_ACTIVATED");
8dc1d9ae 2670 if (nickserv_conf.sync_log)
2671 SyncLog("ACCOUNTACC %s", hi->handle);
d76ed9a9 2672 break;
2673 case PASSWORD_CHANGE:
d6ef86e3 2674#ifdef WITH_LDAP
2675 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
2676 int rc;
2677 if((rc = ldap_do_modify(hi->handle, hi->cookie->data, NULL)) != LDAP_SUCCESS) {
2678 /* Falied to update email in ldap, but still
2679 * updated it here.. what should we do? */
2680 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
2681 return 0;
2682 }
2683 }
2684#endif
d76ed9a9 2685 set_user_handle_info(user, hi, 1);
2686 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2687 reply("NSMSG_PASSWORD_CHANGED");
8dc1d9ae 2688 if (nickserv_conf.sync_log)
2689 SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
d76ed9a9 2690 break;
2691 case EMAIL_CHANGE:
24e9e6c3 2692#ifdef WITH_LDAP
2693 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
2694 int rc;
a3ad3ee3 2695 if((rc = ldap_do_modify(hi->handle, NULL, hi->cookie->data)) != LDAP_SUCCESS) {
24e9e6c3 2696 /* Falied to update email in ldap, but still
2697 * updated it here.. what should we do? */
8dc17ddf 2698 reply("NSMSG_LDAP_FAIL_SEND_EMAIL", ldap_err2string(rc));
24e9e6c3 2699 return 0;
2700 }
2701 }
2702#endif
d8d3ee73 2703 if (!hi->email_addr && nickserv_conf.sync_log) {
2704 /*
2705 * This should only happen if an OREGISTER was sent. Require
2706 * email must be enabled! - SiRVulcaN
2707 */
ac5cb8c5 2708 if (nickserv_conf.sync_log)
2709 SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, hi->cookie->data, user->info);
d8d3ee73 2710 }
24e9e6c3 2711
d76ed9a9 2712 nickserv_set_email_addr(hi, hi->cookie->data);
2713 reply("NSMSG_EMAIL_CHANGED");
8dc1d9ae 2714 if (nickserv_conf.sync_log)
2715 SyncLog("EMAILCHANGE %s %s", hi->handle, hi->cookie->data);
d76ed9a9 2716 break;
1136f709 2717 case ALLOWAUTH: {
2718 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
d76ed9a9 2719 set_user_handle_info(user, hi, 1);
1136f709 2720 nickserv_addmask(user, hi, mask);
d76ed9a9 2721 reply("NSMSG_AUTH_SUCCESS");
1136f709 2722 free(mask);
d76ed9a9 2723 break;
1136f709 2724 }
d76ed9a9 2725 default:
2726 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2727 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2728 break;
2729 }
2730
2731 nickserv_eat_cookie(hi->cookie);
2732
a03d6c77 2733 process_adduser_pending(user);
2734
d76ed9a9 2735 return 1;
2736}
2737
2738static NICKSERV_FUNC(cmd_oregnick) {
2739 const char *nick;
2740 struct handle_info *target;
2741 struct nick_info *ni;
2742
2743 NICKSERV_MIN_PARMS(3);
2744 if (!(target = modcmd_get_handle_info(user, argv[1])))
2745 return 0;
2746 nick = argv[2];
2747 if (!is_registerable_nick(nick)) {
2748 reply("NSMSG_BAD_NICK", nick);
2749 return 0;
2750 }
2751 ni = dict_find(nickserv_nick_dict, nick, NULL);
2752 if (ni) {
2753 reply("NSMSG_NICK_EXISTS", nick);
2754 return 0;
2755 }
2756 register_nick(nick, target);
2757 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2758 return 1;
2759}
2760
2761static NICKSERV_FUNC(cmd_regnick) {
2762 unsigned n;
2763 struct nick_info *ni;
2764
2765 if (!is_registerable_nick(user->nick)) {
2766 reply("NSMSG_BAD_NICK", user->nick);
2767 return 0;
2768 }
2769 /* count their nicks, see if it's too many */
2770 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2771 if (n >= nickserv_conf.nicks_per_handle) {
2772 reply("NSMSG_TOO_MANY_NICKS");
2773 return 0;
2774 }
2775 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2776 if (ni) {
2777 reply("NSMSG_NICK_EXISTS", user->nick);
2778 return 0;
2779 }
2780 register_nick(user->nick, user->handle_info);
2781 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2782 return 1;
2783}
2784
2785static NICKSERV_FUNC(cmd_pass)
2786{
2787 struct handle_info *hi;
c814d8cd 2788 char *old_pass, *new_pass;
8dc17ddf 2789 char crypted[MD5_CRYPT_LENGTH+1];
4b8ccfeb 2790#ifdef WITH_LDAP
c814d8cd 2791 int ldap_result;
4b8ccfeb 2792#endif
d76ed9a9 2793
2794 NICKSERV_MIN_PARMS(3);
2795 hi = user->handle_info;
2796 old_pass = argv[1];
2797 new_pass = argv[2];
2798 argv[2] = "****";
2799 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
c814d8cd 2800
2801#ifdef WITH_LDAP
2802 if(nickserv_conf.ldap_enable) {
2803 ldap_result = ldap_check_auth(hi->handle, old_pass);
2804 if(ldap_result != LDAP_SUCCESS) {
2805 if(ldap_result == LDAP_INVALID_CREDENTIALS)
2806 reply("NSMSG_PASSWORD_INVALID");
2807 else
2808 reply("NSMSG_LDAP_FAIL", ldap_err2string(ldap_result));
2809 return 0;
2810 }
2811 }else
2812#endif
d76ed9a9 2813 if (!checkpass(old_pass, hi->passwd)) {
2814 argv[1] = "BADPASS";
2815 reply("NSMSG_PASSWORD_INVALID");
2816 return 0;
2817 }
8dc17ddf 2818 cryptpass(new_pass, crypted);
c814d8cd 2819#ifdef WITH_LDAP
73d4cc91 2820 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
2821 int rc;
8dc17ddf 2822 if((rc = ldap_do_modify(hi->handle, crypted, NULL)) != LDAP_SUCCESS) {
24e9e6c3 2823 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
73d4cc91 2824 return 0;
2825 }
2826 }
2827#endif
8dc17ddf 2828 //cryptpass(new_pass, hi->passwd);
2829 strcpy(hi->passwd, crypted);
8dc1d9ae 2830 if (nickserv_conf.sync_log)
2831 SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
d76ed9a9 2832 argv[1] = "****";
2833 reply("NSMSG_PASS_SUCCESS");
2834 return 1;
2835}
2836
2837static int
1136f709 2838nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
d76ed9a9 2839{
2840 unsigned int i;
2841 char *new_mask = canonicalize_hostmask(strdup(mask));
2842 for (i=0; i<hi->masks->used; i++) {
2843 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1136f709 2844 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
d76ed9a9 2845 free(new_mask);
2846 return 0;
2847 }
2848 }
2849 string_list_append(hi->masks, new_mask);
1136f709 2850 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
d76ed9a9 2851 return 1;
2852}
2853
2854static NICKSERV_FUNC(cmd_addmask)
2855{
2856 if (argc < 2) {
2857 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1136f709 2858 int res = nickserv_addmask(user, user->handle_info, mask);
d76ed9a9 2859 free(mask);
2860 return res;
2861 } else {
2862 if (!is_gline(argv[1])) {
2863 reply("NSMSG_MASK_INVALID", argv[1]);
2864 return 0;
2865 }
1136f709 2866 return nickserv_addmask(user, user->handle_info, argv[1]);
d76ed9a9 2867 }
2868}
2869
2870static NICKSERV_FUNC(cmd_oaddmask)
2871{
2872 struct handle_info *hi;
2873
2874 NICKSERV_MIN_PARMS(3);
1136f709 2875 if (!(hi = get_victim_oper(user, argv[1])))
d76ed9a9 2876 return 0;
1136f709 2877 return nickserv_addmask(user, hi, argv[2]);
d76ed9a9 2878}
2879
2880static int
1136f709 2881nickserv_delmask(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
d76ed9a9 2882{
2883 unsigned int i;
2884 for (i=0; i<hi->masks->used; i++) {
2885 if (!strcmp(del_mask, hi->masks->list[i])) {
2886 char *old_mask = hi->masks->list[i];
1136f709 2887 if (hi->masks->used == 1 && !force) {
c092fcad 2888 reply("NSMSG_DELMASK_NOTLAST");
d76ed9a9 2889 return 0;
2890 }
2891 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
c092fcad 2892 reply("NSMSG_DELMASK_SUCCESS", old_mask);
d76ed9a9 2893 free(old_mask);
2894 return 1;
2895 }
2896 }
c092fcad 2897 reply("NSMSG_DELMASK_NOT_FOUND");
d76ed9a9 2898 return 0;
2899}
2900
2901static NICKSERV_FUNC(cmd_delmask)
2902{
2903 NICKSERV_MIN_PARMS(2);
1136f709 2904 return nickserv_delmask(cmd, user, user->handle_info, argv[1], 0);
d76ed9a9 2905}
2906
2907static NICKSERV_FUNC(cmd_odelmask)
2908{
2909 struct handle_info *hi;
2910 NICKSERV_MIN_PARMS(3);
1136f709 2911 if (!(hi = get_victim_oper(user, argv[1])))
d76ed9a9 2912 return 0;
1136f709 2913 return nickserv_delmask(cmd, user, hi, argv[2], 1);
d76ed9a9 2914}
2915
2fa83595 2916static int
2917nickserv_addsslfp(struct userNode *user, struct handle_info *hi, const char *sslfp)
2918{
2919 unsigned int i;
2920 char *new_sslfp = strdup(sslfp);
2921 for (i=0; i<hi->sslfps->used; i++) {
2922 if (!irccasecmp(new_sslfp, hi->sslfps->list[i])) {
2923 send_message(user, nickserv, "NSMSG_ADDSSLFP_ALREADY", new_sslfp);
2924 free(new_sslfp);
2925 return 0;
2926 }
2927 }
2928 string_list_append(hi->sslfps, new_sslfp);
2929 send_message(user, nickserv, "NSMSG_ADDSSLFP_SUCCESS", new_sslfp);
2930 return 1;
2931}
2932
2933static NICKSERV_FUNC(cmd_addsslfp)
2934{
2935 NICKSERV_MIN_PARMS((user->sslfp ? 1 : 2));
2936 if ((argc < 2) && (user->sslfp)) {
2937 int res = nickserv_addsslfp(user, user->handle_info, user->sslfp);
2938 return res;
2939 } else {
2940 return nickserv_addsslfp(user, user->handle_info, argv[1]);
2941 }
2942}
2943
2944static NICKSERV_FUNC(cmd_oaddsslfp)
2945{
2946 struct handle_info *hi;
2947
2948 NICKSERV_MIN_PARMS(3);
2949 if (!(hi = get_victim_oper(user, argv[1])))
2950 return 0;
2951 return nickserv_addsslfp(user, hi, argv[2]);
2952}
2953
2954static int
2955nickserv_delsslfp(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, const char *del_sslfp)
2956{
2957 unsigned int i;
2958 for (i=0; i<hi->sslfps->used; i++) {
2959 if (!irccasecmp(del_sslfp, hi->sslfps->list[i])) {
2960 char *old_sslfp = hi->sslfps->list[i];
2961 hi->sslfps->list[i] = hi->sslfps->list[--hi->sslfps->used];
2962 reply("NSMSG_DELSSLFP_SUCCESS", old_sslfp);
2963 free(old_sslfp);
2964 return 1;
2965 }
2966 }
2967 reply("NSMSG_DELSSLFP_NOT_FOUND");
2968 return 0;
2969}
2970
2971static NICKSERV_FUNC(cmd_delsslfp)
2972{
2973 NICKSERV_MIN_PARMS((user->sslfp ? 1 : 2));
2974 if ((argc < 2) && (user->sslfp)) {
2975 return nickserv_delsslfp(cmd, user, user->handle_info, user->sslfp);
2976 } else {
2977 return nickserv_delsslfp(cmd, user, user->handle_info, argv[1]);
2978 }
2979}
2980
2981static NICKSERV_FUNC(cmd_odelsslfp)
2982{
2983 struct handle_info *hi;
2984 NICKSERV_MIN_PARMS(3);
2985 if (!(hi = get_victim_oper(user, argv[1])))
2986 return 0;
2987 return nickserv_delsslfp(cmd, user, hi, argv[2]);
2988}
2989
d76ed9a9 2990int
2991nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2992 unsigned int nn, add = 1, pos;
2993 unsigned long added, removed, flag;
2994
2995 for (added=removed=nn=0; str[nn]; nn++) {
2996 switch (str[nn]) {
2997 case '+': add = 1; break;
2998 case '-': add = 0; break;
2999 default:
3000 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
3001 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
3002 return 0;
3003 }
3004 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
3005 /* cheesy avoidance of looking up the flag name.. */
3006 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
3007 return 0;
3008 }
3009 flag = 1 << (pos - 1);
3010 if (add)
3011 added |= flag, removed &= ~flag;
3012 else
3013 removed |= flag, added &= ~flag;
3014 break;
3015 }
3016 }
3017 *padded = added;
3018 *premoved = removed;
3019 return 1;
3020}
3021
3022static int
3023nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
3024{
3025 unsigned long before, after, added, removed;
3026 struct userNode *uNode;
3027
3028 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
3029 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
3030 return 0;
3031 hi->flags = (hi->flags | added) & ~removed;
3032 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
3033
3034 /* Strip helping flag if they're only a support helper and not
3035 * currently in #support. */
3036 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
3037 struct channelList *schannels;
3038 unsigned int ii;
3039 schannels = chanserv_support_channels();
1136f709 3040 for (ii = 0; ii < schannels->used; ++ii)
3041 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
d76ed9a9 3042 break;
1136f709 3043 if (ii == schannels->used)
d76ed9a9 3044 HANDLE_CLEAR_FLAG(hi, HELPING);
3045 }
3046
3047 if (after && !before) {
3048 /* Add user to current helper list. */
3049 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
3050 userList_append(&curr_helpers, uNode);
3051 } else if (!after && before) {
3052 /* Remove user from current helper list. */
3053 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
3054 userList_remove(&curr_helpers, uNode);
3055 }
3056
3057 return 1;
3058}
3059
3060static void
c092fcad 3061set_list(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, int override)
d76ed9a9 3062{
3063 option_func_t *opt;
3064 unsigned int i;
3065 char *set_display[] = {
338a82b5 3066 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
0f6fe38c 3067 "EMAIL", "ANNOUNCEMENTS", "AUTOHIDE", "MAXLOGINS", "LANGUAGE",
0b587959 3068 "FAKEHOST", "TITLE", "EPITHET", "ADVANCED"
d76ed9a9 3069 };
3070
c092fcad 3071 reply("NSMSG_SETTING_LIST");
3072 reply("NSMSG_SETTING_LIST_HEADER");
d76ed9a9 3073
3074 /* Do this so options are presented in a consistent order. */
3075 for (i = 0; i < ArrayLength(set_display); ++i)
3076 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
c092fcad 3077 opt(cmd, user, hi, override, 0, NULL);
3078 reply("NSMSG_SETTING_LIST_END");
d76ed9a9 3079}
3080
3081static NICKSERV_FUNC(cmd_set)
3082{
3083 struct handle_info *hi;
3084 option_func_t *opt;
3085
3086 hi = user->handle_info;
3087 if (argc < 2) {
c092fcad 3088 set_list(cmd, user, hi, 0);
d76ed9a9 3089 return 1;
3090 }
3091 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
3092 reply("NSMSG_INVALID_OPTION", argv[1]);
3093 return 0;
3094 }
c092fcad 3095 return opt(cmd, user, hi, 0, argc-1, argv+1);
d76ed9a9 3096}
3097
3098static NICKSERV_FUNC(cmd_oset)
3099{
3100 struct handle_info *hi;
3101 option_func_t *opt;
3102
3103 NICKSERV_MIN_PARMS(2);
3104
1136f709 3105 if (!(hi = get_victim_oper(user, argv[1])))
d76ed9a9 3106 return 0;
3107
3108 if (argc < 3) {
c092fcad 3109 set_list(cmd, user, hi, 0);
d76ed9a9 3110 return 1;
3111 }
3112
3113 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
3114 reply("NSMSG_INVALID_OPTION", argv[2]);
3115 return 0;
3116 }
3117
c092fcad 3118 return opt(cmd, user, hi, 1, argc-2, argv+2);
d76ed9a9 3119}
3120
3121static OPTION_FUNC(opt_info)
3122{
3123 const char *info;
3124 if (argc > 1) {
3125 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
3126 free(hi->infoline);
3127 hi->infoline = NULL;
3128 } else {
3129 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
3130 }
3131 }
3132
3133 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
c092fcad 3134 reply("NSMSG_SET_INFO", info);
d76ed9a9 3135 return 1;
3136}
3137
3138static OPTION_FUNC(opt_width)
3139{
3140 if (argc > 1)
3141 hi->screen_width = strtoul(argv[1], NULL, 0);
3142
3143 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
3144 hi->screen_width = MIN_LINE_SIZE;
3145 else if (hi->screen_width > MAX_LINE_SIZE)
3146 hi->screen_width = MAX_LINE_SIZE;
3147
c092fcad 3148 reply("NSMSG_SET_WIDTH", hi->screen_width);
d76ed9a9 3149 return 1;
3150}
3151
3152static OPTION_FUNC(opt_tablewidth)
3153{
3154 if (argc > 1)
3155 hi->table_width = strtoul(argv[1], NULL, 0);
3156
3157 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
3158 hi->table_width = MIN_LINE_SIZE;
3159 else if (hi->screen_width > MAX_LINE_SIZE)
3160 hi->table_width = MAX_LINE_SIZE;
3161
c092fcad 3162 reply("NSMSG_SET_TABLEWIDTH", hi->table_width);
d76ed9a9 3163 return 1;
3164}
3165
3166static OPTION_FUNC(opt_color)
3167{
3168 if (argc > 1) {
3169 if (enabled_string(argv[1]))
3170 HANDLE_SET_FLAG(hi, MIRC_COLOR);
3171 else if (disabled_string(argv[1]))
3172 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
3173 else {
c092fcad 3174 reply("MSG_INVALID_BINARY", argv[1]);
d76ed9a9 3175 return 0;
3176 }
3177 }
3178
c092fcad 3179 reply("NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
d76ed9a9 3180 return 1;
3181}
3182
3183static OPTION_FUNC(opt_privmsg)
3184{
3185 if (argc > 1) {
3186 if (enabled_string(argv[1]))
3187 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
3188 else if (disabled_string(argv[1]))
3189 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
3190 else {
c092fcad 3191 reply("MSG_INVALID_BINARY", argv[1]);
d76ed9a9 3192 return 0;
3193 }
3194 }
3195
c092fcad 3196 reply("NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
d76ed9a9 3197 return 1;
3198}
3199
7fdb7639 3200static OPTION_FUNC(opt_autohide)
3201{
3202 if (argc > 1) {
3203 if (enabled_string(argv[1]))
3204 HANDLE_SET_FLAG(hi, AUTOHIDE);
3205 else if (disabled_string(argv[1]))
3206 HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
3207 else {
c092fcad 3208 reply("MSG_INVALID_BINARY", argv[1]);
7fdb7639 3209 return 0;
3210 }
3211 }
3212
c092fcad 3213 reply("NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
7fdb7639 3214 return 1;
3215}
3216
d76ed9a9 3217static OPTION_FUNC(opt_style)
3218{
3219 char *style;
3220
3221 if (argc > 1) {
338a82b5 3222 if (!irccasecmp(argv[1], "Clean"))
3223 hi->userlist_style = HI_STYLE_CLEAN;
3224 else if (!irccasecmp(argv[1], "Advanced"))
3225 hi->userlist_style = HI_STYLE_ADVANCED;
d9896a83 3226 else if (!irccasecmp(argv[1], "Classic"))
3227 hi->userlist_style = HI_STYLE_CLASSIC;
338a82b5 3228 else /* Default to normal */
3229 hi->userlist_style = HI_STYLE_NORMAL;
3230 } /* TODO: give error if unknow style is chosen */
d76ed9a9 3231
3232 switch (hi->userlist_style) {
338a82b5 3233 case HI_STYLE_ADVANCED:
3234 style = "Advanced";
3235 break;
d9896a83 3236 case HI_STYLE_CLASSIC:
3237 style = "Classic";
3238 break;
338a82b5 3239 case HI_STYLE_CLEAN:
3240 style = "Clean";
3241 break;
3242 case HI_STYLE_NORMAL:
3243 default:
3244 style = "Normal";
d76ed9a9 3245 }
3246
c092fcad 3247 reply("NSMSG_SET_STYLE", style);
d76ed9a9 3248 return 1;
3249}
3250
0f6fe38c 3251static OPTION_FUNC(opt_announcements)
3252{
3253 const char *choice;
3254
3255 if (argc > 1) {
3256 if (enabled_string(argv[1]))
3257 hi->announcements = 'y';
3258 else if (disabled_string(argv[1]))
3259 hi->announcements = 'n';
3260 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
3261 hi->announcements = '?';
3262 else {
3263 reply("NSMSG_INVALID_ANNOUNCE", argv[1]);
3264 return 0;
3265 }
3266 }
3267
3268 switch (hi->announcements) {
3269 case 'y': choice = user_find_message(user, "MSG_ON"); break;
3270 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
3271 case '?': choice = "default"; break;
3272 default: choice = "unknown"; break;
3273 }
3274 reply("NSMSG_SET_ANNOUNCEMENTS", choice);
3275 return 1;
3276}
3277
d76ed9a9 3278static OPTION_FUNC(opt_password)
3279{
8dc17ddf 3280 char crypted[MD5_CRYPT_LENGTH+1];
04cb4dfc 3281 if(argc < 2) {
d6ef86e3 3282 return 0;
3283 }
d76ed9a9 3284 if (!override) {
c092fcad 3285 reply("NSMSG_USE_CMD_PASS");
d76ed9a9 3286 return 0;
3287 }
3288
8dc17ddf 3289 cryptpass(argv[1], crypted);
73d4cc91 3290#ifdef WITH_LDAP
3291 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
3292 int rc;
8dc17ddf 3293 if((rc = ldap_do_modify(hi->handle, crypted, NULL)) != LDAP_SUCCESS) {
24e9e6c3 3294 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
73d4cc91 3295 return 0;
3296 }
3297 }
3298#endif
8dc17ddf 3299 strcpy(hi->passwd, crypted);
4ae3fc8b 3300 if (nickserv_conf.sync_log)
3301 SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
3302
c092fcad 3303 reply("NSMSG_SET_PASSWORD", "***");
d76ed9a9 3304 return 1;
3305}
3306
3307static OPTION_FUNC(opt_flags)
3308{
3309 char flags[33];
3310 unsigned int ii, flen;
3311
3312 if (!override) {
c092fcad 3313 reply("MSG_SETTING_PRIVILEGED", argv[0]);
d76ed9a9 3314 return 0;
3315 }
3316
3317 if (argc > 1)
3318 nickserv_apply_flags(user, hi, argv[1]);
3319
3320 for (ii = flen = 0; handle_flags[ii]; ii++)
3321 if (hi->flags & (1 << ii))
3322 flags[flen++] = handle_flags[ii];
3323 flags[flen] = '\0';
3324 if (hi->flags)
c092fcad 3325 reply("NSMSG_SET_FLAGS", flags);
d76ed9a9 3326 else
c092fcad 3327 reply("NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
d76ed9a9 3328 return 1;
3329}
3330
3331static OPTION_FUNC(opt_email)
3332{
3333 if (argc > 1) {
3334 const char *str;
4c26ef3e 3335 if (!valid_email(argv[1])) {
c092fcad 3336 reply("NSMSG_BAD_EMAIL_ADDR");
d76ed9a9 3337 return 0;
3338 }
1136f709 3339 if ((str = mail_prohibited_address(argv[1]))) {
c092fcad 3340 reply("NSMSG_EMAIL_PROHIBITED", argv[1], str);
d76ed9a9 3341 return 0;
3342 }
3343 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
c092fcad 3344 reply("NSMSG_EMAIL_SAME");
d76ed9a9 3345 else if (!override)
1c0d5243 3346 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1], 0);
d76ed9a9 3347 else {
24e9e6c3 3348#ifdef WITH_LDAP
3349 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
3350 int rc;
8dc17ddf 3351 if((rc = ldap_do_modify(hi->handle, NULL, argv[1])) != LDAP_SUCCESS) {
24e9e6c3 3352 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
3353 return 0;
3354 }
3355 }
3356#endif
d76ed9a9 3357 nickserv_set_email_addr(hi, argv[1]);
3358 if (hi->cookie)
3359 nickserv_eat_cookie(hi->cookie);
c092fcad 3360 reply("NSMSG_SET_EMAIL", visible_email_addr(user, hi));
d76ed9a9 3361 }
3362 } else
c092fcad 3363 reply("NSMSG_SET_EMAIL", visible_email_addr(user, hi));
d76ed9a9 3364 return 1;
3365}
3366
3367static OPTION_FUNC(opt_maxlogins)
3368{
3369 unsigned char maxlogins;
3370 if (argc > 1) {
3371 maxlogins = strtoul(argv[1], NULL, 0);
3372 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
c092fcad 3373 reply("NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
d76ed9a9 3374 return 0;
3375 }
3376 hi->maxlogins = maxlogins;
3377 }
3378 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
c092fcad 3379 reply("NSMSG_SET_MAXLOGINS", maxlogins);
d76ed9a9 3380 return 1;
3381}
3382
0b587959 3383static OPTION_FUNC(opt_advanced)
3384{
3385 if (argc > 1) {
3386 if (enabled_string(argv[1]))
3387 HANDLE_SET_FLAG(hi, ADVANCED);
3388 else if (disabled_string(argv[1]))
3389 HANDLE_CLEAR_FLAG(hi, ADVANCED);
3390 else {
c092fcad 3391 reply("MSG_INVALID_BINARY", argv[1]);
0b587959 3392 return 0;
3393 }
3394 }
3395
c092fcad 3396 reply("NSMSG_SET_ADVANCED", user_find_message(user, HANDLE_FLAGGED(hi, ADVANCED) ? "MSG_ON" : "MSG_OFF"));
0b587959 3397 return 1;
3398}
3399
d76ed9a9 3400static OPTION_FUNC(opt_language)
3401{
3402 struct language *lang;
3403 if (argc > 1) {
3404 lang = language_find(argv[1]);
3405 if (irccasecmp(lang->name, argv[1]))
c092fcad 3406 reply("NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
d76ed9a9 3407 hi->language = lang;
3408 }
c092fcad 3409 reply("NSMSG_SET_LANGUAGE", hi->language->name);
d76ed9a9 3410 return 1;
3411}
3412
1136f709 3413static OPTION_FUNC(opt_karma)
3414{
3415 if (!override) {
3416 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3417 return 0;
3418 }
3419
3420 if (argc > 1) {
3421 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
3422 hi->karma += strtoul(argv[1] + 1, NULL, 10);
3423 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
3424 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
3425 } else {
3426 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
3427 }
3428 }
3429
3430 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
3431 return 1;
3432}
3433
8a729617 3434/* Called from opserv from cmd_access */
d76ed9a9 3435int
3436oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
3437 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
3438 return 0;
3439 if ((user->handle_info->opserv_level < target->opserv_level)
3440 || ((user->handle_info->opserv_level == target->opserv_level)
3441 && (user->handle_info->opserv_level < 1000))) {
3442 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
3443 return 0;
3444 }
3445 if ((user->handle_info->opserv_level < new_level)
3446 || ((user->handle_info->opserv_level == new_level)
3447 && (user->handle_info->opserv_level < 1000))) {
3448 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
3449 return 0;
3450 }
3451 if (user->handle_info == target) {
3452 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
3453 return 0;
3454 }
8a729617 3455#ifdef WITH_LDAP
2045ae25 3456 if(nickserv_conf.ldap_enable && *(nickserv_conf.ldap_oper_group_dn) && *(nickserv_conf.ldap_admin_dn)) {
8a729617 3457 int rc;
17d4a698 3458 if(new_level > nickserv_conf.ldap_oper_group_level)
8a729617 3459 rc = ldap_add2group(target->handle, nickserv_conf.ldap_oper_group_dn);
3460 else
3461 rc = ldap_delfromgroup(target->handle, nickserv_conf.ldap_oper_group_dn);
87677bd8 3462 if(rc != LDAP_SUCCESS && rc != LDAP_TYPE_OR_VALUE_EXISTS && rc != LDAP_NO_SUCH_ATTRIBUTE) {
8a729617 3463 send_message(user, bot, "NSMSG_LDAP_FAIL", ldap_err2string(rc));
3464 return 0;
3465 }
3466 }
35ea100f 3467 if(nickserv_conf.ldap_enable && *(nickserv_conf.ldap_field_oslevel) && *(nickserv_conf.ldap_admin_dn)) {
3468 int rc;
f3aff201 3469 if((rc = ldap_do_oslevel(target->handle, new_level, target->opserv_level)) != LDAP_SUCCESS) {
35ea100f 3470 send_message(user, bot, "NSMSG_LDAP_FAIL", ldap_err2string(rc));
3471 return 0;
3472 }
3473 }
8a729617 3474#endif
d76ed9a9 3475 if (target->opserv_level == new_level)
3476 return 0;
3477 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
3478 user->handle_info->handle, target->handle, new_level, target->opserv_level);
3479 target->opserv_level = new_level;
3480 return 1;
3481}
3482
3483static OPTION_FUNC(opt_level)
3484{
3485 int res;
3486
3487 if (!override) {
c092fcad 3488 reply("MSG_SETTING_PRIVILEGED", argv[0]);
d76ed9a9 3489 return 0;
3490 }
3491
3492 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
c092fcad 3493 reply("NSMSG_SET_LEVEL", hi->opserv_level);
d76ed9a9 3494 return res;
3495}
3496
3497static OPTION_FUNC(opt_epithet)
3498{
d76ed9a9 3499 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
180e0971 3500 char *epithet;
56958740 3501 struct userNode *target, *next_un;
3502
180e0971 3503 if (!override) {
c092fcad 3504 reply("MSG_SETTING_PRIVILEGED", argv[0]);
180e0971 3505 return 0;
3506 }
3507
3508 epithet = unsplit_string(argv+1, argc-1, NULL);
3509
d76ed9a9 3510 if (hi->epithet)
3511 free(hi->epithet);
3512 if ((epithet[0] == '*') && !epithet[1])
3513 hi->epithet = NULL;
3514 else
3515 hi->epithet = strdup(epithet);
56958740 3516
3517 for (target = hi->users; target; target = next_un) {
3518 irc_swhois(nickserv, target, hi->epithet);
3519
3520 next_un = target->next_authed;
3521 }
d76ed9a9 3522 }
3523
3524 if (hi->epithet)
c092fcad 3525 reply("NSMSG_SET_EPITHET", hi->epithet);
d76ed9a9 3526 else
c092fcad 3527 reply("NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
d76ed9a9 3528 return 1;
3529}
3530
3531static OPTION_FUNC(opt_title)
3532{
116d100f 3533 char *title;
3534 const char *none;
3535 char *sptr;
d76ed9a9 3536
d76ed9a9 3537 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
180e0971 3538 if (!override) {
c092fcad 3539 reply("MSG_SETTING_PRIVILEGED", argv[0]);
180e0971 3540 return 0;
3541 }
3542
d76ed9a9 3543 title = argv[1];
574bfc14 3544 if(!strcmp(title, "*")) {
3545 free(hi->fakehost);
3546 hi->fakehost = NULL;
d76ed9a9 3547 }
574bfc14 3548 else {
3549 if (strchr(title, '.')) {
116d100f 3550 reply("NSMSG_TITLE_INVALID");
3551 return 0;
3552 }
574bfc14 3553 /* Alphanumeric titles only. */
3554 for(sptr = title; *sptr; sptr++) {
3555 if(!isalnum(*sptr) && *sptr != '-') {
3556 reply("NSMSG_TITLE_INVALID");
3557 return 0;
3558 }
3559 }
3560 if ((strlen(user->handle_info->handle) + strlen(title) +
3561 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
3562 reply("NSMSG_TITLE_TRUNCATED");
3563 return 0;
3564 }
3565 free(hi->fakehost);
d76ed9a9 3566 hi->fakehost = malloc(strlen(title)+2);
3567 hi->fakehost[0] = '.';
3568 strcpy(hi->fakehost+1, title);
3569 }
3570 apply_fakehost(hi);
3571 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
3572 title = hi->fakehost + 1;
bae4525d 3573 else {
3574 /* If theres no title set then the default title will therefore
d9ffe0e7 3575 be the first part of hidden_host in x3.conf, so for
3576 consistency with opt_fakehost we will print this here.
3577 This isnt actually used in P10, its just handled to keep from crashing... */
bae4525d 3578 char *hs, *hidden_suffix, *rest;
3579
3580 hs = conf_get_data("server/hidden_host", RECDB_QSTRING);
3581 hidden_suffix = strdup(hs);
3582
3583 /* Yes we do this twice */
d9ffe0e7 3584 if((rest = strchr(hidden_suffix, '.')))
3585 {
3586 *rest = '\0';
3587 title = hidden_suffix;
3588 }
3589 else
3590 {
3591 /* A lame default if someone configured hidden_host to something lame */
3592 title = strdup("users");
3593 free(hidden_suffix);
3594 }
bae4525d 3595
bae4525d 3596 }
3597
d76ed9a9 3598 if (!title)
116d100f 3599 none = user_find_message(user, "MSG_NONE");
3600 send_message(user, nickserv, "NSMSG_SET_TITLE", title ? title : none);
d76ed9a9 3601 return 1;
3602}
3603
7637f48f 3604int
c092fcad 3605check_vhost(char *vhost, struct userNode *user, struct svccmd *cmd)
7637f48f 3606{
116d100f 3607 unsigned int y;
7637f48f 3608
3609 // check for a dot in the vhost
3610 if(strchr(vhost, '.') == NULL) {
c092fcad 3611 reply("NSMSG_NOT_VALID_FAKEHOST_DOT", vhost);
7637f48f 3612 return 0;
3613 }
3614
3615 // check for a @ in the vhost
3616 if(strchr(vhost, '@') != NULL) {
c092fcad 3617 reply("NSMSG_NOT_VALID_FAKEHOST_AT", vhost);
7637f48f 3618 return 0;
3619 }
3620
3621 // check for denied words, inspired by monk at paki.sex
3622 for(y = 0; y < nickserv_conf.denied_fakehost_words->used; y++) {
3623 if(strstr(vhost, nickserv_conf.denied_fakehost_words->list[y]) != NULL) {
c092fcad 3624 reply("NSMSG_DENIED_FAKEHOST_WORD", vhost, nickserv_conf.denied_fakehost_words->list[y]);
7637f48f 3625 return 0;
3626 }
3627 }
3628
3629 // check for ircu's HOSTLEN length.
3630 if(strlen(vhost) >= HOSTLEN) {
c092fcad 3631 reply("NSMSG_NOT_VALID_FAKEHOST_LEN", vhost);
7637f48f 3632 return 0;
3633 }
3634
bf93ca8d 3635 /* This can be handled by the regex now if desired.
7637f48f 3636 if (vhost[strspn(vhost, "0123456789.")]) {
3637 hostname = vhost + strlen(vhost);
3638 for (depth = 1; depth && (hostname > vhost); depth--) {
3639 hostname--;
3640 while ((hostname > vhost) && (*hostname != '.')) hostname--;
3641 }
3642
bf93ca8d 3643 if (*hostname == '.') hostname++; * advance past last dot we saw *
7637f48f 3644 if(strlen(hostname) > 4) {
c092fcad 3645 reply("NSMSG_NOT_VALID_FAKEHOST_TLD_LEN", vhost);
7637f48f 3646 return 0;
3647 }
3648 }
bf93ca8d 3649 */
3650 /* test either regex or as valid handle */
3651 if (nickserv_conf.valid_fakehost_regex_set) {
3652 int err = regexec(&nickserv_conf.valid_fakehost_regex, vhost, 0, 0, 0);
3653 if (err) {
3654 char buff[256];
3655 buff[regerror(err, &nickserv_conf.valid_fakehost_regex, buff, sizeof(buff))] = 0;
3656 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
3657 }
3658 if(err == REG_NOMATCH) {
3659 reply("NSMSG_NOT_VALID_FAKEHOST_REGEX", vhost);
3660 return 0;
3661 }
3662 }
3663
7637f48f 3664
3665 return 1;
3666}
3667
d76ed9a9 3668static OPTION_FUNC(opt_fakehost)
3669{
3670 const char *fake;
3671
d76ed9a9 3672 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
180e0971 3673 if (!override) {
c092fcad 3674 reply("MSG_SETTING_PRIVILEGED", argv[0]);
180e0971 3675 return 0;
3676 }
3677
d76ed9a9 3678 fake = argv[1];
3679 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
c092fcad 3680 reply("NSMSG_FAKEHOST_INVALID", HOSTLEN);
d76ed9a9 3681 return 0;
3682 }
7637f48f 3683 if (!strcmp(fake, "*")) {
bf93ca8d 3684 if(hi->fakehost) {
3685 free(hi->fakehost);
3686 hi->fakehost = NULL;
3687 }
3688 }
3689 else if (!check_vhost(argv[1], user, cmd)) {
3690 /* check_vhost takes care of error reply */
3691 return 0;
3692 }
3693 else {
3694 if(hi->fakehost)
3695 free(hi->fakehost);
d76ed9a9 3696 hi->fakehost = strdup(fake);
7637f48f 3697 }
d76ed9a9 3698 apply_fakehost(hi);
bf93ca8d 3699 fake = hi->fakehost;
88b0672a 3700 } else
3701 fake = generate_fakehost(hi);
3702
bf93ca8d 3703 /* Tell them we set the host */
d76ed9a9 3704 if (!fake)
3705 fake = user_find_message(user, "MSG_NONE");
c092fcad 3706 reply("NSMSG_SET_FAKEHOST", fake);
d76ed9a9 3707 return 1;
3708}
3709
0f6fe38c 3710static OPTION_FUNC(opt_note)
3711{
3712 if (!override) {
3713 reply("MSG_SETTING_PRIVILEGED", argv[0]);
3714 return 0;
3715 }
3716
3717 if (argc > 1) {
3718 char *text = unsplit_string(argv + 1, argc - 1, NULL);
3719
3720 if (hi->note)
3721 free(hi->note);
3722
3723 if ((text[0] == '*') && !text[1])
3724 hi->note = NULL;
3725 else {
3726 if (!(hi->note = nickserv_add_note(user->handle_info->handle, now, text)))
3727 hi->note = NULL;
3728 }
3729 }
3730
3731 reply("NSMSG_SET_NOTE", hi->note ? hi->note->note : user_find_message(user, "MSG_NONE"));
3732 return 1;
3733}
3734
d76ed9a9 3735static NICKSERV_FUNC(cmd_reclaim)
3736{
3737 struct handle_info *hi;
3738 struct nick_info *ni;
3739 struct userNode *victim;
3740
3741 NICKSERV_MIN_PARMS(2);
3742 hi = user->handle_info;
3743 ni = dict_find(nickserv_nick_dict, argv[1], 0);
3744 if (!ni) {
3745 reply("NSMSG_UNKNOWN_NICK", argv[1]);
3746 return 0;
3747 }
3748 if (ni->owner != user->handle_info) {
3749 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
3750 return 0;
3751 }
3752 victim = GetUserH(ni->nick);
3753 if (!victim) {
3754 reply("MSG_NICK_UNKNOWN", ni->nick);
3755 return 0;
3756 }
3757 if (victim == user) {
3758 reply("NSMSG_NICK_USER_YOU");
3759 return 0;
3760 }
3761 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
3762 switch (nickserv_conf.reclaim_action) {
3763 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
3764 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
3765 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
3766 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
3767 }
3768 return 1;
3769}
3770
3771static NICKSERV_FUNC(cmd_unregnick)
3772{
3773 const char *nick;
3774 struct handle_info *hi;
3775 struct nick_info *ni;
3776
3777 hi = user->handle_info;
3778 nick = (argc < 2) ? user->nick : (const char*)argv[1];
3779 ni = dict_find(nickserv_nick_dict, nick, NULL);
3780 if (!ni) {
3781 reply("NSMSG_UNKNOWN_NICK", nick);
3782 return 0;
3783 }
3784 if (hi != ni->owner) {
3785 reply("NSMSG_NOT_YOUR_NICK", nick);
3786 return 0;
3787 }
3788 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3789 delete_nick(ni);
3790 return 1;
3791}
3792
3793static NICKSERV_FUNC(cmd_ounregnick)
3794{
3795 struct nick_info *ni;
3796
3797 NICKSERV_MIN_PARMS(2);
3798 if (!(ni = get_nick_info(argv[1]))) {
3799 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
3800 return 0;
3801 }
1136f709 3802 if (!oper_outranks(user, ni->owner))
3803 return 0;
d76ed9a9 3804 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3805 delete_nick(ni);
3806 return 1;
3807}
3808
3809static NICKSERV_FUNC(cmd_unregister)
3810{
3811 struct handle_info *hi;
3812 char *passwd;
3813
3814 NICKSERV_MIN_PARMS(2);
3815 hi = user->handle_info;
3816 passwd = argv[1];
3817 argv[1] = "****";
3818 if (checkpass(passwd, hi->passwd)) {
73d4cc91 3819 if(nickserv_unregister_handle(hi, user, cmd->parent->bot))
3820 return 1;
3821 else
3822 return 0;
d76ed9a9 3823 } else {
3824 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3825 reply("NSMSG_PASSWORD_INVALID");
3826 return 0;
3827 }
3828}
3829
3830static NICKSERV_FUNC(cmd_ounregister)
3831{
3832 struct handle_info *hi;
1136f709 3833 char reason[MAXLEN];
3834 int force;
d76ed9a9 3835
3836 NICKSERV_MIN_PARMS(2);
1136f709 3837 if (!(hi = get_victim_oper(user, argv[1])))
d76ed9a9 3838 return 0;
1136f709 3839
3840 if (HANDLE_FLAGGED(hi, NODELETE)) {
3841 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
3842 return 0;
3843 }
3844
3845 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
3846 if (!force &&
3847 ((hi->flags & nickserv_conf.ounregister_flags)
3848 || hi->users
3849 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
3850 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
3851 return 0;
3852 }
3853 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
3854 global_message(MESSAGE_RECIPIENT_STAFF, reason);
73d4cc91 3855 if(nickserv_unregister_handle(hi, user, cmd->parent->bot))
3856 return 1;
3857 else
3858 return 0;
d76ed9a9 3859}
3860
3861static NICKSERV_FUNC(cmd_status)
3862{
3863 if (nickserv_conf.disable_nicks) {
3864 reply("NSMSG_GLOBAL_STATS_NONICK",
3865 dict_size(nickserv_handle_dict));
3866 } else {
3867 if (user->handle_info) {
3868 int cnt=0;
3869 struct nick_info *ni;
3870 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
3871 reply("NSMSG_HANDLE_STATS", cnt);
3872 } else {
3873 reply("NSMSG_HANDLE_NONE");
3874 }
3875 reply("NSMSG_GLOBAL_STATS",
3876 dict_size(nickserv_handle_dict),
3877 dict_size(nickserv_nick_dict));
3878 }
3879 return 1;
3880}
3881
3882static NICKSERV_FUNC(cmd_ghost)
3883{
3884 struct userNode *target;
3885 char reason[MAXLEN];
3886
3887 NICKSERV_MIN_PARMS(2);
3888 if (!(target = GetUserH(argv[1]))) {
3889 reply("MSG_NICK_UNKNOWN", argv[1]);
3890 return 0;
3891 }
3892 if (target == user) {
3893 reply("NSMSG_CANNOT_GHOST_SELF");
3894 return 0;
3895 }
3896 if (!target->handle_info || (target->handle_info != user->handle_info)) {
3897 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
3898 return 0;
3899 }
3900 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
3901 DelUser(target, nickserv, 1, reason);
3902 reply("NSMSG_GHOST_KILLED", argv[1]);
3903 return 1;
3904}
3905
3906static NICKSERV_FUNC(cmd_vacation)
3907{
3908 HANDLE_SET_FLAG(user->handle_info, FROZEN);
3909 reply("NSMSG_ON_VACATION");
3910 return 1;
3911}
3912
3913static int
3914nickserv_saxdb_write(struct saxdb_context *ctx) {
3915 dict_iterator_t it;
3916 struct handle_info *hi;
3917 char flags[33];
3918
3919 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3920 hi = iter_data(it);
d76ed9a9 3921 saxdb_start_record(ctx, iter_key(it), 0);
0f6fe38c 3922 if (hi->announcements != '?') {
3923 flags[0] = hi->announcements;
3924 flags[1] = 0;
3925 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
3926 }
d76ed9a9 3927 if (hi->cookie) {
3928 struct handle_cookie *cookie = hi->cookie;
3929 char *type;
3930
3931 switch (cookie->type) {
3932 case ACTIVATION: type = KEY_ACTIVATION; break;
3933 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3934 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3935 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3936 default: type = NULL; break;
3937 }
3938 if (type) {
3939 saxdb_start_record(ctx, KEY_COOKIE, 0);
3940 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3941 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3942 if (cookie->data)
3943 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3944 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3945 saxdb_end_record(ctx);
3946 }
3947 }
3948 if (hi->email_addr)
3949 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3950 if (hi->epithet)
3951 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
0f6fe38c 3952 if (hi->note) {
3953 saxdb_start_record(ctx, KEY_NOTE_NOTE, 0);
3954 saxdb_write_string(ctx, KEY_NOTE_SETTER, hi->note->setter);
3955 saxdb_write_int(ctx, KEY_NOTE_DATE, hi->note->date);
3956 saxdb_write_string(ctx, KEY_NOTE_NOTE, hi->note->note);
3957 saxdb_end_record(ctx);
3958 }
2362161a 3959
d76ed9a9 3960 if (hi->fakehost)
3961 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3962 if (hi->flags) {
3963 int ii, flen;
3964
3965 for (ii=flen=0; handle_flags[ii]; ++ii)
3966 if (hi->flags & (1 << ii))
3967 flags[flen++] = handle_flags[ii];
3968 flags[flen] = 0;
3969 saxdb_write_string(ctx, KEY_FLAGS, flags);
3970 }
d76ed9a9 3971 if (hi->infoline)
3972 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3973 if (hi->last_quit_host[0])
3974 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3975 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
1136f709 3976 if (hi->karma != 0)
3977 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
d76ed9a9 3978 if (hi->masks->used)
3979 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2fa83595 3980 if (hi->sslfps->used)
3981 saxdb_write_string_list(ctx, KEY_SSLFPS, hi->sslfps);
5177fd21 3982 if (hi->ignores->used)
3983 saxdb_write_string_list(ctx, KEY_IGNORES, hi->ignores);
d76ed9a9 3984 if (hi->maxlogins)
3985 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3986 if (hi->nicks) {
3987 struct string_list *slist;
3988 struct nick_info *ni;
3989
3990 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3991 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3992 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3993 free(slist->list);
3994 free(slist);
3995 }
3996 if (hi->opserv_level)
3997 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3998 if (hi->language != lang_C)
3999 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
4000 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
4001 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
4002 if (hi->screen_width)
4003 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
4004 if (hi->table_width)
4005 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
4006 flags[0] = hi->userlist_style;
4007 flags[1] = 0;
4008 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
4009 saxdb_end_record(ctx);
4010 }
5177fd21 4011
d76ed9a9 4012 return 0;
4013}
4014
4015static handle_merge_func_t *handle_merge_func_list;
50dafce8 4016static void **handle_merge_func_list_extra;
d76ed9a9 4017static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
4018
4019void
50dafce8 4020reg_handle_merge_func(handle_merge_func_t func, void *extra)
d76ed9a9 4021{
4022 if (handle_merge_func_used == handle_merge_func_size) {
4023 if (handle_merge_func_size) {
4024 handle_merge_func_size <<= 1;
4025 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
50dafce8 4026 handle_merge_func_list_extra = realloc(handle_merge_func_list_extra, handle_merge_func_size*sizeof(void*));
d76ed9a9 4027 } else {
4028 handle_merge_func_size = 8;
4029 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
50dafce8 4030 handle_merge_func_list_extra = malloc(handle_merge_func_size*sizeof(void*));
d76ed9a9 4031 }
4032 }
50dafce8 4033 handle_merge_func_list[handle_merge_func_used] = func;
4034 handle_merge_func_list_extra[handle_merge_func_used++] = extra;
d76ed9a9 4035}
4036
4037static NICKSERV_FUNC(cmd_merge)
4038{
4039 struct handle_info *hi_from, *hi_to;
4040 struct userNode *last_user;
4041 struct userData *cList, *cListNext;
4042 unsigned int ii, jj, n;
d76ed9a9 4043
4044 NICKSERV_MIN_PARMS(3);
4045
1136f709 4046 if (!(hi_from = get_victim_oper(user, argv[1])))
d76ed9a9 4047 return 0;
1136f709 4048 if (!(hi_to = get_victim_oper(user, argv[2])))
d76ed9a9 4049 return 0;
4050 if (hi_to == hi_from) {
4051 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
4052 return 0;
4053 }
4054
4055 for (n=0; n<handle_merge_func_used; n++)
50dafce8 4056 handle_merge_func_list[n](user, hi_to, hi_from, handle_merge_func_list_extra[n]);
d76ed9a9 4057
4058 /* Append "from" handle's nicks to "to" handle's nick list. */
4059 if (hi_to->nicks) {
4060 struct nick_info *last_ni;
4061 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
4062 last_ni->next = hi_from->nicks;
4063 }
4064 while (hi_from->nicks) {
4065 hi_from->nicks->owner = hi_to;
4066 hi_from->nicks = hi_from->nicks->next;
4067 }
4068
4069 /* Merge the hostmasks. */
4070 for (ii=0; ii<hi_from->masks->used; ii++) {
4071 char *mask = hi_from->masks->list[ii];
4072 for (jj=0; jj<hi_to->masks->used; jj++)
4073 if (match_ircglobs(hi_to->masks->list[jj], mask))
4074 break;
4075 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
4076 string_list_append(hi_to->masks, strdup(mask));
4077 }
4078
c73514ea 4079 /* Merge the SSL fingerprints. */
4080 for (ii=0; ii<hi_from->sslfps->used; ii++) {
4081 char *sslfp = hi_from->sslfps->list[ii];
4082 for (jj=0; jj<hi_to->sslfps->used; jj++)
4083 if (!irccasecmp(hi_to->sslfps->list[jj], sslfp))
4084 break;
4085 if (jj==hi_to->sslfps->used) /* Nothing from the "to" handle covered this sslfp, so add it. */
4086 string_list_append(hi_to->sslfps, strdup(sslfp));
4087 }
4088
5177fd21 4089 /* Merge the ignores. */
4090 for (ii=0; ii<hi_from->ignores->used; ii++) {
4091 char *ignore = hi_from->ignores->list[ii];
4092 for (jj=0; jj<hi_to->ignores->used; jj++)
4093 if (match_ircglobs(hi_to->ignores->list[jj], ignore))
4094 break;
4095 if (jj==hi_to->ignores->used) /* Nothing from the "to" handle covered this mask, so add it. */
4096 string_list_append(hi_to->ignores, strdup(ignore));
4097 }
4098
d76ed9a9 4099 /* Merge the lists of authed users. */
4100 if (hi_to->users) {
4101 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
4102 last_user->next_authed = hi_from->users;
4103 } else {
4104 hi_to->users = hi_from->users;
4105 }
4106 /* Repoint the old "from" handle's users. */
4107 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
4108 last_user->handle_info = hi_to;
4109 }
4110 hi_from->users = NULL;
4111
4112 /* Merge channel userlists. */
4113 for (cList=hi_from->channels; cList; cList=cListNext) {
4114 struct userData *cList2;
4115 cListNext = cList->u_next;
4116 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
4117 if (cList->channel == cList2->channel)
4118 break;
4119 if (cList2 && (cList2->access >= cList->access)) {
4120 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);
4121 /* keep cList2 in hi_to; remove cList from hi_from */
4122 del_channel_user(cList, 1);
4123 } else {
4124 if (cList2) {
4125 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);
4126 /* remove the lower-ranking cList2 from hi_to */
4127 del_channel_user(cList2, 1);
4128 } else {
4129 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
4130 }
4131 /* cList needs to be moved from hi_from to hi_to */
4132 cList->handle = hi_to;
4133 /* Remove from linked list for hi_from */
4134 assert(!cList->u_prev);
4135 hi_from->channels = cList->u_next;
4136 if (cList->u_next)
4137 cList->u_next->u_prev = cList->u_prev;
4138 /* Add to linked list for hi_to */
4139 cList->u_prev = NULL;
4140 cList->u_next = hi_to->channels;
4141 if (hi_to->channels)
4142 hi_to->channels->u_prev = cList;
4143 hi_to->channels = cList;
4144 }
4145 }
4146
4147 /* Do they get an OpServ level promotion? */
4148 if (hi_from->opserv_level > hi_to->opserv_level)
4149 hi_to->opserv_level = hi_from->opserv_level;
4150
4151 /* What about last seen time? */
4152 if (hi_from->lastseen > hi_to->lastseen)
4153 hi_to->lastseen = hi_from->lastseen;
4154
1136f709 4155 /* New karma is the sum of the two original karmas. */
4156 hi_to->karma += hi_from->karma;
4157
0d16e639 4158 /* Does a fakehost carry over? (This intentionally doesn't set it
4159 * for users previously attached to hi_to. They'll just have to
4160 * reconnect.)
4161 */
4162 if (hi_from->fakehost && !hi_to->fakehost)
4163 hi_to->fakehost = strdup(hi_from->fakehost);
4164
d76ed9a9 4165 /* Notify of success. */
d76ed9a9 4166 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
57692f5e 4167 global_message_args(MESSAGE_RECIPIENT_OPERS, "NSMSG_ACCOUNT_MERGED", user->nick,
4168 user->handle_info->handle, hi_from->handle, hi_to->handle);
d76ed9a9 4169
4170 /* Unregister the "from" handle. */
258d1427 4171 nickserv_unregister_handle(hi_from, NULL, cmd->parent->bot);
73d4cc91 4172 /* TODO: fix it so that if the ldap delete in nickserv_unregister_handle fails,
4173 * the process isn't completed.
4174 */
d76ed9a9 4175
4176 return 1;
4177}
4178
4179struct nickserv_discrim {
d76ed9a9 4180 unsigned long flags_on, flags_off;
4181 time_t min_registered, max_registered;
4182 time_t lastseen;
1136f709 4183 unsigned int limit;
4184 int min_level, max_level;
4185 int min_karma, max_karma;
d76ed9a9 4186 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
4187 const char *nickmask;
4188 const char *hostmask;
4189 const char *handlemask;
4190 const char *emailmask;
b96027ad 4191#ifdef WITH_LDAP
4192 unsigned int inldap;
4193#endif
d76ed9a9 4194};
4195
4196typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
4197
4198struct discrim_apply_info {
4199 struct nickserv_discrim *discrim;
4200 discrim_search_func func;
4201 struct userNode *source;
4202 unsigned int matched;
4203};
4204
4205static struct nickserv_discrim *
c092fcad 4206nickserv_discrim_create(struct svccmd *cmd, struct userNode *user, unsigned int argc, char *argv[])
d76ed9a9 4207{
4208 unsigned int i;
4209 struct nickserv_discrim *discrim;
4210
4211 discrim = malloc(sizeof(*discrim));
4212 memset(discrim, 0, sizeof(*discrim));
4213 discrim->min_level = 0;
1136f709 4214 discrim->max_level = INT_MAX;
d76ed9a9 4215 discrim->limit = 50;
4216 discrim->min_registered = 0;
4217 discrim->max_registered = INT_MAX;
1136f709 4218 discrim->lastseen = LONG_MAX;
4219 discrim->min_karma = INT_MIN;
4220 discrim->max_karma = INT_MAX;
b96027ad 4221#ifdef WITH_LDAP
4222 discrim->inldap = 2;
4223#endif
d76ed9a9 4224
4225 for (i=0; i<argc; i++) {
4226 if (i == argc - 1) {
c092fcad 4227 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9 4228 goto fail;
4229 }
4230 if (!irccasecmp(argv[i], "limit")) {
4231 discrim->limit = strtoul(argv[++i], NULL, 0);
4232 } else if (!irccasecmp(argv[i], "flags")) {
4233 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
4234 } else if (!irccasecmp(argv[i], "registered")) {
4235 const char *cmp = argv[++i];
4236 if (cmp[0] == '<') {
4237 if (cmp[1] == '=') {
4238 discrim->min_registered = now - ParseInterval(cmp+2);
4239 } else {
4240 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
4241 }
4242 } else if (cmp[0] == '=') {
4243 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
4244 } else if (cmp[0] == '>') {
4245 if (cmp[1] == '=') {
4246 discrim->max_registered = now - ParseInterval(cmp+2);
4247 } else {
4248 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
4249 }
4250 } else {
c092fcad 4251 reply("MSG_INVALID_CRITERIA", cmp);
d76ed9a9 4252 }
4253 } else if (!irccasecmp(argv[i], "seen")) {
4254 discrim->lastseen = now - ParseInterval(argv[++i]);
4255 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
4256 discrim->nickmask = argv[++i];
4257 } else if (!irccasecmp(argv[i], "hostmask")) {
4258 i++;
4259 if (!irccasecmp(argv[i], "exact")) {
4260 if (i == argc - 1) {
c092fcad 4261 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9 4262 goto fail;
4263 }
4264 discrim->hostmask_type = EXACT;
4265 } else if (!irccasecmp(argv[i], "subset")) {
4266 if (i == argc - 1) {
c092fcad 4267 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9 4268 goto fail;
4269 }
4270 discrim->hostmask_type = SUBSET;
4271 } else if (!irccasecmp(argv[i], "superset")) {
4272 if (i == argc - 1) {
c092fcad 4273 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9 4274 goto fail;
4275 }
4276 discrim->hostmask_type = SUPERSET;
4277 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
4278 if (i == argc - 1) {
c092fcad 4279 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9 4280 goto fail;
4281 }
4282 discrim->hostmask_type = LASTQUIT;
4283 } else {
4284 i--;
4285 discrim->hostmask_type = SUPERSET;
4286 }
4287 discrim->hostmask = argv[++i];
b96027ad 4288 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask") || !irccasecmp(argv[i], "account")) {
d76ed9a9 4289 if (!irccasecmp(argv[++i], "*")) {
4290 discrim->handlemask = 0;
4291 } else {
4292 discrim->handlemask = argv[i];
4293 }
4294 } else if (!irccasecmp(argv[i], "email")) {
4295 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
c092fcad 4296 reply("MSG_NO_SEARCH_ACCESS", "email");
d76ed9a9 4297 goto fail;
4298 } else if (!irccasecmp(argv[++i], "*")) {
4299 discrim->emailmask = 0;
4300 } else {
4301 discrim->emailmask = argv[i];
4302 }
4303 } else if (!irccasecmp(argv[i], "access")) {
4304 const char *cmp = argv[++i];
4305 if (cmp[0] == '<') {
4306 if (discrim->min_level == 0) discrim->min_level = 1;
4307 if (cmp[1] == '=') {
4308 discrim->max_level = strtoul(cmp+2, NULL, 0);
4309 } else {
4310 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
4311 }
4312 } else if (cmp[0] == '=') {
4313 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
4314 } else if (cmp[0] == '>') {
4315 if (cmp[1] == '=') {
4316 discrim->min_level = strtoul(cmp+2, NULL, 0);
4317 } else {
4318 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
4319 }
4320 } else {
c092fcad 4321 reply("MSG_INVALID_CRITERIA", cmp);
d76ed9a9 4322 }
1136f709 4323 } else if (!irccasecmp(argv[i], "karma")) {
4324 const char *cmp = argv[++i];
4325 if (cmp[0] == '<') {
4326 if (cmp[1] == '=') {
4327 discrim->max_karma = strtoul(cmp+2, NULL, 0);
4328 } else {
4329 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
4330 }
4331 } else if (cmp[0] == '=') {
4332 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
4333 } else if (cmp[0] == '>') {
4334 if (cmp[1] == '=') {
4335 discrim->min_karma = strtoul(cmp+2, NULL, 0);
4336 } else {
4337 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
4338 }
4339 } else {
4340 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
4341 }
d5faccba 4342#ifdef WITH_LDAP
b96027ad 4343 } else if (nickserv_conf.ldap_enable && !irccasecmp(argv[i], "inldap")) {
4344 i++;
4345 if(true_string(argv[i])) {
4346 discrim->inldap = 1;
4347 }
4348 else if (false_string(argv[i])) {
4349 discrim->inldap = 0;
4350 }
4351 else {
4352 reply("MSG_INVALID_BINARY", argv[i]);
4353 }
d5faccba 4354#endif
b96027ad 4355 } else {
c092fcad 4356 reply("MSG_INVALID_CRITERIA", argv[i]);
d76ed9a9 4357 goto fail;
4358 }
4359 }
4360 return discrim;
4361 fail:
4362 free(discrim);
4363 return NULL;
4364}
4365
4366static int
4367nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
4368{
4369 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
4370 || (discrim->flags_off & hi->flags)
4371 || (discrim->min_registered > hi->registered)
4372 || (discrim->max_registered < hi->registered)
4373 || (discrim->lastseen < (hi->users?now:hi->lastseen))
4374 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
4375 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
4376 || (discrim->min_level > hi->opserv_level)
1136f709 4377 || (discrim->max_level < hi->opserv_level)
4378 || (discrim->min_karma > hi->karma)
4379 || (discrim->max_karma < hi->karma)
4380 ) {
d76ed9a9 4381 return 0;
4382 }
4383 if (discrim->hostmask) {
4384 unsigned int i;
4385 for (i=0; i<hi->masks->used; i++) {
4386 const char *mask = hi->masks->list[i];
4387 if ((discrim->hostmask_type == SUBSET)
4388 && (match_ircglobs(discrim->hostmask, mask))) break;
4389 else if ((discrim->hostmask_type == EXACT)
4390 && !irccasecmp(discrim->hostmask, mask)) break;
4391 else if ((discrim->hostmask_type == SUPERSET)
4392 && (match_ircglobs(mask, discrim->hostmask))) break;
4393 else if ((discrim->hostmask_type == LASTQUIT)
4394 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
4395 }
4396 if (i==hi->masks->used) return 0;
4397 }
4398 if (discrim->nickmask) {
4399 struct nick_info *nick = hi->nicks;
4400 while (nick) {
4401 if (match_ircglob(nick->nick, discrim->nickmask)) break;
4402 nick = nick->next;
4403 }
4404 if (!nick) return 0;
4405 }
b96027ad 4406#ifdef WITH_LDAP
4407 if(nickserv_conf.ldap_enable && discrim->inldap != 2) {
4408 int rc;
4409 rc = ldap_get_user_info(hi->handle, NULL);
4410 if(discrim->inldap == 1 && rc != LDAP_SUCCESS)
4411 return 0;
4412 if(discrim->inldap == 0 && rc == LDAP_SUCCESS)
4413 return 0;
4414 }
4415
4416#endif
d76ed9a9 4417 return 1;
4418}
4419
4420static unsigned int
4421nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
4422{
4423 dict_iterator_t it, next;
4424 unsigned int matched;
4425
4426 for (it = dict_first(nickserv_handle_dict), matched = 0;
4427 it && (matched < discrim->limit);
4428 it = next) {
4429 next = iter_next(it);
4430 if (nickserv_discrim_match(discrim, iter_data(it))) {
4431 dsf(source, iter_data(it));
4432 matched++;
4433 }
4434 }
4435 return matched;
4436}
4437
4438static void
4439search_print_func(struct userNode *source, struct handle_info *match)
4440{
4441 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
4442}
4443
4444static void
4445search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
4446{
4447}
4448
4449static void
4450search_unregister_func (struct userNode *source, struct handle_info *match)
4451{
4452 if (oper_has_access(source, nickserv, match->opserv_level, 0))
258d1427 4453 nickserv_unregister_handle(match, source, nickserv); // XXX nickserv hard coded
d76ed9a9 4454}
4455
4cb36ef0 4456#ifdef WITH_LDAP
b96027ad 4457static void
4458search_add2ldap_func (struct userNode *source, struct handle_info *match)
4459{
87677bd8 4460 int rc;
4461 if(match->email_addr && match->passwd && match->handle) {
4462 rc = ldap_do_add(match->handle, match->passwd, match->email_addr);
4463 if(rc != LDAP_SUCCESS) {
4464 send_message(source, nickserv, "NSMSG_LDAP_FAIL_ADD", match->handle, ldap_err2string(rc));
4465 }
b96027ad 4466 }
b96027ad 4467}
4cb36ef0 4468#endif
b96027ad 4469
d76ed9a9 4470static int
4471nickserv_sort_accounts_by_access(const void *a, const void *b)
4472{
4473 const struct handle_info *hi_a = *(const struct handle_info**)a;
4474 const struct handle_info *hi_b = *(const struct handle_info**)b;
4475 if (hi_a->opserv_level != hi_b->opserv_level)
4476 return hi_b->opserv_level - hi_a->opserv_level;
4477 return irccasecmp(hi_a->handle, hi_b->handle);
4478}
4479
4480void
4481nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
4482{
4483 struct handle_info_list hil;
4484 struct helpfile_table tbl;
4485 unsigned int ii;
4486 dict_iterator_t it;
4487 const char **ary;
4488
4489 memset(&hil, 0, sizeof(hil));
4490 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
4491 struct handle_info *hi = iter_data(it);
4492 if (hi->opserv_level)
4493 handle_info_list_append(&hil, hi);
4494 }
4495 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
4496 tbl.length = hil.used + 1;
4497 tbl.width = 2;
a8370a20 4498 tbl.flags = TABLE_NO_FREE | TABLE_REPEAT_ROWS | TABLE_REPEAT_HEADERS;
d76ed9a9 4499 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4500 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
4501 ary[0] = "Account";
4502 ary[1] = "Level";
4503 for (ii = 0; ii < hil.used; ) {
4504 ary = malloc(tbl.width * sizeof(ary[0]));
4505 ary[0] = hil.list[ii]->handle;
4506 ary[1] = strtab(hil.list[ii]->opserv_level);
4507 tbl.contents[++ii] = ary;
4508 }
4509 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
a8370a20 4510 /*reply("MSG_MATCH_COUNT", hil.used); */
d76ed9a9 4511 for (ii = 0; ii < hil.used; ii++)
4512 free(tbl.contents[ii]);
4513 free(tbl.contents);
4514 free(hil.list);
4515}
4516
4517static NICKSERV_FUNC(cmd_search)
4518{
4519 struct nickserv_discrim *discrim;
4520 discrim_search_func action;
4521 struct svccmd *subcmd;
4522 unsigned int matches;
4523 char buf[MAXLEN];
4524
4525 NICKSERV_MIN_PARMS(3);
4526 sprintf(buf, "search %s", argv[1]);
4527 subcmd = dict_find(nickserv_service->commands, buf, NULL);
4528 if (!irccasecmp(argv[1], "print"))
4529 action = search_print_func;
4530 else if (!irccasecmp(argv[1], "count"))
4531 action = search_count_func;
4532 else if (!irccasecmp(argv[1], "unregister"))
4533 action = search_unregister_func;
b96027ad 4534#ifdef WITH_LDAP
4535 else if (nickserv_conf.ldap_enable && !irccasecmp(argv[1], "add2ldap"))
4536 action = search_add2ldap_func;
4537#endif
d76ed9a9 4538 else {
4539 reply("NSMSG_INVALID_ACTION", argv[1]);
4540 return 0;
4541 }
4542
4543 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
4544 return 0;
4545
c092fcad 4546 discrim = nickserv_discrim_create(cmd, user, argc-2, argv+2);
d76ed9a9 4547 if (!discrim)
4548 return 0;
4549
4550 if (action == search_print_func)
4551 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
4552 else if (action == search_count_func)
4553 discrim->limit = INT_MAX;
4554
4555 matches = nickserv_discrim_search(discrim, action, user);
4556
4557 if (matches)
4558 reply("MSG_MATCH_COUNT", matches);
4559 else
4560 reply("MSG_NO_MATCHES");
4561
4562 free(discrim);
4563 return 0;
4564}
4565
4566static MODCMD_FUNC(cmd_checkpass)
4567{
4568 struct handle_info *hi;
4569
4570 NICKSERV_MIN_PARMS(3);
4571 if (!(hi = get_handle_info(argv[1]))) {
4572 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4573 return 0;
4574 }
4575 if (checkpass(argv[2], hi->passwd))
4576 reply("CHECKPASS_YES");
4577 else
4578 reply("CHECKPASS_NO");
4579 argv[2] = "****";
4580 return 1;
4581}
4582
1136f709 4583static MODCMD_FUNC(cmd_checkemail)
4584{
4585 struct handle_info *hi;
4586
4587 NICKSERV_MIN_PARMS(3);
4588 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
4589 return 0;
4590 }
4591 if (!hi->email_addr)
4592 reply("CHECKEMAIL_NOT_SET");
4593 else if (!irccasecmp(argv[2], hi->email_addr))
4594 reply("CHECKEMAIL_YES");
4595 else
4596 reply("CHECKEMAIL_NO");
4597 return 1;
4598}
4599
d76ed9a9 4600static void
82b7b0d8 4601nickserv_db_read_handle(char *handle, dict_t obj)
d76ed9a9 4602{
4603 const char *str;
2fa83595 4604 struct string_list *masks, *sslfps, *slist, *ignores;
d76ed9a9 4605 struct handle_info *hi;
4606 struct userNode *authed_users;
1136f709 4607 struct userData *channel_list;
d76ed9a9 4608 unsigned long int id;
4609 unsigned int ii;
4610 dict_t subdb;
0f6fe38c 4611 char *setter, *note;
4612 time_t date;
d76ed9a9 4613
4614 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
4615 id = str ? strtoul(str, NULL, 0) : 0;
4616 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
4617 if (!str) {
4618 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
4619 return;
4620 }
4621 if ((hi = get_handle_info(handle))) {
4622 authed_users = hi->users;
1136f709 4623 channel_list = hi->channels;
d76ed9a9 4624 hi->users = NULL;
02c37249 4625 hi->channels = NULL;
d76ed9a9 4626 dict_remove(nickserv_handle_dict, hi->handle);
4627 } else {
4628 authed_users = NULL;
1136f709 4629 channel_list = NULL;
d76ed9a9 4630 }
acb142f0 4631 if(nickserv_conf.force_handles_lowercase)
4632 irc_strtolower(handle);
d76ed9a9 4633 hi = register_handle(handle, str, id);
4634 if (authed_users) {
4635 hi->users = authed_users;
4636 while (authed_users) {
4637 authed_users->handle_info = hi;
4638 authed_users = authed_users->next_authed;
4639 }
4640 }
1136f709 4641 hi->channels = channel_list;
d76ed9a9 4642 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
4643 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
2fa83595 4644 sslfps = database_get_data(obj, KEY_SSLFPS, RECDB_STRING_LIST);
4645 hi->sslfps = sslfps ? string_list_copy(sslfps) : alloc_string_list(1);
5177fd21 4646 ignores = database_get_data(obj, KEY_IGNORES, RECDB_STRING_LIST);
4647 hi->ignores = ignores ? string_list_copy(ignores) : alloc_string_list(1);
d76ed9a9 4648 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
4649 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
4650 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
4651 hi->language = language_find(str ? str : "C");
4652 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
4653 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
4654 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
4655 if (str)
4656 hi->infoline = strdup(str);
4657 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
4658 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
4659 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
4660 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
1136f709 4661 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
4662 hi->karma = str ? strtoul(str, NULL, 0) : 0;
d76ed9a9 4663 /* We want to read the nicks even if disable_nicks is set. This is so
4664 * that we don't lose the nick data entirely. */
4665 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
4666 if (slist) {
4667 for (ii=0; ii<slist->used; ii++)
4668 register_nick(slist->list[ii], hi);
4669 }
4670 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
4671 if (str) {
4672 for (ii=0; str[ii]; ii++)
4673 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
4674 }
4675 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
338a82b5 4676 hi->userlist_style = str ? str[0] : HI_DEFAULT_STYLE;
0f6fe38c 4677 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
4678 hi->announcements = str ? str[0] : '?';
d76ed9a9 4679 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
4680 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
4681 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
4682 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
4683 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
4684 if (!str)
4685 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
4686 if (str)
4687 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
4688 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
4689 if (str)
4690 nickserv_set_email_addr(hi, str);
4691 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
4692 if (str)
4693 hi->epithet = strdup(str);
0f6fe38c 4694 subdb = database_get_data(obj, KEY_NOTE_NOTE, RECDB_OBJECT);
4695 if (subdb) {
4696 setter = database_get_data(subdb, KEY_NOTE_SETTER, RECDB_QSTRING);
4697 str = database_get_data(subdb, KEY_NOTE_DATE, RECDB_QSTRING);
4698 date = str ? (time_t)strtoul(str, NULL, 0) : now;
4699 note = database_get_data(subdb, KEY_NOTE_NOTE, RECDB_QSTRING);
4700 if (setter && date && note)
4701 {
4702 if (!(hi->note = nickserv_add_note(setter, date, note)))
4703 hi->note = NULL;
4704 }
4705 }
2362161a 4706
d76ed9a9 4707 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
4708 if (str)
4709 hi->fakehost = strdup(str);
7637f48f 4710
d76ed9a9 4711 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
4712 if (subdb) {
4713 const char *data, *type, *expires, *cookie_str;
4714 struct handle_cookie *cookie;
4715
4716 cookie = calloc(1, sizeof(*cookie));
4717 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
4718 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
4719 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
4720 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
4721 if (!type || !expires || !cookie_str) {
4722 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
4723 goto cookie_out;
4724 }
4725 if (!irccasecmp(type, KEY_ACTIVATION))
4726 cookie->type = ACTIVATION;
4727 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
4728 cookie->type = PASSWORD_CHANGE;
4729 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
4730 cookie->type = EMAIL_CHANGE;
4731 else if (!irccasecmp(type, KEY_ALLOWAUTH))
4732 cookie->type = ALLOWAUTH;
4733 else {
4734 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
4735 goto cookie_out;
4736 }
4737 cookie->expires = strtoul(expires, NULL, 0);
4738 if (cookie->expires < now)
4739 goto cookie_out;
4740 if (data)
4741 cookie->data = strdup(data);
4742 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
4743 cookie->hi = hi;
4744 cookie_out:
4745 if (cookie->hi)
4746 nickserv_bake_cookie(cookie);
4747 else
4748 nickserv_free_cookie(cookie);
4749 }
4750}
4751
4752static int
4753nickserv_saxdb_read(dict_t db) {
4754 dict_iterator_t it;
4755 struct record_data *rd;
82b7b0d8 4756 char *handle;
d76ed9a9 4757
4758 for (it=dict_first(db); it; it=iter_next(it)) {
4759 rd = iter_data(it);
82b7b0d8 4760 handle = strdup(iter_key(it));
4761 nickserv_db_read_handle(handle, rd->d.object);
4762 free(handle);
d76ed9a9 4763 }
4764 return 0;
4765}
4766
4767static NICKSERV_FUNC(cmd_mergedb)
4768{
4769 struct timeval start, stop;
4770 dict_t db;
4771
4772 NICKSERV_MIN_PARMS(2);
4773 gettimeofday(&start, NULL);
4774 if (!(db = parse_database(argv[1]))) {
4775 reply("NSMSG_DB_UNREADABLE", argv[1]);
4776 return 0;
4777 }
4778 nickserv_saxdb_read(db);
4779 free_database(db);
4780 gettimeofday(&stop, NULL);
4781 stop.tv_sec -= start.tv_sec;
4782 stop.tv_usec -= start.tv_usec;
4783 if (stop.tv_usec < 0) {
4784 stop.tv_sec -= 1;
4785 stop.tv_usec += 1000000;
4786 }
4787 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
4788 return 1;
4789}
4790
4791static void
4792expire_handles(UNUSED_ARG(void *data))
4793{
4794 dict_iterator_t it, next;
4795 time_t expiry;
4796 struct handle_info *hi;
4797
4798 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4799 next = iter_next(it);
4800 hi = iter_data(it);
4801 if ((hi->opserv_level > 0)
4802 || hi->users
4803 || HANDLE_FLAGGED(hi, FROZEN)
4804 || HANDLE_FLAGGED(hi, NODELETE)) {
4805 continue;
4806 }
4807 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4808 if ((now - hi->lastseen) > expiry) {
4809 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
258d1427 4810 nickserv_unregister_handle(hi, NULL, NULL);
d76ed9a9 4811 }
4812 }
4813
4814 if (nickserv_conf.handle_expire_frequency)
4815 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4816}
4817
4818static void
4819nickserv_load_dict(const char *fname)
4820{
4821 FILE *file;
4822 char line[128];
4823 if (!(file = fopen(fname, "r"))) {
4824 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4825 return;
4826 }
1136f709 4827 while (fgets(line, sizeof(line), file)) {
d76ed9a9 4828 if (!line[0])
4829 continue;
4830 if (line[strlen(line)-1] == '\n')
4831 line[strlen(line)-1] = 0;
4832 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4833 }
4834 fclose(file);
4835 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4836}
4837
4838static enum reclaim_action
4839reclaim_action_from_string(const char *str) {
4840 if (!str)
4841 return RECLAIM_NONE;
4842 else if (!irccasecmp(str, "warn"))
4843 return RECLAIM_WARN;
4844 else if (!irccasecmp(str, "svsnick"))
4845 return RECLAIM_SVSNICK;
4846 else if (!irccasecmp(str, "kill"))
4847 return RECLAIM_KILL;
4848 else
4849 return RECLAIM_NONE;
4850}
4851
4852static void
4853nickserv_conf_read(void)
4854{
4855 dict_t conf_node, child;
4856 const char *str;
4857 dict_iterator_t it;
7637f48f 4858 struct string_list *strlist;
d76ed9a9 4859
4860 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4861 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4862 return;
4863 }
4864 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4865 if (!str)
4866 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4867 if (nickserv_conf.valid_handle_regex_set)
4868 regfree(&nickserv_conf.valid_handle_regex);
4869 if (str) {
4870 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4871 nickserv_conf.valid_handle_regex_set = !err;
4872 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4873 } else {
4874 nickserv_conf.valid_handle_regex_set = 0;
4875 }
4876 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4877 if (nickserv_conf.valid_nick_regex_set)
4878 regfree(&nickserv_conf.valid_nick_regex);
4879 if (str) {
4880 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4881 nickserv_conf.valid_nick_regex_set = !err;
4882 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4883 } else {
4884 nickserv_conf.valid_nick_regex_set = 0;
4885 }
bf93ca8d 4886 str = database_get_data(conf_node, KEY_VALID_FAKEHOST_REGEX, RECDB_QSTRING);
4887 if (nickserv_conf.valid_fakehost_regex_set)
4888 regfree(&nickserv_conf.valid_fakehost_regex);
4889 if (str) {
4890 int err = regcomp(&nickserv_conf.valid_fakehost_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4891 nickserv_conf.valid_fakehost_regex_set = !err;
4892 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_fakehost_regex (error %d)", err);
4893 } else {
4894 nickserv_conf.valid_fakehost_regex_set = 0;
4895 }
d76ed9a9 4896 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4897 if (!str)
4898 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4899 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4900 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4901 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4902 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4903 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4904 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4905 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4906 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4907 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4908 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4909 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4910 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4911 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4912 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4913 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4914 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4915 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4916 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4917 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4918 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4919 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4920 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4921 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4922 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4923 if (!str)
4924 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4925 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4926 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4927 if (!str)
4928 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4929 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4930 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4931 if (!str)
4932 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4933 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4934 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4935 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4936 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4937 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4938 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
1136f709 4939 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4940 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4941 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4942 if (!str)
4943 str = "ShgsfnHbu";
4944 nickserv_conf.ounregister_flags = 0;
4945 while(*str) {
4946 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4947 str++;
4948 if(pos)
4949 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4950 }
d76ed9a9 4951 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4952 if (!nickserv_conf.disable_nicks) {
4953 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4954 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4955 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4956 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4957 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4958 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4959 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4960 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4961 }
4962 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4963 for (it=dict_first(child); it; it=iter_next(it)) {
4964 const char *key = iter_key(it), *value;
4965 unsigned char flag;
4966 int pos;
4967
4968 if (!strncasecmp(key, "uc_", 3))
4969 flag = toupper(key[3]);
4970 else if (!strncasecmp(key, "lc_", 3))
4971 flag = tolower(key[3]);
4972 else
4973 flag = key[0];
4974
4975 if ((pos = handle_inverse_flags[flag])) {
4976 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4977 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4978 }
4979 }
4980 if (nickserv_conf.weak_password_dict)
4981 dict_delete(nickserv_conf.weak_password_dict);
4982 nickserv_conf.weak_password_dict = dict_new();
4983 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4984 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4985 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4986 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4987 if (str)
4988 nickserv_load_dict(str);
4989 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4990 if (nickserv && str)
4991 NickChange(nickserv, str, 0);
4992 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4993 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4994 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4995 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4996 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4997 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4998 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4999 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
8dc1d9ae 5000 str = database_get_data(conf_node, KEY_SYNC_LOG, RECDB_QSTRING);
5001 nickserv_conf.sync_log = str ? enabled_string(str) : 0;
d76ed9a9 5002 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
5003 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
5004 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
5005 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
5006 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
5007 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
5008 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
5009 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
5010 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
5011 nickserv_conf.titlehost_suffix = str ? str : "example.net";
5a1daaab 5012
7637f48f 5013 free_string_list(nickserv_conf.denied_fakehost_words);
5014 strlist = database_get_data(conf_node, KEY_DENIED_FAKEHOST_WORDS, RECDB_STRING_LIST);
5015 if(strlist)
5016 strlist = string_list_copy(strlist);
5017 else {
5018 strlist = alloc_string_list(4);
5019 string_list_append(strlist, strdup("sex"));
5020 string_list_append(strlist, strdup("fuck"));
5021 }
5022 nickserv_conf.denied_fakehost_words = strlist;
5023
338a82b5 5024 str = database_get_data(conf_node, KEY_DEFAULT_STYLE, RECDB_QSTRING);
5025 nickserv_conf.default_style = str ? str[0] : HI_DEFAULT_STYLE;
5026
5a1daaab 5027 str = database_get_data(conf_node, KEY_AUTO_OPER, RECDB_QSTRING);
5028 nickserv_conf.auto_oper = str ? str : "";
5029
5030 str = database_get_data(conf_node, KEY_AUTO_ADMIN, RECDB_QSTRING);
5031 nickserv_conf.auto_admin = str ? str : "";
5032
69517d70 5033 str = database_get_data(conf_node, KEY_AUTO_OPER_PRIVS, RECDB_QSTRING);
5034 nickserv_conf.auto_oper_privs = str ? str : "";
5035
5036 str = database_get_data(conf_node, KEY_AUTO_ADMIN_PRIVS, RECDB_QSTRING);
5037 nickserv_conf.auto_admin_privs = str ? str : "";
5038
d76ed9a9 5039 str = conf_get_data("server/network", RECDB_QSTRING);
5040 nickserv_conf.network_name = str ? str : "some IRC network";
5041 if (!nickserv_conf.auth_policer_params) {
5042 nickserv_conf.auth_policer_params = policer_params_new();
5043 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
5044 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
5045 }
5046 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
5047 for (it=dict_first(child); it; it=iter_next(it))
5048 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
e166c31b 5049
5050 str = database_get_data(conf_node, KEY_LDAP_ENABLE, RECDB_QSTRING);
5051 nickserv_conf.ldap_enable = str ? strtoul(str, NULL, 0) : 0;
acb142f0 5052
5053 str = database_get_data(conf_node, KEY_FORCE_HANDLES_LOWERCASE, RECDB_QSTRING);
5054 nickserv_conf.force_handles_lowercase = str ? strtol(str, NULL, 0) : 0;
5055
39edf54a 5056#ifndef WITH_LDAP
5057 if(nickserv_conf.ldap_enable > 0) {
5058 /* ldap is enabled but not compiled in - error out */
5059 log_module(MAIN_LOG, LOG_ERROR, "ldap is enabled in config, but not compiled in!");
5060 nickserv_conf.ldap_enable = 0;
5061 sleep(5);
5062 }
5063#endif
e166c31b 5064
39edf54a 5065#ifdef WITH_LDAP
bec5dd26 5066 str = database_get_data(conf_node, KEY_LDAP_URI, RECDB_QSTRING);
5067 nickserv_conf.ldap_uri = str ? str : "";
e166c31b 5068
e166c31b 5069 str = database_get_data(conf_node, KEY_LDAP_BASE, RECDB_QSTRING);
5070 nickserv_conf.ldap_base = str ? str : "";
5071
5072 str = database_get_data(conf_node, KEY_LDAP_DN_FMT, RECDB_QSTRING);
5073 nickserv_conf.ldap_dn_fmt = str ? str : "";
5074
5075 str = database_get_data(conf_node, KEY_LDAP_VERSION, RECDB_QSTRING);
5076 nickserv_conf.ldap_version = str ? strtoul(str, NULL, 0) : 3;
5077
5078 str = database_get_data(conf_node, KEY_LDAP_AUTOCREATE, RECDB_QSTRING);
5079 nickserv_conf.ldap_autocreate = str ? strtoul(str, NULL, 0) : 0;
ea02341b 5080
ddcb3eb3 5081 str = database_get_data(conf_node, KEY_LDAP_TIMEOUT, RECDB_QSTRING);
5082 nickserv_conf.ldap_timeout = str ? strtoul(str, NULL, 0) : 5;
5083
ea02341b 5084 str = database_get_data(conf_node, KEY_LDAP_ADMIN_DN, RECDB_QSTRING);
5085 nickserv_conf.ldap_admin_dn = str ? str : "";
5086
5087 str = database_get_data(conf_node, KEY_LDAP_ADMIN_PASS, RECDB_QSTRING);
5088 nickserv_conf.ldap_admin_pass = str ? str : "";
5089
5090 str = database_get_data(conf_node, KEY_LDAP_FIELD_ACCOUNT, RECDB_QSTRING);
5091 nickserv_conf.ldap_field_account = str ? str : "";
5092
5093 str = database_get_data(conf_node, KEY_LDAP_FIELD_PASSWORD, RECDB_QSTRING);
5094 nickserv_conf.ldap_field_password = str ? str : "";
5095
5096 str = database_get_data(conf_node, KEY_LDAP_FIELD_EMAIL, RECDB_QSTRING);
5097 nickserv_conf.ldap_field_email = str ? str : "";
5098
35ea100f 5099 str = database_get_data(conf_node, KEY_LDAP_FIELD_OSLEVEL, RECDB_QSTRING);
5100 nickserv_conf.ldap_field_oslevel = str ? str : "";
5101
8a729617 5102 str = database_get_data(conf_node, KEY_LDAP_OPER_GROUP_DN, RECDB_QSTRING);
5103 nickserv_conf.ldap_oper_group_dn = str ? str : "";
5104
17d4a698 5105 str = database_get_data(conf_node, KEY_LDAP_OPER_GROUP_LEVEL, RECDB_QSTRING);
5106 nickserv_conf.ldap_oper_group_level = str ? strtoul(str, NULL, 0) : 99;
5107
8a729617 5108 str = database_get_data(conf_node, KEY_LDAP_FIELD_GROUP_MEMBER, RECDB_QSTRING);
5109 nickserv_conf.ldap_field_group_member = str ? str : "";
5110
73d4cc91 5111 free_string_list(nickserv_conf.ldap_object_classes);
5112 strlist = database_get_data(conf_node, KEY_LDAP_OBJECT_CLASSES, RECDB_STRING_LIST);
5113 if(strlist)
5114 strlist = string_list_copy(strlist);
5115 else {
5116 strlist = alloc_string_list(4);
5117 string_list_append(strlist, strdup("top"));
5118 }
5119 nickserv_conf.ldap_object_classes = strlist;
5120
39edf54a 5121#endif
e166c31b 5122
d76ed9a9 5123}
5124
5125static void
5126nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
5127 const char *msg;
5128 char newnick[NICKLEN+1];
5129
5130 assert(user);
5131 assert(ni);
5132 switch (action) {
5133 case RECLAIM_NONE:
5134 /* do nothing */
5135 break;
5136 case RECLAIM_WARN:
5137 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
72971fc8 5138 send_message(user, nickserv, "NSMSG_RECLAIM_HOWTO", ni->owner->handle, nickserv->nick, self->name, ni->owner->handle);
d76ed9a9 5139 break;
5140 case RECLAIM_SVSNICK:
5141 do {
5142 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
5143 } while (GetUserH(newnick));
5144 irc_svsnick(nickserv, user, newnick);
5145 break;
5146 case RECLAIM_KILL:
5147 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
1136f709 5148 DelUser(user, nickserv, 1, msg);
d76ed9a9 5149 break;
5150 }
5151}
5152
5153static void
5154nickserv_reclaim_p(void *data) {
5155 struct userNode *user = data;
5156 struct nick_info *ni = get_nick_info(user->nick);
5157 if (ni)
5158 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
5159}
5160
5161static int
f0fb2e2d 5162check_user_nick(struct userNode *user, UNUSED_ARG(void *extra)) {
d76ed9a9 5163 struct nick_info *ni;
5164 user->modes &= ~FLAGS_REGNICK;
5165 if (!(ni = get_nick_info(user->nick)))
5166 return 0;
5167 if (user->handle_info == ni->owner) {
5168 user->modes |= FLAGS_REGNICK;
5169 irc_regnick(user);
5170 return 0;
5171 }
5172 if (nickserv_conf.warn_nick_owned)
5173 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
72971fc8 5174 send_message(user, nickserv, "NSMSG_RECLAIM_HOWTO", ni->owner->handle, nickserv->nick, self->name, ni->owner->handle);
d76ed9a9 5175 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
5176 return 0;
5177 if (nickserv_conf.auto_reclaim_delay)
5178 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
5179 else
5180 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
d76ed9a9 5181
1136f709 5182 return 0;
d76ed9a9 5183}
5184
5185void
5186handle_account(struct userNode *user, const char *stamp)
5187{
5188 struct handle_info *hi;
b21e2cfe 5189 char *colon;
d76ed9a9 5190
5191#ifdef WITH_PROTOCOL_P10
a9b5e3de 5192 time_t timestamp = 0;
5193
5194 colon = strchr(stamp, ':');
5195 if(colon && colon[1])
5196 {
5197 *colon = 0;
5198 timestamp = atoi(colon+1);
5199 }
d76ed9a9 5200 hi = dict_find(nickserv_handle_dict, stamp, NULL);
c0e47c86 5201 if(hi && timestamp && hi->registered != timestamp)
a9b5e3de 5202 {
4cb36ef0 5203 log_module(MAIN_LOG, LOG_WARNING, "%s using account %s but timestamp does not match %s is not %s.", user->nick, stamp, ctime(&timestamp),
5204ctime(&hi->registered));
a9b5e3de 5205 return;
5206 }
d76ed9a9 5207#else
5208 hi = dict_find(nickserv_id_dict, stamp, NULL);
9b2d838a 5209 log_module(MAIN_LOG, LOG_WARNING, "Using non-P10 code in accounts, not tested at all!");
d76ed9a9 5210#endif
5211
5212 if (hi) {
5213 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
5214 return;
5215 }
5216 set_user_handle_info(user, hi, 0);
5217 } else {
5218 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
5219 }
5220}
5221
5222void
63189c10 5223handle_nick_change(struct userNode *user, const char *old_nick, UNUSED_ARG(void *extra))
d76ed9a9 5224{
5225 struct handle_info *hi;
5226
5227 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
5228 dict_remove(nickserv_allow_auth_dict, old_nick);
5229 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
5230 }
5231 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
f0fb2e2d 5232 check_user_nick(user, NULL);
d76ed9a9 5233}
5234
5235void
a6bcc929 5236nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why), UNUSED_ARG(void *extra))
d76ed9a9 5237{
5238 dict_remove(nickserv_allow_auth_dict, user->nick);
5239 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
5240 set_user_handle_info(user, NULL, 0);
5241}
5242
5243static struct modcmd *
5244nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
5245{
5246 if (min_level > 0) {
5247 char buf[16];
5248 sprintf(buf, "%u", min_level);
5249 if (must_be_qualified) {
5250 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
5251 } else {
5252 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
5253 }
5254 } else if (min_level == 0) {
5255 if (must_be_qualified) {
5256 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
5257 } else {
5258 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
5259 }
5260 } else {
5261 if (must_be_qualified) {
5262 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
5263 } else {
5264 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
5265 }
5266 }
5267}
5268
5269static void
30874d66 5270nickserv_db_cleanup(UNUSED_ARG(void* extra))
d76ed9a9 5271{
a6bcc929 5272 unreg_del_user_func(nickserv_remove_user, NULL);
d76ed9a9 5273 userList_clean(&curr_helpers);
5274 policer_params_delete(nickserv_conf.auth_policer_params);
5275 dict_delete(nickserv_handle_dict);
5276 dict_delete(nickserv_nick_dict);
5277 dict_delete(nickserv_opt_dict);
5278 dict_delete(nickserv_allow_auth_dict);
5279 dict_delete(nickserv_email_dict);
5280 dict_delete(nickserv_id_dict);
5281 dict_delete(nickserv_conf.weak_password_dict);
5282 free(auth_func_list);
81ac4787 5283 free(auth_func_list_extra);
d76ed9a9 5284 free(unreg_func_list);
974d3831 5285 free(unreg_func_list_extra);
d76ed9a9 5286 free(rf_list);
3070719a 5287 free(rf_list_extra);
d76ed9a9 5288 free(allowauth_func_list);
99c332f8 5289 free(allowauth_func_list_extra);
d76ed9a9 5290 free(handle_merge_func_list);
50dafce8 5291 free(handle_merge_func_list_extra);
d76ed9a9 5292 free(failpw_func_list);
c8b793cb 5293 free(failpw_func_list_extra);
d76ed9a9 5294 if (nickserv_conf.valid_handle_regex_set)
5295 regfree(&nickserv_conf.valid_handle_regex);
5296 if (nickserv_conf.valid_nick_regex_set)
5297 regfree(&nickserv_conf.valid_nick_regex);
5298}
5299
81ac4787 5300void handle_loc_auth_oper(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle), UNUSED_ARG(void *extra)) {
c3915bdc 5301 char *privv[MAXNUMPARAMS];
5302 int privc, i;
5303
10be9be0 5304 if (!*nickserv_conf.auto_oper || !user->handle_info)
5305 return;
5306
5307 if (!IsOper(user)) {
5308 if (*nickserv_conf.auto_admin && user->handle_info->opserv_level >= opserv_conf_admin_level()) {
c3915bdc 5309 if (nickserv_conf.auto_admin_privs[0]) {
5310 irc_raw_privs(user, nickserv_conf.auto_admin_privs);
5311 privc = split_line(strdup(nickserv_conf.auto_admin_privs), false, MAXNUMPARAMS, privv);
5312 for (i = 0; i < privc; i++) {
5313 client_modify_priv_by_name(user, privv[i], 1);
5314 }
5315 }
10be9be0 5316 irc_umode(user, nickserv_conf.auto_admin);
5317 irc_sno(0x1, "%s (%s@%s) is now an IRC Administrator",
5318 user->nick, user->ident, user->hostname);
c3915bdc 5319 send_message(user, nickserv, "NSMSG_AUTO_OPER_ADMIN");
10be9be0 5320 } else if (*nickserv_conf.auto_oper && user->handle_info->opserv_level) {
c3915bdc 5321 if (nickserv_conf.auto_oper_privs[0]) {
5322 irc_raw_privs(user, nickserv_conf.auto_oper_privs);
5323 privc = split_line(strdup(nickserv_conf.auto_oper_privs), false, MAXNUMPARAMS, privv);
5324 for (i = 0; i < privc; i++) {
5325 client_modify_priv_by_name(user, privv[i], 1);
5326 }
5327 }
10be9be0 5328 irc_umode(user, nickserv_conf.auto_oper);
5329 irc_sno(0x1, "%s (%s@%s) is now an IRC Operator",
5330 user->nick, user->ident, user->hostname);
c3915bdc 5331 send_message(user, nickserv, "NSMSG_AUTO_OPER");
10be9be0 5332 }
5333 }
5334}
5335
d76ed9a9 5336void
5337init_nickserv(const char *nick)
5338{
7637f48f 5339 struct chanNode *chan;
d76ed9a9 5340 unsigned int i;
5341 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
f0fb2e2d 5342 reg_new_user_func(check_user_nick, NULL);
63189c10 5343 reg_nick_change_func(handle_nick_change, NULL);
a6bcc929 5344 reg_del_user_func(nickserv_remove_user, NULL);
d76ed9a9 5345 reg_account_func(handle_account);
81ac4787 5346 reg_auth_func(handle_loc_auth_oper, NULL);
d76ed9a9 5347
5348 /* set up handle_inverse_flags */
5349 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
5350 for (i=0; handle_flags[i]; i++) {
5351 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
5352 flag_access_levels[i] = 0;
5353 }
5354
5355 conf_register_reload(nickserv_conf_read);
5356 nickserv_opt_dict = dict_new();
5357 nickserv_email_dict = dict_new();
5177fd21 5358
d76ed9a9 5359 dict_set_free_keys(nickserv_email_dict, free);
5360 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
5361
5362 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4048352e 5363/* Removed qualified_host as default requirement for AUTH, REGISTER, PASS, etc. nets
5364 * can enable it per command using modcmd. (its a shitty default IMO, and now in 1.3
5365 * a big pain to disable since its nolonger in the config file. ) -Rubin
5366 */
5367 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+loghostmask", NULL);
d76ed9a9 5368 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4048352e 5369 nickserv_define_func("REGISTER", cmd_register, -1, 0, 0);
d76ed9a9 5370 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4048352e 5371 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 0);
d76ed9a9 5372 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
5373 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
5374 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
5375 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
5376 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
2fa83595 5377 nickserv_define_func("ADDSSLFP", cmd_addsslfp, -1, 1, 0);
5378 nickserv_define_func("OADDSSLFP", cmd_oaddsslfp, 0, 1, 0);
5379 nickserv_define_func("DELSSLFP", cmd_delsslfp, -1, 1, 0);
5380 nickserv_define_func("ODELSSLFP", cmd_odelsslfp, 0, 1, 0);
4048352e 5381 nickserv_define_func("PASS", cmd_pass, -1, 1, 0);
d76ed9a9 5382 nickserv_define_func("SET", cmd_set, -1, 1, 0);
5383 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
5384 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
5385 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
5386 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
5387 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
1136f709 5388 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
d76ed9a9 5389 if (!nickserv_conf.disable_nicks) {
5390 /* nick management commands */
5391 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
5392 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
5393 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
5394 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
5395 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
5396 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
5397 }
5398 if (nickserv_conf.email_enabled) {
5399 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4048352e 5400 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 0);
5401 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 0);
d76ed9a9 5402 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
34938510 5403 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
d76ed9a9 5404 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
5405 }
5406 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
5177fd21 5407 /* ignore commands */
5408 nickserv_define_func("ADDIGNORE", cmd_addignore, -1, 1, 0);
5409 nickserv_define_func("OADDIGNORE", cmd_oaddignore, 0, 1, 0);
5410 nickserv_define_func("DELIGNORE", cmd_delignore, -1, 1, 0);
5411 nickserv_define_func("ODELIGNORE", cmd_odelignore, 0, 1, 0);
d76ed9a9 5412 /* miscellaneous commands */
5413 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
5414 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
5415 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
5416 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
5417 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
1136f709 5418 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
d76ed9a9 5419 /* other options */
5420 dict_insert(nickserv_opt_dict, "INFO", opt_info);
5421 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
5422 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
5423 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
5424 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
7fdb7639 5425 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
338a82b5 5426 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
d76ed9a9 5427 dict_insert(nickserv_opt_dict, "PASS", opt_password);
5428 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
5429 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
5430 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
5431 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
5432 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
0f6fe38c 5433 dict_insert(nickserv_opt_dict, "NOTE", opt_note);
d76ed9a9 5434 if (nickserv_conf.titlehost_suffix) {
5435 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
5436 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
5437 }
0f6fe38c 5438 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
d76ed9a9 5439 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
0b587959 5440 dict_insert(nickserv_opt_dict, "ADVANCED", opt_advanced);
d76ed9a9 5441 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
1136f709 5442 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
d76ed9a9 5443
5444 nickserv_handle_dict = dict_new();
5445 dict_set_free_keys(nickserv_handle_dict, free);
5446 dict_set_free_data(nickserv_handle_dict, free_handle_info);
5447
5448 nickserv_id_dict = dict_new();
5449 dict_set_free_keys(nickserv_id_dict, free);
5450
5451 nickserv_nick_dict = dict_new();
1117fc5a 5452 dict_set_free_data(nickserv_nick_dict, free);
d76ed9a9 5453
5454 nickserv_allow_auth_dict = dict_new();
5455
5456 userList_init(&curr_helpers);
5457
5458 if (nick) {
a32da4c7 5459 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
1136f709 5460 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
d76ed9a9 5461 nickserv_service = service_register(nickserv);
5462 }
5463 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
30874d66 5464 reg_exit_func(nickserv_db_cleanup, NULL);
d76ed9a9 5465 if(nickserv_conf.handle_expire_frequency)
5466 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
7637f48f 5467
5468 if(autojoin_channels && nickserv) {
5469 for (i = 0; i < autojoin_channels->used; i++) {
5470 chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
5471 AddChannelUser(nickserv, chan)->modes |= MODE_CHANOP;
5472 }
5473 }
e166c31b 5474#ifdef WITH_LDAP
5475 ldap_do_init(nickserv_conf);
5476#endif
7637f48f 5477
d76ed9a9 5478 message_register_table(msgtab);
5479}