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