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