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