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