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