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