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