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