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