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