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