]> jfr.im git - irc/evilnet/x3.git/blame - src/nickserv.c
Fixed a bug when reading services/nickserv/hard_maxlogins from x3.conf
[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
67fd744c
MB
1628 argv[2] = "****";
1629
d762299d 1630 if (nickserv_conf.sync_log)
1631 SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, email ? email : "@", user->info); /* Send just @ for email if none */
d76ed9a9 1632 return 1;
1633}
1634
5177fd21 1635static int
c092fcad 1636nickserv_ignore(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, char *mask)
5177fd21 1637{
1638 unsigned int i;
92fac64c 1639 struct userNode *target;
d07e3fff 1640 char *new_mask = strdup(pretty_mask(mask));
5177fd21 1641 for (i=0; i<hi->ignores->used; i++) {
1642 if (!irccasecmp(new_mask, hi->ignores->list[i])) {
c092fcad 1643 reply("NSMSG_ADDIGNORE_ALREADY", new_mask);
d07e3fff 1644 free(new_mask);
5177fd21 1645 return 0;
1646 }
1647 }
1648 string_list_append(hi->ignores, new_mask);
c092fcad 1649 reply("NSMSG_ADDIGNORE_SUCCESS", new_mask);
5177fd21 1650
92fac64c 1651 for (target = hi->users; target; target = target->next_authed) {
1652 irc_silence(target, new_mask, 1);
1653 }
5177fd21 1654 return 1;
1655}
1656
1657static NICKSERV_FUNC(cmd_addignore)
1658{
1659 NICKSERV_MIN_PARMS(2);
1660
c092fcad 1661 return nickserv_ignore(cmd, user, user->handle_info, argv[1]);
5177fd21 1662}
1663
1664static NICKSERV_FUNC(cmd_oaddignore)
1665{
1666 struct handle_info *hi;
1667
1668 NICKSERV_MIN_PARMS(3);
1136f709 1669 if (!(hi = get_victim_oper(user, argv[1])))
5177fd21 1670 return 0;
668dc38e 1671
c092fcad 1672 return nickserv_ignore(cmd, user, hi, argv[2]);
5177fd21 1673}
1674
1675static int
c092fcad 1676nickserv_delignore(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, char *del_mask)
5177fd21 1677{
1678 unsigned int i;
92fac64c 1679 struct userNode *target;
d07e3fff 1680 char *pmask = strdup(pretty_mask(del_mask));
5177fd21 1681 for (i=0; i<hi->ignores->used; i++) {
d07e3fff 1682 if (!strcmp(pmask, hi->ignores->list[i]) || !strcmp(del_mask, hi->ignores->list[i])) {
5177fd21 1683 char *old_mask = hi->ignores->list[i];
1684 hi->ignores->list[i] = hi->ignores->list[--hi->ignores->used];
c092fcad 1685 reply("NSMSG_DELMASK_SUCCESS", old_mask);
92fac64c 1686 for (target = hi->users; target; target = target->next_authed) {
3f5b8801 1687 irc_silence(target, old_mask, 0);
92fac64c 1688 }
d07e3fff 1689 free(old_mask);
1690 free(pmask);
5177fd21 1691 return 1;
1692 }
1693 }
c092fcad 1694 reply("NSMSG_DELMASK_NOT_FOUND");
5177fd21 1695 return 0;
1696}
1697
1698static NICKSERV_FUNC(cmd_delignore)
1699{
1700 NICKSERV_MIN_PARMS(2);
c092fcad 1701 return nickserv_delignore(cmd, user, user->handle_info, argv[1]);
5177fd21 1702}
1703
1704static NICKSERV_FUNC(cmd_odelignore)
1705{
1706 struct handle_info *hi;
1707 NICKSERV_MIN_PARMS(3);
1136f709 1708 if (!(hi = get_victim_oper(user, argv[1])))
5177fd21 1709 return 0;
c092fcad 1710 return nickserv_delignore(cmd, user, hi, argv[2]);
5177fd21 1711}
1712
d76ed9a9 1713static NICKSERV_FUNC(cmd_handleinfo)
1714{
1715 char buff[400];
1716 unsigned int i, pos=0, herelen;
1717 struct userNode *target, *next_un;
1718 struct handle_info *hi;
1719 const char *nsmsg_none;
1720
1721 if (argc < 2) {
1722 if (!(hi = user->handle_info)) {
1723 reply("NSMSG_MUST_AUTH");
1724 return 0;
1725 }
1726 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1727 return 0;
1728 }
1729
1730 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1731 reply("NSMSG_HANDLEINFO_ON", hi->handle);
de9510bc 1732 reply("MSG_BAR");
d76ed9a9 1733 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1734
1735 if (!hi->users) {
1736 intervalString(buff, now - hi->lastseen, user->handle_info);
1737 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1738 } else {
1739 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1740 }
1741
1742 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1743 if (HANDLE_FLAGGED(hi, FROZEN))
1744 reply("NSMSG_HANDLEINFO_VACATION");
1745
1746 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1747 struct do_not_register *dnr;
1748 if ((dnr = chanserv_is_dnr(NULL, hi)))
1749 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1136f709 1750 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
d76ed9a9 1751 return 1;
684e2f02 1752 } else if (hi != user->handle_info) {
1753 reply("NSMSG_HANDLEINFO_END");
d76ed9a9 1754 return 1;
684e2f02 1755 }
d76ed9a9 1756
1136f709 1757 if (IsOper(user))
1758 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1759
d76ed9a9 1760 if (nickserv_conf.email_enabled)
1761 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1762
1763 if (hi->cookie) {
1764 const char *type;
1765 switch (hi->cookie->type) {
1766 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1767 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1768 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1769 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1770 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1771 }
1772 reply(type);
1773 }
1774
1775 if (hi->flags) {
1776 unsigned long flen = 1;
1777 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1778 flags[0] = '+';
1779 for (i=0, flen=1; handle_flags[i]; i++)
1780 if (hi->flags & 1 << i)
1781 flags[flen++] = handle_flags[i];
1782 flags[flen] = 0;
1783 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1784 } else {
1785 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1786 }
1787
1788 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1789 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1790 || (hi->opserv_level > 0)) {
1791 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1792 }
1793
0f6fe38c 1794 if (IsHelping(user) || IsOper(user))
1795 {
1796 if (hi->note)
1797 {
1798 char date[64];
1799 strftime(date, 64, "%b %d %Y", localtime(&hi->note->date));
1800 reply("NSMSG_HANDLEINFO_NOTE", hi->note->setter, date, hi->note->note);
1801 }
1802 }
1803
d76ed9a9 1804 if (hi->fakehost)
1805 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1806
1807 if (hi->last_quit_host[0])
1808 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1809 else
1810 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1811
1812 if (nickserv_conf.disable_nicks) {
1813 /* nicks disabled; don't show anything about registered nicks */
1814 } else if (hi->nicks) {
1815 struct nick_info *ni, *next_ni;
1816 for (ni = hi->nicks; ni; ni = next_ni) {
1817 herelen = strlen(ni->nick);
1818 if (pos + herelen + 1 > ArrayLength(buff)) {
1819 next_ni = ni;
1820 goto print_nicks_buff;
1821 } else {
1822 next_ni = ni->next;
1823 }
1824 memcpy(buff+pos, ni->nick, herelen);
1825 pos += herelen; buff[pos++] = ' ';
1826 if (!next_ni) {
1827 print_nicks_buff:
1828 buff[pos-1] = 0;
1829 reply("NSMSG_HANDLEINFO_NICKS", buff);
1830 pos = 0;
1831 }
1832 }
1833 } else {
1834 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1835 }
1836
1837 if (hi->masks->used) {
1838 for (i=0; i < hi->masks->used; i++) {
1839 herelen = strlen(hi->masks->list[i]);
1840 if (pos + herelen + 1 > ArrayLength(buff)) {
1841 i--;
1842 goto print_mask_buff;
1843 }
1844 memcpy(buff+pos, hi->masks->list[i], herelen);
1845 pos += herelen; buff[pos++] = ' ';
1846 if (i+1 == hi->masks->used) {
1847 print_mask_buff:
1848 buff[pos-1] = 0;
1849 reply("NSMSG_HANDLEINFO_MASKS", buff);
1850 pos = 0;
1851 }
1852 }
1853 } else {
1854 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1855 }
1856
2fa83595 1857 if (hi->sslfps->used) {
1858 for (i=0; i < hi->sslfps->used; i++) {
1859 herelen = strlen(hi->sslfps->list[i]);
1860 if (pos + herelen + 1 > ArrayLength(buff)) {
1861 i--;
1862 goto print_sslfp_buff;
1863 }
1864 memcpy(buff+pos, hi->sslfps->list[i], herelen);
1865 pos += herelen; buff[pos++] = ' ';
1866 if (i+1 == hi->sslfps->used) {
1867 print_sslfp_buff:
1868 buff[pos-1] = 0;
1869 reply("NSMSG_HANDLEINFO_SSLFPS", buff);
1870 pos = 0;
1871 }
1872 }
1873 } else {
1874 reply("NSMSG_HANDLEINFO_SSLFPS", nsmsg_none);
1875 }
1876
5177fd21 1877 if (hi->ignores->used) {
1878 for (i=0; i < hi->ignores->used; i++) {
1879 herelen = strlen(hi->ignores->list[i]);
1880 if (pos + herelen + 1 > ArrayLength(buff)) {
1881 i--;
1882 goto print_ignore_buff;
1883 }
1884 memcpy(buff+pos, hi->ignores->list[i], herelen);
1885 pos += herelen; buff[pos++] = ' ';
1886 if (i+1 == hi->ignores->used) {
1887 print_ignore_buff:
1888 buff[pos-1] = 0;
1889 reply("NSMSG_HANDLEINFO_IGNORES", buff);
1890 pos = 0;
1891 }
1892 }
1893 } else {
1894 reply("NSMSG_HANDLEINFO_IGNORES", nsmsg_none);
1895 }
1896
d76ed9a9 1897 if (hi->channels) {
1136f709 1898 struct userData *chan, *next;
d76ed9a9 1899 char *name;
1900
1136f709 1901 for (chan = hi->channels; chan; chan = next) {
1902 next = chan->u_next;
1903 name = chan->channel->channel->name;
d76ed9a9 1904 herelen = strlen(name);
1905 if (pos + herelen + 7 > ArrayLength(buff)) {
1136f709 1906 next = chan;
d76ed9a9 1907 goto print_chans_buff;
1908 }
1136f709 1909 if (IsUserSuspended(chan))
d76ed9a9 1910 buff[pos++] = '-';
1136f709 1911 pos += sprintf(buff+pos, "%s:%s ", user_level_name_from_level(chan->access), name);
d76ed9a9 1912 if (next == NULL) {
1913 print_chans_buff:
1914 buff[pos-1] = 0;
1915 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1916 pos = 0;
1917 }
1918 }
1919 } else {
1920 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1921 }
1922
1923 for (target = hi->users; target; target = next_un) {
1924 herelen = strlen(target->nick);
1925 if (pos + herelen + 1 > ArrayLength(buff)) {
1926 next_un = target;
1927 goto print_cnick_buff;
1928 } else {
1929 next_un = target->next_authed;
1930 }
1931 memcpy(buff+pos, target->nick, herelen);
1932 pos += herelen; buff[pos++] = ' ';
1933 if (!next_un) {
1934 print_cnick_buff:
1935 buff[pos-1] = 0;
1936 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1937 pos = 0;
1938 }
1939 }
1940
de9510bc 1941 reply("NSMSG_HANDLEINFO_END");
1136f709 1942 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
d76ed9a9 1943}
1944
1945static NICKSERV_FUNC(cmd_userinfo)
1946{
1947 struct userNode *target;
1948
1949 NICKSERV_MIN_PARMS(2);
1950 if (!(target = GetUserH(argv[1]))) {
1951 reply("MSG_NICK_UNKNOWN", argv[1]);
1952 return 0;
1953 }
1954 if (target->handle_info)
1955 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1956 else
1957 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1958 return 1;
1959}
1960
1961static NICKSERV_FUNC(cmd_nickinfo)
1962{
1963 struct nick_info *ni;
ae275267 1964 char buff[400];
d76ed9a9 1965
1966 NICKSERV_MIN_PARMS(2);
1967 if (!(ni = get_nick_info(argv[1]))) {
1968 reply("MSG_NICK_UNKNOWN", argv[1]);
1969 return 0;
1970 }
ae275267
MB
1971
1972 reply("NSMSG_NICKINFO_ON", ni->nick);
1973 reply("MSG_BAR");
1974 reply("NSMSG_NICKINFO_REGGED", ctime(&ni->registered));
1975
1976 if (!GetUserH(ni->nick)) {
d18d752e 1977 intervalString(buff, now - ni->lastseen, user->handle_info);
ae275267
MB
1978 reply("NSMSG_NICKINFO_LASTSEEN", buff);
1979 } else {
1980 reply("NSMSG_NICKINFO_LASTSEEN_NOW");
1981 }
1982
1983 reply("NSMSG_NICKINFO_OWNER", ni->owner->handle);
1984
1985 reply("NSMSG_NICKINFO_END");
1986
d76ed9a9 1987 return 1;
1988}
1989
1990static NICKSERV_FUNC(cmd_rename_handle)
1991{
1992 struct handle_info *hi;
a45e6ec7 1993 struct userNode *uNode;
57692f5e 1994 char *old_handle;
d76ed9a9 1995 unsigned int nn;
1996
1997 NICKSERV_MIN_PARMS(3);
acb142f0 1998 if(nickserv_conf.force_handles_lowercase)
1999 irc_strtolower(argv[2]);
1136f709 2000 if (!(hi = get_victim_oper(user, argv[1])))
d76ed9a9 2001 return 0;
2002 if (!is_valid_handle(argv[2])) {
2003 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
2004 return 0;
2005 }
2006 if (get_handle_info(argv[2])) {
2007 reply("NSMSG_HANDLE_EXISTS", argv[2]);
2008 return 0;
2009 }
b4065650 2010 if(strlen(argv[2]) > 30)
d0cb2fb6 2011 {
b4065650 2012 reply("NMSG_HANDLE_TOLONG", argv[2], 30);
d0cb2fb6 2013 return 0;
2014 }
73d4cc91 2015#ifdef WITH_LDAP
73d4cc91 2016 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
3c607a5f 2017 int rc;
73d4cc91 2018 if( (rc = ldap_rename_account(hi->handle, argv[2])) != LDAP_SUCCESS) {
2019 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
2020 return 0;
2021 }
2022 }
2023#endif
d76ed9a9 2024
2025 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
2026 hi->handle = strdup(argv[2]);
2027 dict_insert(nickserv_handle_dict, hi->handle, hi);
2028 for (nn=0; nn<rf_list_used; nn++)
3070719a 2029 rf_list[nn](hi, old_handle, rf_list_extra[nn]);
a45e6ec7 2030
2031 if (nickserv_conf.sync_log) {
2032 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2033 irc_rename(uNode, hi->handle);
2034
4ae3fc8b 2035 SyncLog("RENAME %s %s", old_handle, hi->handle);
a45e6ec7 2036 }
2037
d76ed9a9 2038 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
57692f5e 2039 global_message_args(MESSAGE_RECIPIENT_OPERS, "NSMSG_ACCOUNT_RENAMED",
2040 user->handle_info->handle, old_handle, hi->handle);
2041
d76ed9a9 2042 free(old_handle);
2043 return 1;
2044}
2045
2046static failpw_func_t *failpw_func_list;
c8b793cb 2047static void **failpw_func_list_extra;
d76ed9a9 2048static unsigned int failpw_func_size = 0, failpw_func_used = 0;
2049
2050void
c8b793cb 2051reg_failpw_func(failpw_func_t func, void *extra)
d76ed9a9 2052{
2053 if (failpw_func_used == failpw_func_size) {
2054 if (failpw_func_size) {
2055 failpw_func_size <<= 1;
2056 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
c8b793cb 2057 failpw_func_list_extra = realloc(failpw_func_list_extra, failpw_func_size*sizeof(void*));
d76ed9a9 2058 } else {
2059 failpw_func_size = 8;
2060 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
c8b793cb 2061 failpw_func_list_extra = malloc(failpw_func_size*sizeof(void*));
d76ed9a9 2062 }
2063 }
c8b793cb 2064 failpw_func_list[failpw_func_used] = func;
2065 failpw_func_list_extra[failpw_func_used++] = extra;
d76ed9a9 2066}
2067
d9cd0e9d 2068/*
b21e2cfe 2069 * Return hi if the handle/pass pair matches, NULL if it doesnt.
d9cd0e9d 2070 *
2071 * called by nefariouses enhanced AC login-on-connect code
2072 *
2073 */
1679a283 2074struct handle_info *loc_auth(char *sslfp, char *handle, char *password, char *userhost)
d9cd0e9d 2075{
d57dba72 2076 int wildmask = 0, auth = 0;
2077 int used, maxlogins;
b21e2cfe 2078 unsigned int ii;
d9cd0e9d 2079 struct handle_info *hi;
d9cd0e9d 2080 struct userNode *other;
4b8ccfeb 2081#ifdef WITH_LDAP
e0ee1ed8 2082 int ldap_result = LDAP_SUCCESS;
bec5dd26 2083 char *email = NULL;
4b8ccfeb 2084#endif
d57dba72 2085
d9cd0e9d 2086 hi = dict_find(nickserv_handle_dict, handle, NULL);
d57dba72 2087
c814d8cd 2088#ifdef WITH_LDAP
d57dba72 2089 if (nickserv_conf.ldap_enable) {
c814d8cd 2090 ldap_result = ldap_check_auth(handle, password);
d57dba72 2091 if (!hi && (ldap_result != LDAP_SUCCESS))
1f459b76 2092 return NULL;
d57dba72 2093 if (ldap_result == LDAP_SUCCESS) {
2094 /* Mark auth as successful */
2095 auth++;
1f459b76 2096 }
d57dba72 2097
2098 if (!hi && (ldap_result == LDAP_SUCCESS) && nickserv_conf.ldap_autocreate) {
2099 /* user not found, but authed to ldap successfully..
2100 * create the account.
2101 */
2102 char *mask;
2103 int rc;
2104
2105 /* Add a *@* mask */
2106 /* TODO if userhost is not null, build mask based on that. */
2107 if(nickserv_conf.default_hostmask)
2108 mask = "*@*";
2109 else
2110 return NULL; /* They dont have a *@* mask so they can't loc */
2111
2112 if(!(hi = nickserv_register(NULL, NULL, handle, password, 0))) {
2113 return 0; /* couldn't add the user for some reason */
bec5dd26 2114 }
d57dba72 2115
2116 if((rc = ldap_get_user_info(handle, &email) != LDAP_SUCCESS))
2117 {
2118 if(nickserv_conf.email_required) {
2119 return 0;
2120 }
2121 }
2122 if(email) {
2123 nickserv_set_email_addr(hi, email);
2124 free(email);
2125 }
2126 if(mask) {
2127 char* mask_canonicalized = canonicalize_hostmask(strdup(mask));
2128 string_list_append(hi->masks, mask_canonicalized);
2129 }
2130 if(nickserv_conf.sync_log)
2131 SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, "@", handle);
2132 }
c814d8cd 2133 }
2134#endif
2135
d57dba72 2136 /* hi should now be a valid handle, if not return NULL */
2137 if (!hi)
b21e2cfe 2138 return NULL;
1679a283 2139
d57dba72 2140#ifdef WITH_LDAP
2141 if (password && *password && !nickserv_conf.ldap_enable) {
2142#else
2143 if (password && *password) {
2144#endif
2145 if (checkpass(password, hi->passwd))
2146 auth++;
2147 }
2148
2149 if (!auth && sslfp && *sslfp && hi->sslfps->used) {
1679a283 2150 /* If any SSL fingerprint matches, allow it. */
2151 for (ii=0; ii<hi->sslfps->used; ii++) {
2152 if (!irccasecmp(sslfp, hi->sslfps->list[ii])) {
d57dba72 2153 auth++;
1679a283 2154 break;
2155 }
2156 }
1679a283 2157 }
d57dba72 2158
2159 /* Auth should have succeeded by this point */
2160 if (!auth)
2161 return NULL;
1679a283 2162
b21e2cfe 2163 /* We don't know the users hostname, or anything because they
2164 * havn't registered yet. So we can only allow LOC if your
2165 * account has *@* as a hostmask.
7dd05763 2166 *
2167 * UPDATE: New nefarious LOC supports u@h
b21e2cfe 2168 */
7dd05763 2169 if(userhost) {
2170 char *buf;
2171 char *ident;
2172 char *realhost;
2173 char *ip;
2174 char *uh;
2175 char *ui;
2176
2177 buf = strdup(userhost);
2178 ident = mysep(&buf, "@");
2179 realhost = mysep(&buf, ":");
2180 ip = mysep(&buf, ":");
2181 if(!ip || !realhost || !ident) {
2182 free(buf);
2183 return NULL; /* Invalid AC request, just quit */
2184 }
2185 uh = malloc(strlen(userhost));
2186 ui = malloc(strlen(userhost));
2187 sprintf(uh, "%s@%s", ident, realhost);
2188 sprintf(ui, "%s@%s", ident, ip);
d57dba72 2189 for (ii=0; ii<hi->masks->used; ii++)
7dd05763 2190 {
2191 if(match_ircglob(uh, hi->masks->list[ii])
2192 || match_ircglob(ui, hi->masks->list[ii]))
2193 {
2194 wildmask++;
2195 break;
2196 }
2197 }
2198 free(buf);
2199 free(uh);
2200 free(ui);
2201 }
2202 else {
2203
2204 for (ii=0; ii<hi->masks->used; ii++)
2205 {
2206 if (!strcmp(hi->masks->list[ii], "*@*"))
2207 {
2208 wildmask++;
2209 break;
2210 }
2211 }
d9cd0e9d 2212 }
b21e2cfe 2213 if(wildmask < 1)
2214 return NULL;
2215
d9cd0e9d 2216 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
b21e2cfe 2217 return NULL;
d9cd0e9d 2218 }
c814d8cd 2219
d9cd0e9d 2220 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
d9cd0e9d 2221 for (used = 0, other = hi->users; other; other = other->next_authed) {
2222 if (++used >= maxlogins) {
b21e2cfe 2223 return NULL;
d9cd0e9d 2224 }
2225 }
5aef35cf 2226 /* TODO - Add LOGGING to this function so LOC's are logged.. */
b21e2cfe 2227 return hi;
d9cd0e9d 2228}
2229
d76ed9a9 2230static NICKSERV_FUNC(cmd_auth)
2231{
2232 int pw_arg, used, maxlogins;
2233 struct handle_info *hi;
2234 const char *passwd;
393a3e56 2235 const char *handle;
d76ed9a9 2236 struct userNode *other;
39edf54a 2237#ifdef WITH_LDAP
a5a8a781 2238 int ldap_result = LDAP_OTHER;
39edf54a 2239 char *email = NULL;
2240#endif
d76ed9a9 2241
2242 if (user->handle_info) {
2243 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2244 return 0;
2245 }
2246 if (IsStamped(user)) {
2247 /* Unauthenticated users might still have been stamped
2248 previously and could therefore have a hidden host;
2249 do not allow them to authenticate. */
2250 reply("NSMSG_STAMPED_AUTH");
2251 return 0;
2252 }
2253 if (argc == 3) {
393a3e56 2254 passwd = argv[2];
2255 handle = argv[1];
fe08d345 2256 pw_arg = 2;
d76ed9a9 2257 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
393a3e56 2258 } else if (argc == 2) {
2259 passwd = argv[1];
fe08d345 2260 pw_arg = 1;
d76ed9a9 2261 if (nickserv_conf.disable_nicks) {
393a3e56 2262 hi = get_handle_info(user->nick);
d76ed9a9 2263 } else {
2264 /* try to look up their handle from their nick */
e166c31b 2265 /* TODO: handle ldap auth on nickserv style networks, too */
d76ed9a9 2266 struct nick_info *ni;
2267 ni = get_nick_info(user->nick);
2268 if (!ni) {
2269 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
2270 return 0;
2271 }
2272 hi = ni->owner;
2273 }
393a3e56 2274 if (hi) {
2275 handle = hi->handle;
2276 } else {
2277 handle = user->nick;
2278 }
d76ed9a9 2279 } else {
2280 reply("MSG_MISSING_PARAMS", argv[0]);
b1bf690d 2281 svccmd_send_help_brief(user, nickserv, cmd);
d76ed9a9 2282 return 0;
2283 }
393a3e56 2284
2285#ifdef WITH_LDAP
2286 if(strchr(argv[1], '<') || strchr(handle, '>')) {
2287 reply("NSMSG_NO_ANGLEBRACKETS");
2288 return 0;
2289 }
e7fe8840 2290 if (!is_valid_handle(handle)) {
393a3e56 2291 reply("NSMSG_BAD_HANDLE", handle);
2292 return 0;
2293 }
2294
2295 if(nickserv_conf.ldap_enable) {
2296 ldap_result = ldap_check_auth(handle, passwd);
2297 /* Get the users email address and update it */
2298 if(ldap_result == LDAP_SUCCESS) {
2299 int rc;
2300 if((rc = ldap_get_user_info(handle, &email) != LDAP_SUCCESS))
2301 {
2302 if(nickserv_conf.email_required) {
2303 reply("NSMSG_LDAP_FAIL_GET_EMAIL", ldap_err2string(rc));
2304 return 0;
2305 }
2306 }
2307 }
2308 else if(ldap_result != LDAP_INVALID_CREDENTIALS) {
2309 reply("NSMSG_LDAP_FAIL", ldap_err2string(ldap_result));
2310 return 0;
2311 }
2312 }
2313#endif
2314
d76ed9a9 2315 if (!hi) {
39edf54a 2316#ifdef WITH_LDAP
a5a8a781 2317 if(nickserv_conf.ldap_enable && ldap_result == LDAP_SUCCESS && nickserv_conf.ldap_autocreate) {
e166c31b 2318 /* user not found, but authed to ldap successfully..
2319 * create the account.
e166c31b 2320 */
39edf54a 2321 char *mask;
38bff5f0 2322 if(!(hi = nickserv_register(user, user, argv[1], argv[2], 0))) {
39edf54a 2323 reply("NSMSG_UNABLE_TO_ADD");
2324 return 0; /* couldn't add the user for some reason */
2325 }
2326 /* Add a *@* mask */
2327 if(nickserv_conf.default_hostmask)
2328 mask = "*@*";
2329 else
2330 mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2331
2332 if(mask) {
2333 char* mask_canonicalized = canonicalize_hostmask(strdup(mask));
2334 string_list_append(hi->masks, mask_canonicalized);
2335 }
2336 if(email) {
2337 nickserv_set_email_addr(hi, email);
8dc06852 2338 free(email);
39edf54a 2339 }
2340 if(nickserv_conf.sync_log)
2341 SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, email ? email : "@", user->info);
e166c31b 2342 }
39edf54a 2343 else {
2344#endif
2345 reply("NSMSG_HANDLE_NOT_FOUND");
2346 return 0;
2347#ifdef WITH_LDAP
2348 }
2349#endif
d76ed9a9 2350 }
2351 /* Responses from here on look up the language used by the handle they asked about. */
d76ed9a9 2352 if (!valid_user_for(user, hi)) {
2353 if (hi->email_addr && nickserv_conf.email_enabled)
2354 send_message_type(4, user, cmd->parent->bot,
2355 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
2356 hi->handle);
2357 else
2358 send_message_type(4, user, cmd->parent->bot,
2359 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
2360 hi->handle);
2361 argv[pw_arg] = "BADMASK";
2362 return 1;
2363 }
e166c31b 2364#ifdef WITH_LDAP
2fa83595 2365 if(( ( nickserv_conf.ldap_enable && ldap_result == LDAP_INVALID_CREDENTIALS ) ||
2366 ( (!nickserv_conf.ldap_enable) && (!checkpass(passwd, hi->passwd)) ) ) && !valid_user_sslfp(user, hi)) {
e166c31b 2367#else
2fa83595 2368 if (!checkpass(passwd, hi->passwd) && !valid_user_sslfp(user, hi)) {
e166c31b 2369#endif
d76ed9a9 2370 unsigned int n;
2371 send_message_type(4, user, cmd->parent->bot,
2372 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
2373 argv[pw_arg] = "BADPASS";
c8b793cb 2374 for (n=0; n<failpw_func_used; n++)
2375 failpw_func_list[n](user, hi, failpw_func_list_extra[n]);
d76ed9a9 2376 if (nickserv_conf.autogag_enabled) {
2377 if (!user->auth_policer.params) {
2378 user->auth_policer.last_req = now;
2379 user->auth_policer.params = nickserv_conf.auth_policer_params;
2380 }
2381 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
2382 char *hostmask;
2383 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
2384 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
2385 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
2386 free(hostmask);
2387 argv[pw_arg] = "GAGGED";
2388 }
2389 }
2390 return 1;
2391 }
2392 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2393 send_message_type(4, user, cmd->parent->bot,
2394 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
2395 argv[pw_arg] = "SUSPENDED";
2396 return 1;
2397 }
2398 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2399 for (used = 0, other = hi->users; other; other = other->next_authed) {
2400 if (++used >= maxlogins) {
2401 send_message_type(4, user, cmd->parent->bot,
2402 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
2403 maxlogins);
2404 argv[pw_arg] = "MAXLOGINS";
2405 return 1;
2406 }
2407 }
2408
2409 set_user_handle_info(user, hi, 1);
2410 if (nickserv_conf.email_required && !hi->email_addr)
2411 reply("NSMSG_PLEASE_SET_EMAIL");
2412 if (!is_secure_password(hi->handle, passwd, NULL))
2413 reply("NSMSG_WEAK_PASSWORD");
2414 if (hi->passwd[0] != '$')
2415 cryptpass(passwd, hi->passwd);
ac3bdc8d 2416
5a1daaab 2417 /* If a channel was waiting for this user to auth,
2418 * finish adding them */
ac3bdc8d 2419 process_adduser_pending(user);
5a1daaab 2420
0f6fe38c 2421 reply("NSMSG_AUTH_SUCCESS");
2422
2423
7fdb7639 2424 /* Set +x if autohide is on */
2425 if(HANDLE_FLAGGED(hi, AUTOHIDE))
2426 irc_umode(user, "+x");
2427
1136f709 2428 if (!hi->masks->used) {
2429 irc_in_addr_t ip;
2430 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
2431 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
2432 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
2433 }
2434
c3915bdc 2435 /* Wipe out the pass for the logs */
d76ed9a9 2436 argv[pw_arg] = "****";
2437 return 1;
2438}
2439
2440static allowauth_func_t *allowauth_func_list;
99c332f8 2441static void **allowauth_func_list_extra;
d76ed9a9 2442static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
2443
2444void
99c332f8 2445reg_allowauth_func(allowauth_func_t func, void *extra)
d76ed9a9 2446{
2447 if (allowauth_func_used == allowauth_func_size) {
2448 if (allowauth_func_size) {
2449 allowauth_func_size <<= 1;
2450 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
99c332f8 2451 allowauth_func_list_extra = realloc(allowauth_func_list_extra, allowauth_func_size*sizeof(void*));
d76ed9a9 2452 } else {
2453 allowauth_func_size = 8;
2454 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
99c332f8 2455 allowauth_func_list_extra = malloc(allowauth_func_size*sizeof(void*));
d76ed9a9 2456 }
2457 }
99c332f8 2458 allowauth_func_list[allowauth_func_used] = func;
2459 allowauth_func_list_extra[allowauth_func_used++] = extra;
d76ed9a9 2460}
2461
2462static NICKSERV_FUNC(cmd_allowauth)
2463{
2464 struct userNode *target;
2465 struct handle_info *hi;
2466 unsigned int n;
2467
2468 NICKSERV_MIN_PARMS(2);
2469 if (!(target = GetUserH(argv[1]))) {
2470 reply("MSG_NICK_UNKNOWN", argv[1]);
2471 return 0;
2472 }
2473 if (target->handle_info) {
2474 reply("NSMSG_USER_PREV_AUTH", target->nick);
2475 return 0;
2476 }
2477 if (IsStamped(target)) {
2478 /* Unauthenticated users might still have been stamped
2479 previously and could therefore have a hidden host;
2480 do not allow them to authenticate to an account. */
2481 reply("NSMSG_USER_PREV_STAMP", target->nick);
2482 return 0;
2483 }
2484 if (argc == 2)
2485 hi = NULL;
2486 else if (!(hi = get_handle_info(argv[2]))) {
2487 reply("MSG_HANDLE_UNKNOWN", argv[2]);
2488 return 0;
2489 }
2490 if (hi) {
2491 if (hi->opserv_level > user->handle_info->opserv_level) {
2492 reply("MSG_USER_OUTRANKED", hi->handle);
2493 return 0;
2494 }
2495 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
2496 || (hi->opserv_level > 0))
2497 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
2498 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
2499 return 0;
2500 }
2501 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
2502 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
2503 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
2504 if (nickserv_conf.email_enabled)
2505 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
2506 } else {
2507 if (dict_remove(nickserv_allow_auth_dict, target->nick))
2508 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
2509 else
2510 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
2511 }
2512 for (n=0; n<allowauth_func_used; n++)
99c332f8 2513 allowauth_func_list[n](user, target, hi, allowauth_func_list_extra[n]);
d76ed9a9 2514 return 1;
2515}
2516
2517static NICKSERV_FUNC(cmd_authcookie)
2518{
2519 struct handle_info *hi;
2520
2521 NICKSERV_MIN_PARMS(2);
2522 if (user->handle_info) {
2523 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2524 return 0;
2525 }
2526 if (IsStamped(user)) {
2527 /* Unauthenticated users might still have been stamped
2528 previously and could therefore have a hidden host;
2529 do not allow them to authenticate to an account. */
2530 reply("NSMSG_STAMPED_AUTHCOOKIE");
2531 return 0;
2532 }
2533 if (!(hi = get_handle_info(argv[1]))) {
2534 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2535 return 0;
2536 }
2537 if (!hi->email_addr) {
2538 reply("MSG_SET_EMAIL_ADDR");
2539 return 0;
2540 }
1c0d5243 2541 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL, 0);
d76ed9a9 2542 return 1;
2543}
2544
2545static NICKSERV_FUNC(cmd_delcookie)
2546{
2547 struct handle_info *hi;
2548
2549 hi = user->handle_info;
2550 if (!hi->cookie) {
2551 reply("NSMSG_NO_COOKIE");
2552 return 0;
2553 }
2554 switch (hi->cookie->type) {
2555 case ACTIVATION:
2556 case EMAIL_CHANGE:
2557 reply("NSMSG_MUST_TIME_OUT");
2558 break;
2559 default:
2560 nickserv_eat_cookie(hi->cookie);
2561 reply("NSMSG_ATE_COOKIE");
2562 break;
2563 }
2564 return 1;
2565}
2566
34938510 2567static NICKSERV_FUNC(cmd_odelcookie)
2568{
2569 struct handle_info *hi;
2570
2571 NICKSERV_MIN_PARMS(2);
2572
1136f709 2573 if (!(hi = get_victim_oper(user, argv[1])))
34938510 2574 return 0;
2575
2576 if (!hi->cookie) {
2577 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
2578 return 0;
2579 }
2580
eca6aa4f 2581 switch (hi->cookie->type) {
2582 case ACTIVATION:
2583 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
29545775 2584#ifdef WITH_LDAP
2585 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
2586 int rc;
2587 if((rc = ldap_do_modify(hi->handle, hi->cookie->data, NULL)) != LDAP_SUCCESS) {
2588 /* Falied to update password in ldap, but still
2589 * updated it here.. what should we do? */
2590 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
2591 return 0;
2592 }
2593 }
2594#endif
eca6aa4f 2595 if (nickserv_conf.sync_log)
2596 SyncLog("ACCOUNTACC %s", hi->handle);
2597 break;
2598 case PASSWORD_CHANGE:
eca6aa4f 2599 break;
2600 case EMAIL_CHANGE:
eca6aa4f 2601 break;
29545775 2602 case ALLOWAUTH:
2603 break;
eca6aa4f 2604 default:
2605 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2606 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2607 break;
2608 }
2609
34938510 2610 nickserv_eat_cookie(hi->cookie);
2611 reply("NSMSG_ATE_FOREIGN_COOKIE", hi->handle);
2612
2613 return 1;
2614}
2615
d76ed9a9 2616static NICKSERV_FUNC(cmd_resetpass)
2617{
2618 struct handle_info *hi;
2619 char crypted[MD5_CRYPT_LENGTH];
1c0d5243 2620 int weblink;
d76ed9a9 2621
2622 NICKSERV_MIN_PARMS(3);
1c0d5243 2623 if(argc >= 4 && !strcmp(argv[3], "WEBLINK"))
2624 weblink = 1;
2625 else
2626 weblink = 0;
d76ed9a9 2627 if (user->handle_info) {
2628 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2629 return 0;
2630 }
2631 if (IsStamped(user)) {
2632 /* Unauthenticated users might still have been stamped
2633 previously and could therefore have a hidden host;
2634 do not allow them to activate an account. */
2635 reply("NSMSG_STAMPED_RESETPASS");
2636 return 0;
2637 }
2638 if (!(hi = get_handle_info(argv[1]))) {
2639 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2640 return 0;
2641 }
2642 if (!hi->email_addr) {
2643 reply("MSG_SET_EMAIL_ADDR");
2644 return 0;
2645 }
2646 cryptpass(argv[2], crypted);
2647 argv[2] = "****";
1c0d5243 2648 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted, weblink);
d76ed9a9 2649 return 1;
2650}
2651
2652static NICKSERV_FUNC(cmd_cookie)
2653{
2654 struct handle_info *hi;
2655 const char *cookie;
2656
2657 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
2658 cookie = argv[1];
2659 } else {
2660 NICKSERV_MIN_PARMS(3);
2661 if (!(hi = get_handle_info(argv[1]))) {
2662 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2663 return 0;
2664 }
2665 cookie = argv[2];
2666 }
2667
2668 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2669 reply("NSMSG_HANDLE_SUSPENDED");
2670 return 0;
2671 }
2672
2673 if (!hi->cookie) {
2674 reply("NSMSG_NO_COOKIE");
2675 return 0;
2676 }
2677
2678 /* Check validity of operation before comparing cookie to
2679 * prohibit guessing by authed users. */
2680 if (user->handle_info
2681 && (hi->cookie->type != EMAIL_CHANGE)
2682 && (hi->cookie->type != PASSWORD_CHANGE)) {
2683 reply("NSMSG_CANNOT_COOKIE");
2684 return 0;
2685 }
2686
2687 if (strcmp(cookie, hi->cookie->cookie)) {
2688 reply("NSMSG_BAD_COOKIE");
2689 return 0;
2690 }
2691
2692 switch (hi->cookie->type) {
2693 case ACTIVATION:
d6ef86e3 2694#ifdef WITH_LDAP
2695 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
2696 int rc;
2697 if((rc = ldap_do_modify(hi->handle, hi->cookie->data, NULL)) != LDAP_SUCCESS) {
2698 /* Falied to update email in ldap, but still
2699 * updated it here.. what should we do? */
2700 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
2701 return 0;
2702 }
2703 }
2704#endif
d76ed9a9 2705 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2706 set_user_handle_info(user, hi, 1);
2707 reply("NSMSG_HANDLE_ACTIVATED");
8dc1d9ae 2708 if (nickserv_conf.sync_log)
2709 SyncLog("ACCOUNTACC %s", hi->handle);
d76ed9a9 2710 break;
2711 case PASSWORD_CHANGE:
d6ef86e3 2712#ifdef WITH_LDAP
2713 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
2714 int rc;
2715 if((rc = ldap_do_modify(hi->handle, hi->cookie->data, NULL)) != LDAP_SUCCESS) {
2716 /* Falied to update email in ldap, but still
2717 * updated it here.. what should we do? */
2718 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
2719 return 0;
2720 }
2721 }
2722#endif
d76ed9a9 2723 set_user_handle_info(user, hi, 1);
2724 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2725 reply("NSMSG_PASSWORD_CHANGED");
8dc1d9ae 2726 if (nickserv_conf.sync_log)
2727 SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
d76ed9a9 2728 break;
2729 case EMAIL_CHANGE:
24e9e6c3 2730#ifdef WITH_LDAP
2731 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
2732 int rc;
a3ad3ee3 2733 if((rc = ldap_do_modify(hi->handle, NULL, hi->cookie->data)) != LDAP_SUCCESS) {
24e9e6c3 2734 /* Falied to update email in ldap, but still
2735 * updated it here.. what should we do? */
8dc17ddf 2736 reply("NSMSG_LDAP_FAIL_SEND_EMAIL", ldap_err2string(rc));
24e9e6c3 2737 return 0;
2738 }
2739 }
2740#endif
d8d3ee73 2741 if (!hi->email_addr && nickserv_conf.sync_log) {
2742 /*
2743 * This should only happen if an OREGISTER was sent. Require
2744 * email must be enabled! - SiRVulcaN
2745 */
ac5cb8c5 2746 if (nickserv_conf.sync_log)
2747 SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, hi->cookie->data, user->info);
d8d3ee73 2748 }
24e9e6c3 2749
d76ed9a9 2750 nickserv_set_email_addr(hi, hi->cookie->data);
2751 reply("NSMSG_EMAIL_CHANGED");
8dc1d9ae 2752 if (nickserv_conf.sync_log)
2753 SyncLog("EMAILCHANGE %s %s", hi->handle, hi->cookie->data);
d76ed9a9 2754 break;
1136f709 2755 case ALLOWAUTH: {
2756 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
d76ed9a9 2757 set_user_handle_info(user, hi, 1);
1136f709 2758 nickserv_addmask(user, hi, mask);
d76ed9a9 2759 reply("NSMSG_AUTH_SUCCESS");
1136f709 2760 free(mask);
d76ed9a9 2761 break;
1136f709 2762 }
d76ed9a9 2763 default:
2764 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2765 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2766 break;
2767 }
2768
2769 nickserv_eat_cookie(hi->cookie);
2770
a03d6c77 2771 process_adduser_pending(user);
2772
d76ed9a9 2773 return 1;
2774}
2775
2776static NICKSERV_FUNC(cmd_oregnick) {
2777 const char *nick;
2778 struct handle_info *target;
2779 struct nick_info *ni;
2780
2781 NICKSERV_MIN_PARMS(3);
2782 if (!(target = modcmd_get_handle_info(user, argv[1])))
2783 return 0;
2784 nick = argv[2];
2785 if (!is_registerable_nick(nick)) {
2786 reply("NSMSG_BAD_NICK", nick);
2787 return 0;
2788 }
2789 ni = dict_find(nickserv_nick_dict, nick, NULL);
2790 if (ni) {
2791 reply("NSMSG_NICK_EXISTS", nick);
2792 return 0;
2793 }
2794 register_nick(nick, target);
2795 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2796 return 1;
2797}
2798
2799static NICKSERV_FUNC(cmd_regnick) {
2800 unsigned n;
2801 struct nick_info *ni;
2802
2803 if (!is_registerable_nick(user->nick)) {
2804 reply("NSMSG_BAD_NICK", user->nick);
2805 return 0;
2806 }
2807 /* count their nicks, see if it's too many */
2808 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2809 if (n >= nickserv_conf.nicks_per_handle) {
2810 reply("NSMSG_TOO_MANY_NICKS");
2811 return 0;
2812 }
2813 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2814 if (ni) {
2815 reply("NSMSG_NICK_EXISTS", user->nick);
2816 return 0;
2817 }
2818 register_nick(user->nick, user->handle_info);
2819 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2820 return 1;
2821}
2822
2823static NICKSERV_FUNC(cmd_pass)
2824{
2825 struct handle_info *hi;
c814d8cd 2826 char *old_pass, *new_pass;
8dc17ddf 2827 char crypted[MD5_CRYPT_LENGTH+1];
4b8ccfeb 2828#ifdef WITH_LDAP
c814d8cd 2829 int ldap_result;
4b8ccfeb 2830#endif
d76ed9a9 2831
2832 NICKSERV_MIN_PARMS(3);
2833 hi = user->handle_info;
2834 old_pass = argv[1];
2835 new_pass = argv[2];
2836 argv[2] = "****";
2837 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
c814d8cd 2838
2839#ifdef WITH_LDAP
2840 if(nickserv_conf.ldap_enable) {
2841 ldap_result = ldap_check_auth(hi->handle, old_pass);
2842 if(ldap_result != LDAP_SUCCESS) {
2843 if(ldap_result == LDAP_INVALID_CREDENTIALS)
2844 reply("NSMSG_PASSWORD_INVALID");
2845 else
2846 reply("NSMSG_LDAP_FAIL", ldap_err2string(ldap_result));
2847 return 0;
2848 }
2849 }else
2850#endif
d76ed9a9 2851 if (!checkpass(old_pass, hi->passwd)) {
2852 argv[1] = "BADPASS";
2853 reply("NSMSG_PASSWORD_INVALID");
2854 return 0;
2855 }
8dc17ddf 2856 cryptpass(new_pass, crypted);
c814d8cd 2857#ifdef WITH_LDAP
73d4cc91 2858 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
2859 int rc;
8dc17ddf 2860 if((rc = ldap_do_modify(hi->handle, crypted, NULL)) != LDAP_SUCCESS) {
24e9e6c3 2861 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
73d4cc91 2862 return 0;
2863 }
2864 }
2865#endif
8dc17ddf 2866 //cryptpass(new_pass, hi->passwd);
2867 strcpy(hi->passwd, crypted);
8dc1d9ae 2868 if (nickserv_conf.sync_log)
2869 SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
d76ed9a9 2870 argv[1] = "****";
2871 reply("NSMSG_PASS_SUCCESS");
2872 return 1;
2873}
2874
2875static int
1136f709 2876nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
d76ed9a9 2877{
2878 unsigned int i;
2879 char *new_mask = canonicalize_hostmask(strdup(mask));
2880 for (i=0; i<hi->masks->used; i++) {
2881 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1136f709 2882 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
d76ed9a9 2883 free(new_mask);
2884 return 0;
2885 }
2886 }
2887 string_list_append(hi->masks, new_mask);
1136f709 2888 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
d76ed9a9 2889 return 1;
2890}
2891
2892static NICKSERV_FUNC(cmd_addmask)
2893{
2894 if (argc < 2) {
2895 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1136f709 2896 int res = nickserv_addmask(user, user->handle_info, mask);
d76ed9a9 2897 free(mask);
2898 return res;
2899 } else {
2900 if (!is_gline(argv[1])) {
2901 reply("NSMSG_MASK_INVALID", argv[1]);
2902 return 0;
2903 }
1136f709 2904 return nickserv_addmask(user, user->handle_info, argv[1]);
d76ed9a9 2905 }
2906}
2907
2908static NICKSERV_FUNC(cmd_oaddmask)
2909{
2910 struct handle_info *hi;
2911
2912 NICKSERV_MIN_PARMS(3);
1136f709 2913 if (!(hi = get_victim_oper(user, argv[1])))
d76ed9a9 2914 return 0;
1136f709 2915 return nickserv_addmask(user, hi, argv[2]);
d76ed9a9 2916}
2917
2918static int
1136f709 2919nickserv_delmask(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
d76ed9a9 2920{
2921 unsigned int i;
2922 for (i=0; i<hi->masks->used; i++) {
2923 if (!strcmp(del_mask, hi->masks->list[i])) {
2924 char *old_mask = hi->masks->list[i];
1136f709 2925 if (hi->masks->used == 1 && !force) {
c092fcad 2926 reply("NSMSG_DELMASK_NOTLAST");
d76ed9a9 2927 return 0;
2928 }
2929 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
c092fcad 2930 reply("NSMSG_DELMASK_SUCCESS", old_mask);
d76ed9a9 2931 free(old_mask);
2932 return 1;
2933 }
2934 }
c092fcad 2935 reply("NSMSG_DELMASK_NOT_FOUND");
d76ed9a9 2936 return 0;
2937}
2938
2939static NICKSERV_FUNC(cmd_delmask)
2940{
2941 NICKSERV_MIN_PARMS(2);
1136f709 2942 return nickserv_delmask(cmd, user, user->handle_info, argv[1], 0);
d76ed9a9 2943}
2944
2945static NICKSERV_FUNC(cmd_odelmask)
2946{
2947 struct handle_info *hi;
2948 NICKSERV_MIN_PARMS(3);
1136f709 2949 if (!(hi = get_victim_oper(user, argv[1])))
d76ed9a9 2950 return 0;
1136f709 2951 return nickserv_delmask(cmd, user, hi, argv[2], 1);
d76ed9a9 2952}
2953
2fa83595 2954static int
2955nickserv_addsslfp(struct userNode *user, struct handle_info *hi, const char *sslfp)
2956{
2957 unsigned int i;
2958 char *new_sslfp = strdup(sslfp);
2959 for (i=0; i<hi->sslfps->used; i++) {
2960 if (!irccasecmp(new_sslfp, hi->sslfps->list[i])) {
2961 send_message(user, nickserv, "NSMSG_ADDSSLFP_ALREADY", new_sslfp);
2962 free(new_sslfp);
2963 return 0;
2964 }
2965 }
2966 string_list_append(hi->sslfps, new_sslfp);
2967 send_message(user, nickserv, "NSMSG_ADDSSLFP_SUCCESS", new_sslfp);
2968 return 1;
2969}
2970
2971static NICKSERV_FUNC(cmd_addsslfp)
2972{
2973 NICKSERV_MIN_PARMS((user->sslfp ? 1 : 2));
2974 if ((argc < 2) && (user->sslfp)) {
2975 int res = nickserv_addsslfp(user, user->handle_info, user->sslfp);
2976 return res;
2977 } else {
2978 return nickserv_addsslfp(user, user->handle_info, argv[1]);
2979 }
2980}
2981
2982static NICKSERV_FUNC(cmd_oaddsslfp)
2983{
2984 struct handle_info *hi;
2985
2986 NICKSERV_MIN_PARMS(3);
2987 if (!(hi = get_victim_oper(user, argv[1])))
2988 return 0;
2989 return nickserv_addsslfp(user, hi, argv[2]);
2990}
2991
2992static int
2993nickserv_delsslfp(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, const char *del_sslfp)
2994{
2995 unsigned int i;
2996 for (i=0; i<hi->sslfps->used; i++) {
2997 if (!irccasecmp(del_sslfp, hi->sslfps->list[i])) {
2998 char *old_sslfp = hi->sslfps->list[i];
2999 hi->sslfps->list[i] = hi->sslfps->list[--hi->sslfps->used];
3000 reply("NSMSG_DELSSLFP_SUCCESS", old_sslfp);
3001 free(old_sslfp);
3002 return 1;
3003 }
3004 }
3005 reply("NSMSG_DELSSLFP_NOT_FOUND");
3006 return 0;
3007}
3008
3009static NICKSERV_FUNC(cmd_delsslfp)
3010{
3011 NICKSERV_MIN_PARMS((user->sslfp ? 1 : 2));
3012 if ((argc < 2) && (user->sslfp)) {
3013 return nickserv_delsslfp(cmd, user, user->handle_info, user->sslfp);
3014 } else {
3015 return nickserv_delsslfp(cmd, user, user->handle_info, argv[1]);
3016 }
3017}
3018
3019static NICKSERV_FUNC(cmd_odelsslfp)
3020{
3021 struct handle_info *hi;
3022 NICKSERV_MIN_PARMS(3);
3023 if (!(hi = get_victim_oper(user, argv[1])))
3024 return 0;
3025 return nickserv_delsslfp(cmd, user, hi, argv[2]);
3026}
3027
d76ed9a9 3028int
3029nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
3030 unsigned int nn, add = 1, pos;
3031 unsigned long added, removed, flag;
3032
3033 for (added=removed=nn=0; str[nn]; nn++) {
3034 switch (str[nn]) {
3035 case '+': add = 1; break;
3036 case '-': add = 0; break;
3037 default:
3038 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
3039 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
3040 return 0;
3041 }
3042 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
3043 /* cheesy avoidance of looking up the flag name.. */
3044 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
3045 return 0;
3046 }
3047 flag = 1 << (pos - 1);
3048 if (add)
3049 added |= flag, removed &= ~flag;
3050 else
3051 removed |= flag, added &= ~flag;
3052 break;
3053 }
3054 }
3055 *padded = added;
3056 *premoved = removed;
3057 return 1;
3058}
3059
3060static int
3061nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
3062{
3063 unsigned long before, after, added, removed;
3064 struct userNode *uNode;
3065
3066 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
3067 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
3068 return 0;
3069 hi->flags = (hi->flags | added) & ~removed;
3070 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
3071
3072 /* Strip helping flag if they're only a support helper and not
3073 * currently in #support. */
3074 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
3075 struct channelList *schannels;
3076 unsigned int ii;
3077 schannels = chanserv_support_channels();
1136f709 3078 for (ii = 0; ii < schannels->used; ++ii)
3079 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
d76ed9a9 3080 break;
1136f709 3081 if (ii == schannels->used)
d76ed9a9 3082 HANDLE_CLEAR_FLAG(hi, HELPING);
3083 }
3084
3085 if (after && !before) {
3086 /* Add user to current helper list. */
3087 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
3088 userList_append(&curr_helpers, uNode);
3089 } else if (!after && before) {
3090 /* Remove user from current helper list. */
3091 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
3092 userList_remove(&curr_helpers, uNode);
3093 }
3094
3095 return 1;
3096}
3097
3098static void
c092fcad 3099set_list(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, int override)
d76ed9a9 3100{
3101 option_func_t *opt;
3102 unsigned int i;
3103 char *set_display[] = {
338a82b5 3104 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
0f6fe38c 3105 "EMAIL", "ANNOUNCEMENTS", "AUTOHIDE", "MAXLOGINS", "LANGUAGE",
0b587959 3106 "FAKEHOST", "TITLE", "EPITHET", "ADVANCED"
d76ed9a9 3107 };
3108
c092fcad 3109 reply("NSMSG_SETTING_LIST");
3110 reply("NSMSG_SETTING_LIST_HEADER");
d76ed9a9 3111
3112 /* Do this so options are presented in a consistent order. */
3113 for (i = 0; i < ArrayLength(set_display); ++i)
3114 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
886bca7c 3115 opt(cmd, user, hi, override, 0, 0, NULL);
c092fcad 3116 reply("NSMSG_SETTING_LIST_END");
d76ed9a9 3117}
3118
3119static NICKSERV_FUNC(cmd_set)
3120{
3121 struct handle_info *hi;
3122 option_func_t *opt;
3123
3124 hi = user->handle_info;
3125 if (argc < 2) {
c092fcad 3126 set_list(cmd, user, hi, 0);
d76ed9a9 3127 return 1;
3128 }
3129 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
3130 reply("NSMSG_INVALID_OPTION", argv[1]);
3131 return 0;
3132 }
886bca7c 3133 return opt(cmd, user, hi, 0, 0, argc-1, argv+1);
d76ed9a9 3134}
3135
3136static NICKSERV_FUNC(cmd_oset)
3137{
3138 struct handle_info *hi;
3139 option_func_t *opt;
3140
3141 NICKSERV_MIN_PARMS(2);
3142
1136f709 3143 if (!(hi = get_victim_oper(user, argv[1])))
d76ed9a9 3144 return 0;
3145
3146 if (argc < 3) {
c092fcad 3147 set_list(cmd, user, hi, 0);
d76ed9a9 3148 return 1;
3149 }
3150
3151 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
3152 reply("NSMSG_INVALID_OPTION", argv[2]);
3153 return 0;
3154 }
3155
886bca7c 3156 return opt(cmd, user, hi, 1, 0, argc-2, argv+2);
d76ed9a9 3157}
3158
3159static OPTION_FUNC(opt_info)
3160{
3161 const char *info;
3162 if (argc > 1) {
3163 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
3164 free(hi->infoline);
3165 hi->infoline = NULL;
3166 } else {
3167 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
3168 }
3169 }
3170
3171 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
886bca7c
MB
3172 if (!(noreply))
3173 reply("NSMSG_SET_INFO", info);
d76ed9a9 3174 return 1;
3175}
3176
3177static OPTION_FUNC(opt_width)
3178{
3179 if (argc > 1)
3180 hi->screen_width = strtoul(argv[1], NULL, 0);
3181
3182 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
3183 hi->screen_width = MIN_LINE_SIZE;
3184 else if (hi->screen_width > MAX_LINE_SIZE)
3185 hi->screen_width = MAX_LINE_SIZE;
3186
886bca7c
MB
3187 if (!(noreply))
3188 reply("NSMSG_SET_WIDTH", hi->screen_width);
d76ed9a9 3189 return 1;
3190}
3191
3192static OPTION_FUNC(opt_tablewidth)
3193{
3194 if (argc > 1)
3195 hi->table_width = strtoul(argv[1], NULL, 0);
3196
3197 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
3198 hi->table_width = MIN_LINE_SIZE;
3199 else if (hi->screen_width > MAX_LINE_SIZE)
3200 hi->table_width = MAX_LINE_SIZE;
3201
886bca7c
MB
3202 if (!(noreply))
3203 reply("NSMSG_SET_TABLEWIDTH", hi->table_width);
d76ed9a9 3204 return 1;
3205}
3206
3207static OPTION_FUNC(opt_color)
3208{
3209 if (argc > 1) {
3210 if (enabled_string(argv[1]))
3211 HANDLE_SET_FLAG(hi, MIRC_COLOR);
3212 else if (disabled_string(argv[1]))
3213 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
3214 else {
886bca7c
MB
3215 if (!(noreply))
3216 reply("MSG_INVALID_BINARY", argv[1]);
d76ed9a9 3217 return 0;
3218 }
3219 }
3220
886bca7c
MB
3221 if (!(noreply))
3222 reply("NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
d76ed9a9 3223 return 1;
3224}
3225
3226static OPTION_FUNC(opt_privmsg)
3227{
3228 if (argc > 1) {
3229 if (enabled_string(argv[1]))
3230 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
3231 else if (disabled_string(argv[1]))
3232 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
3233 else {
886bca7c
MB
3234 if (!(noreply))
3235 reply("MSG_INVALID_BINARY", argv[1]);
d76ed9a9 3236 return 0;
3237 }
3238 }
3239
886bca7c
MB
3240 if (!(noreply))
3241 reply("NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
d76ed9a9 3242 return 1;
3243}
3244
7fdb7639 3245static OPTION_FUNC(opt_autohide)
3246{
3247 if (argc > 1) {
3248 if (enabled_string(argv[1]))
3249 HANDLE_SET_FLAG(hi, AUTOHIDE);
3250 else if (disabled_string(argv[1]))
3251 HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
3252 else {
886bca7c
MB
3253 if (!(noreply))
3254 reply("MSG_INVALID_BINARY", argv[1]);
7fdb7639 3255 return 0;
3256 }
3257 }
3258
886bca7c
MB
3259 if (!(noreply))
3260 reply("NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
7fdb7639 3261 return 1;
3262}
3263
d76ed9a9 3264static OPTION_FUNC(opt_style)
3265{
3266 char *style;
3267
3268 if (argc > 1) {
338a82b5 3269 if (!irccasecmp(argv[1], "Clean"))
3270 hi->userlist_style = HI_STYLE_CLEAN;
3271 else if (!irccasecmp(argv[1], "Advanced"))
3272 hi->userlist_style = HI_STYLE_ADVANCED;
d9896a83 3273 else if (!irccasecmp(argv[1], "Classic"))
3274 hi->userlist_style = HI_STYLE_CLASSIC;
338a82b5 3275 else /* Default to normal */
3276 hi->userlist_style = HI_STYLE_NORMAL;
3277 } /* TODO: give error if unknow style is chosen */
d76ed9a9 3278
3279 switch (hi->userlist_style) {
338a82b5 3280 case HI_STYLE_ADVANCED:
3281 style = "Advanced";
3282 break;
d9896a83 3283 case HI_STYLE_CLASSIC:
3284 style = "Classic";
3285 break;
338a82b5 3286 case HI_STYLE_CLEAN:
3287 style = "Clean";
3288 break;
3289 case HI_STYLE_NORMAL:
3290 default:
3291 style = "Normal";
d76ed9a9 3292 }
3293
886bca7c
MB
3294 if (!(noreply))
3295 reply("NSMSG_SET_STYLE", style);
d76ed9a9 3296 return 1;
3297}
3298
0f6fe38c 3299static OPTION_FUNC(opt_announcements)
3300{
3301 const char *choice;
3302
3303 if (argc > 1) {
3304 if (enabled_string(argv[1]))
3305 hi->announcements = 'y';
3306 else if (disabled_string(argv[1]))
3307 hi->announcements = 'n';
3308 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
3309 hi->announcements = '?';
3310 else {
886bca7c
MB
3311 if (!(noreply))
3312 reply("NSMSG_INVALID_ANNOUNCE", argv[1]);
0f6fe38c 3313 return 0;
3314 }
3315 }
3316
3317 switch (hi->announcements) {
3318 case 'y': choice = user_find_message(user, "MSG_ON"); break;
3319 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
3320 case '?': choice = "default"; break;
3321 default: choice = "unknown"; break;
3322 }
886bca7c
MB
3323 if (!(noreply))
3324 reply("NSMSG_SET_ANNOUNCEMENTS", choice);
0f6fe38c 3325 return 1;
3326}
3327
d76ed9a9 3328static OPTION_FUNC(opt_password)
3329{
8dc17ddf 3330 char crypted[MD5_CRYPT_LENGTH+1];
04cb4dfc 3331 if(argc < 2) {
d6ef86e3 3332 return 0;
3333 }
d76ed9a9 3334 if (!override) {
886bca7c
MB
3335 if (!(noreply))
3336 reply("NSMSG_USE_CMD_PASS");
d76ed9a9 3337 return 0;
3338 }
3339
8dc17ddf 3340 cryptpass(argv[1], crypted);
73d4cc91 3341#ifdef WITH_LDAP
3342 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
3343 int rc;
8dc17ddf 3344 if((rc = ldap_do_modify(hi->handle, crypted, NULL)) != LDAP_SUCCESS) {
886bca7c
MB
3345 if (!(noreply))
3346 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
3347 return 0;
73d4cc91 3348 }
3349 }
3350#endif
8dc17ddf 3351 strcpy(hi->passwd, crypted);
4ae3fc8b 3352 if (nickserv_conf.sync_log)
3353 SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
3354
886bca7c
MB
3355 if (!(noreply))
3356 reply("NSMSG_SET_PASSWORD", "***");
d76ed9a9 3357 return 1;
3358}
3359
3360static OPTION_FUNC(opt_flags)
3361{
3362 char flags[33];
3363 unsigned int ii, flen;
3364
3365 if (!override) {
886bca7c
MB
3366 if (!(noreply))
3367 reply("MSG_SETTING_PRIVILEGED", argv[0]);
d76ed9a9 3368 return 0;
3369 }
3370
3371 if (argc > 1)
3372 nickserv_apply_flags(user, hi, argv[1]);
3373
3374 for (ii = flen = 0; handle_flags[ii]; ii++)
3375 if (hi->flags & (1 << ii))
3376 flags[flen++] = handle_flags[ii];
3377 flags[flen] = '\0';
886bca7c
MB
3378 if (!(noreply)) {
3379 if (hi->flags)
3380 reply("NSMSG_SET_FLAGS", flags);
3381 else
3382 reply("NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
3383 }
d76ed9a9 3384 return 1;
3385}
3386
3387static OPTION_FUNC(opt_email)
3388{
3389 if (argc > 1) {
3390 const char *str;
4c26ef3e 3391 if (!valid_email(argv[1])) {
886bca7c
MB
3392 if (!(noreply))
3393 reply("NSMSG_BAD_EMAIL_ADDR");
d76ed9a9 3394 return 0;
3395 }
1136f709 3396 if ((str = mail_prohibited_address(argv[1]))) {
886bca7c
MB
3397 if (!(noreply))
3398 reply("NSMSG_EMAIL_PROHIBITED", argv[1], str);
d76ed9a9 3399 return 0;
3400 }
886bca7c
MB
3401 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1])) {
3402 if (!(noreply))
3403 reply("NSMSG_EMAIL_SAME");
3404 } else if (!override)
1c0d5243 3405 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1], 0);
d76ed9a9 3406 else {
24e9e6c3 3407#ifdef WITH_LDAP
3408 if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
3409 int rc;
8dc17ddf 3410 if((rc = ldap_do_modify(hi->handle, NULL, argv[1])) != LDAP_SUCCESS) {
886bca7c
MB
3411 if (!(noreply))
3412 reply("NSMSG_LDAP_FAIL", ldap_err2string(rc));
24e9e6c3 3413 return 0;
3414 }
3415 }
3416#endif
d76ed9a9 3417 nickserv_set_email_addr(hi, argv[1]);
3418 if (hi->cookie)
3419 nickserv_eat_cookie(hi->cookie);
886bca7c
MB
3420 if (!(noreply))
3421 reply("NSMSG_SET_EMAIL", visible_email_addr(user, hi));
d76ed9a9 3422 }
886bca7c
MB
3423 } else {
3424 if (!(noreply))
3425 reply("NSMSG_SET_EMAIL", visible_email_addr(user, hi));
3426 }
d76ed9a9 3427 return 1;
3428}
3429
3430static OPTION_FUNC(opt_maxlogins)
3431{
3432 unsigned char maxlogins;
3433 if (argc > 1) {
3434 maxlogins = strtoul(argv[1], NULL, 0);
3435 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
886bca7c
MB
3436 if (!(noreply))
3437 reply("NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
d76ed9a9 3438 return 0;
3439 }
3440 hi->maxlogins = maxlogins;
3441 }
3442 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
886bca7c
MB
3443 if (!(noreply))
3444 reply("NSMSG_SET_MAXLOGINS", maxlogins);
d76ed9a9 3445 return 1;
3446}
3447
0b587959 3448static OPTION_FUNC(opt_advanced)
3449{
3450 if (argc > 1) {
3451 if (enabled_string(argv[1]))
3452 HANDLE_SET_FLAG(hi, ADVANCED);
3453 else if (disabled_string(argv[1]))
3454 HANDLE_CLEAR_FLAG(hi, ADVANCED);
3455 else {
886bca7c
MB
3456 if (!(noreply))
3457 reply("MSG_INVALID_BINARY", argv[1]);
0b587959 3458 return 0;
3459 }
3460 }
3461
886bca7c
MB
3462 if (!(noreply))
3463 reply("NSMSG_SET_ADVANCED", user_find_message(user, HANDLE_FLAGGED(hi, ADVANCED) ? "MSG_ON" : "MSG_OFF"));
0b587959 3464 return 1;
3465}
3466
d76ed9a9 3467static OPTION_FUNC(opt_language)
3468{
3469 struct language *lang;
3470 if (argc > 1) {
3471 lang = language_find(argv[1]);
886bca7c
MB
3472 if (irccasecmp(lang->name, argv[1])) {
3473 if (!(noreply))
3474 reply("NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
3475 }
d76ed9a9 3476 hi->language = lang;
3477 }
886bca7c
MB
3478 if (!(noreply))
3479 reply("NSMSG_SET_LANGUAGE", hi->language->name);
d76ed9a9 3480 return 1;
3481}
3482
1136f709 3483static OPTION_FUNC(opt_karma)
3484{
3485 if (!override) {
886bca7c
MB
3486 if (!(noreply))
3487 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
1136f709 3488 return 0;
3489 }
3490
3491 if (argc > 1) {
3492 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
3493 hi->karma += strtoul(argv[1] + 1, NULL, 10);
3494 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
3495 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
3496 } else {
886bca7c
MB
3497 if (!(noreply))
3498 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
1136f709 3499 }
3500 }
3501
886bca7c
MB
3502 if (!(noreply))
3503 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
1136f709 3504 return 1;
3505}
3506
8a729617 3507/* Called from opserv from cmd_access */
d76ed9a9 3508int
3509oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
3510 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
3511 return 0;
3512 if ((user->handle_info->opserv_level < target->opserv_level)
3513 || ((user->handle_info->opserv_level == target->opserv_level)
3514 && (user->handle_info->opserv_level < 1000))) {
3515 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
3516 return 0;
3517 }
3518 if ((user->handle_info->opserv_level < new_level)
3519 || ((user->handle_info->opserv_level == new_level)
3520 && (user->handle_info->opserv_level < 1000))) {
3521 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
3522 return 0;
3523 }
3524 if (user->handle_info == target) {
3525 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
3526 return 0;
3527 }
8a729617 3528#ifdef WITH_LDAP
2045ae25 3529 if(nickserv_conf.ldap_enable && *(nickserv_conf.ldap_oper_group_dn) && *(nickserv_conf.ldap_admin_dn)) {
8a729617 3530 int rc;
17d4a698 3531 if(new_level > nickserv_conf.ldap_oper_group_level)
8a729617 3532 rc = ldap_add2group(target->handle, nickserv_conf.ldap_oper_group_dn);
3533 else
3534 rc = ldap_delfromgroup(target->handle, nickserv_conf.ldap_oper_group_dn);
87677bd8 3535 if(rc != LDAP_SUCCESS && rc != LDAP_TYPE_OR_VALUE_EXISTS && rc != LDAP_NO_SUCH_ATTRIBUTE) {
8a729617 3536 send_message(user, bot, "NSMSG_LDAP_FAIL", ldap_err2string(rc));
3537 return 0;
3538 }
3539 }
35ea100f 3540 if(nickserv_conf.ldap_enable && *(nickserv_conf.ldap_field_oslevel) && *(nickserv_conf.ldap_admin_dn)) {
3541 int rc;
f3aff201 3542 if((rc = ldap_do_oslevel(target->handle, new_level, target->opserv_level)) != LDAP_SUCCESS) {
35ea100f 3543 send_message(user, bot, "NSMSG_LDAP_FAIL", ldap_err2string(rc));
3544 return 0;
3545 }
3546 }
8a729617 3547#endif
d76ed9a9 3548 if (target->opserv_level == new_level)
3549 return 0;
3550 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
3551 user->handle_info->handle, target->handle, new_level, target->opserv_level);
3552 target->opserv_level = new_level;
3553 return 1;
3554}
3555
3556static OPTION_FUNC(opt_level)
3557{
3558 int res;
3559
3560 if (!override) {
886bca7c
MB
3561 if (!(noreply))
3562 reply("MSG_SETTING_PRIVILEGED", argv[0]);
d76ed9a9 3563 return 0;
3564 }
3565
3566 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
886bca7c
MB
3567 if (!(noreply))
3568 reply("NSMSG_SET_LEVEL", hi->opserv_level);
d76ed9a9 3569 return res;
3570}
3571
3572static OPTION_FUNC(opt_epithet)
3573{
d76ed9a9 3574 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
180e0971 3575 char *epithet;
56958740 3576 struct userNode *target, *next_un;
3577
180e0971 3578 if (!override) {
886bca7c
MB
3579 if (!(noreply))
3580 reply("MSG_SETTING_PRIVILEGED", argv[0]);
180e0971 3581 return 0;
3582 }
3583
3584 epithet = unsplit_string(argv+1, argc-1, NULL);
3585
d76ed9a9 3586 if (hi->epithet)
3587 free(hi->epithet);
3588 if ((epithet[0] == '*') && !epithet[1])
3589 hi->epithet = NULL;
3590 else
3591 hi->epithet = strdup(epithet);
56958740 3592
3593 for (target = hi->users; target; target = next_un) {
3594 irc_swhois(nickserv, target, hi->epithet);
3595
3596 next_un = target->next_authed;
3597 }
d76ed9a9 3598 }
3599
886bca7c
MB
3600 if (!(noreply)) {
3601 if (hi->epithet)
3602 reply("NSMSG_SET_EPITHET", hi->epithet);
3603 else
3604 reply("NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
3605 }
d76ed9a9 3606 return 1;
3607}
3608
3609static OPTION_FUNC(opt_title)
3610{
116d100f 3611 char *title;
886bca7c 3612 const char *none = NULL;
116d100f 3613 char *sptr;
d76ed9a9 3614
d76ed9a9 3615 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
180e0971 3616 if (!override) {
886bca7c
MB
3617 if (!(noreply))
3618 reply("MSG_SETTING_PRIVILEGED", argv[0]);
180e0971 3619 return 0;
3620 }
3621
d76ed9a9 3622 title = argv[1];
574bfc14 3623 if(!strcmp(title, "*")) {
3624 free(hi->fakehost);
3625 hi->fakehost = NULL;
d76ed9a9 3626 }
574bfc14 3627 else {
3628 if (strchr(title, '.')) {
886bca7c
MB
3629 if (!(noreply))
3630 reply("NSMSG_TITLE_INVALID");
116d100f 3631 return 0;
3632 }
574bfc14 3633 /* Alphanumeric titles only. */
3634 for(sptr = title; *sptr; sptr++) {
3635 if(!isalnum(*sptr) && *sptr != '-') {
886bca7c
MB
3636 if (!(noreply))
3637 reply("NSMSG_TITLE_INVALID");
574bfc14 3638 return 0;
3639 }
3640 }
3641 if ((strlen(user->handle_info->handle) + strlen(title) +
3642 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
886bca7c
MB
3643 if (!(noreply))
3644 reply("NSMSG_TITLE_TRUNCATED");
574bfc14 3645 return 0;
3646 }
3647 free(hi->fakehost);
d76ed9a9 3648 hi->fakehost = malloc(strlen(title)+2);
3649 hi->fakehost[0] = '.';
3650 strcpy(hi->fakehost+1, title);
3651 }
3652 apply_fakehost(hi);
3653 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
3654 title = hi->fakehost + 1;
bae4525d 3655 else {
3656 /* If theres no title set then the default title will therefore
d9ffe0e7 3657 be the first part of hidden_host in x3.conf, so for
3658 consistency with opt_fakehost we will print this here.
3659 This isnt actually used in P10, its just handled to keep from crashing... */
bae4525d 3660 char *hs, *hidden_suffix, *rest;
3661
3662 hs = conf_get_data("server/hidden_host", RECDB_QSTRING);
3663 hidden_suffix = strdup(hs);
3664
3665 /* Yes we do this twice */
d9ffe0e7 3666 if((rest = strchr(hidden_suffix, '.')))
3667 {
3668 *rest = '\0';
3669 title = hidden_suffix;
3670 }
3671 else
3672 {
3673 /* A lame default if someone configured hidden_host to something lame */
3674 title = strdup("users");
3675 free(hidden_suffix);
3676 }
bae4525d 3677
bae4525d 3678 }
3679
d76ed9a9 3680 if (!title)
116d100f 3681 none = user_find_message(user, "MSG_NONE");
886bca7c
MB
3682 if (!(noreply))
3683 send_message(user, nickserv, "NSMSG_SET_TITLE", title ? title : none);
d76ed9a9 3684 return 1;
3685}
3686
7637f48f 3687int
c092fcad 3688check_vhost(char *vhost, struct userNode *user, struct svccmd *cmd)
7637f48f 3689{
116d100f 3690 unsigned int y;
7637f48f 3691
3692 // check for a dot in the vhost
3693 if(strchr(vhost, '.') == NULL) {
c092fcad 3694 reply("NSMSG_NOT_VALID_FAKEHOST_DOT", vhost);
7637f48f 3695 return 0;
3696 }
3697
3698 // check for a @ in the vhost
3699 if(strchr(vhost, '@') != NULL) {
c092fcad 3700 reply("NSMSG_NOT_VALID_FAKEHOST_AT", vhost);
7637f48f 3701 return 0;
3702 }
3703
3704 // check for denied words, inspired by monk at paki.sex
3705 for(y = 0; y < nickserv_conf.denied_fakehost_words->used; y++) {
3706 if(strstr(vhost, nickserv_conf.denied_fakehost_words->list[y]) != NULL) {
c092fcad 3707 reply("NSMSG_DENIED_FAKEHOST_WORD", vhost, nickserv_conf.denied_fakehost_words->list[y]);
7637f48f 3708 return 0;
3709 }
3710 }
3711
3712 // check for ircu's HOSTLEN length.
3713 if(strlen(vhost) >= HOSTLEN) {
c092fcad 3714 reply("NSMSG_NOT_VALID_FAKEHOST_LEN", vhost);
7637f48f 3715 return 0;
3716 }
3717
bf93ca8d 3718 /* This can be handled by the regex now if desired.
7637f48f 3719 if (vhost[strspn(vhost, "0123456789.")]) {
3720 hostname = vhost + strlen(vhost);
3721 for (depth = 1; depth && (hostname > vhost); depth--) {
3722 hostname--;
3723 while ((hostname > vhost) && (*hostname != '.')) hostname--;
3724 }
3725
bf93ca8d 3726 if (*hostname == '.') hostname++; * advance past last dot we saw *
7637f48f 3727 if(strlen(hostname) > 4) {
c092fcad 3728 reply("NSMSG_NOT_VALID_FAKEHOST_TLD_LEN", vhost);
7637f48f 3729 return 0;
3730 }
3731 }
bf93ca8d 3732 */
3733 /* test either regex or as valid handle */
3734 if (nickserv_conf.valid_fakehost_regex_set) {
3735 int err = regexec(&nickserv_conf.valid_fakehost_regex, vhost, 0, 0, 0);
3736 if (err) {
3737 char buff[256];
3738 buff[regerror(err, &nickserv_conf.valid_fakehost_regex, buff, sizeof(buff))] = 0;
3739 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
3740 }
3741 if(err == REG_NOMATCH) {
3742 reply("NSMSG_NOT_VALID_FAKEHOST_REGEX", vhost);
3743 return 0;
3744 }
3745 }
3746
7637f48f 3747
3748 return 1;
3749}
3750
d76ed9a9 3751static OPTION_FUNC(opt_fakehost)
3752{
3753 const char *fake;
3754
d76ed9a9 3755 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
180e0971 3756 if (!override) {
886bca7c
MB
3757 if (!(noreply))
3758 reply("MSG_SETTING_PRIVILEGED", argv[0]);
180e0971 3759 return 0;
3760 }
3761
d76ed9a9 3762 fake = argv[1];
3763 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
886bca7c
MB
3764 if (!(noreply))
3765 reply("NSMSG_FAKEHOST_INVALID", HOSTLEN);
d76ed9a9 3766 return 0;
3767 }
7637f48f 3768 if (!strcmp(fake, "*")) {
bf93ca8d 3769 if(hi->fakehost) {
3770 free(hi->fakehost);
3771 hi->fakehost = NULL;
3772 }
3773 }
3774 else if (!check_vhost(argv[1], user, cmd)) {
3775 /* check_vhost takes care of error reply */
3776 return 0;
3777 }
3778 else {
3779 if(hi->fakehost)
3780 free(hi->fakehost);
d76ed9a9 3781 hi->fakehost = strdup(fake);
7637f48f 3782 }
d76ed9a9 3783 apply_fakehost(hi);
bf93ca8d 3784 fake = hi->fakehost;
88b0672a 3785 } else
3786 fake = generate_fakehost(hi);
3787
bf93ca8d 3788 /* Tell them we set the host */
d76ed9a9 3789 if (!fake)
3790 fake = user_find_message(user, "MSG_NONE");
886bca7c
MB
3791 if (!(noreply))
3792 reply("NSMSG_SET_FAKEHOST", fake);
d76ed9a9 3793 return 1;
3794}
3795
0f6fe38c 3796static OPTION_FUNC(opt_note)
3797{
3798 if (!override) {
886bca7c
MB
3799 if (!(noreply))
3800 reply("MSG_SETTING_PRIVILEGED", argv[0]);
0f6fe38c 3801 return 0;
3802 }
3803
3804 if (argc > 1) {
3805 char *text = unsplit_string(argv + 1, argc - 1, NULL);
3806
3807 if (hi->note)
3808 free(hi->note);
3809
3810 if ((text[0] == '*') && !text[1])
3811 hi->note = NULL;
3812 else {
3813 if (!(hi->note = nickserv_add_note(user->handle_info->handle, now, text)))
3814 hi->note = NULL;
3815 }
3816 }
3817
886bca7c
MB
3818 if (!(noreply))
3819 reply("NSMSG_SET_NOTE", hi->note ? hi->note->note : user_find_message(user, "MSG_NONE"));
0f6fe38c 3820 return 1;
3821}
3822
d76ed9a9 3823static NICKSERV_FUNC(cmd_reclaim)
3824{
3825 struct handle_info *hi;
3826 struct nick_info *ni;
3827 struct userNode *victim;
3828
3829 NICKSERV_MIN_PARMS(2);
3830 hi = user->handle_info;
3831 ni = dict_find(nickserv_nick_dict, argv[1], 0);
3832 if (!ni) {
3833 reply("NSMSG_UNKNOWN_NICK", argv[1]);
3834 return 0;
3835 }
3836 if (ni->owner != user->handle_info) {
3837 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
3838 return 0;
3839 }
3840 victim = GetUserH(ni->nick);
3841 if (!victim) {
3842 reply("MSG_NICK_UNKNOWN", ni->nick);
3843 return 0;
3844 }
3845 if (victim == user) {
3846 reply("NSMSG_NICK_USER_YOU");
3847 return 0;
3848 }
3849 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
3850 switch (nickserv_conf.reclaim_action) {
3851 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
3852 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
3853 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
3854 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
3855 }
3856 return 1;
3857}
3858
3859static NICKSERV_FUNC(cmd_unregnick)
3860{
3861 const char *nick;
3862 struct handle_info *hi;
3863 struct nick_info *ni;
3864
3865 hi = user->handle_info;
3866 nick = (argc < 2) ? user->nick : (const char*)argv[1];
3867 ni = dict_find(nickserv_nick_dict, nick, NULL);
3868 if (!ni) {
3869 reply("NSMSG_UNKNOWN_NICK", nick);
3870 return 0;
3871 }
3872 if (hi != ni->owner) {
3873 reply("NSMSG_NOT_YOUR_NICK", nick);
3874 return 0;
3875 }
3876 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3877 delete_nick(ni);
3878 return 1;
3879}
3880
3881static NICKSERV_FUNC(cmd_ounregnick)
3882{
3883 struct nick_info *ni;
3884
3885 NICKSERV_MIN_PARMS(2);
3886 if (!(ni = get_nick_info(argv[1]))) {
3887 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
3888 return 0;
3889 }
1136f709 3890 if (!oper_outranks(user, ni->owner))
3891 return 0;
d76ed9a9 3892 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3893 delete_nick(ni);
3894 return 1;
3895}
3896
3897static NICKSERV_FUNC(cmd_unregister)
3898{
3899 struct handle_info *hi;
3900 char *passwd;
3901
3902 NICKSERV_MIN_PARMS(2);
3903 hi = user->handle_info;
3904 passwd = argv[1];
3905 argv[1] = "****";
3906 if (checkpass(passwd, hi->passwd)) {
73d4cc91 3907 if(nickserv_unregister_handle(hi, user, cmd->parent->bot))
3908 return 1;
3909 else
3910 return 0;
d76ed9a9 3911 } else {
3912 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3913 reply("NSMSG_PASSWORD_INVALID");
3914 return 0;
3915 }
3916}
3917
3918static NICKSERV_FUNC(cmd_ounregister)
3919{
3920 struct handle_info *hi;
1136f709 3921 char reason[MAXLEN];
3922 int force;
d76ed9a9 3923
3924 NICKSERV_MIN_PARMS(2);
1136f709 3925 if (!(hi = get_victim_oper(user, argv[1])))
d76ed9a9 3926 return 0;
1136f709 3927
3928 if (HANDLE_FLAGGED(hi, NODELETE)) {
3929 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
3930 return 0;
3931 }
3932
3933 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
3934 if (!force &&
3935 ((hi->flags & nickserv_conf.ounregister_flags)
3936 || hi->users
3937 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
3938 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
3939 return 0;
3940 }
3941 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
3942 global_message(MESSAGE_RECIPIENT_STAFF, reason);
73d4cc91 3943 if(nickserv_unregister_handle(hi, user, cmd->parent->bot))
3944 return 1;
3945 else
3946 return 0;
d76ed9a9 3947}
3948
3949static NICKSERV_FUNC(cmd_status)
3950{
3951 if (nickserv_conf.disable_nicks) {
3952 reply("NSMSG_GLOBAL_STATS_NONICK",
3953 dict_size(nickserv_handle_dict));
3954 } else {
3955 if (user->handle_info) {
3956 int cnt=0;
3957 struct nick_info *ni;
3958 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
3959 reply("NSMSG_HANDLE_STATS", cnt);
3960 } else {
3961 reply("NSMSG_HANDLE_NONE");
3962 }
3963 reply("NSMSG_GLOBAL_STATS",
3964 dict_size(nickserv_handle_dict),
3965 dict_size(nickserv_nick_dict));
3966 }
3967 return 1;
3968}
3969
3970static NICKSERV_FUNC(cmd_ghost)
3971{
3972 struct userNode *target;
3973 char reason[MAXLEN];
3974
3975 NICKSERV_MIN_PARMS(2);
3976 if (!(target = GetUserH(argv[1]))) {
3977 reply("MSG_NICK_UNKNOWN", argv[1]);
3978 return 0;
3979 }
3980 if (target == user) {
3981 reply("NSMSG_CANNOT_GHOST_SELF");
3982 return 0;
3983 }
3984 if (!target->handle_info || (target->handle_info != user->handle_info)) {
3985 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
3986 return 0;
3987 }
3988 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
3989 DelUser(target, nickserv, 1, reason);
3990 reply("NSMSG_GHOST_KILLED", argv[1]);
3991 return 1;
3992}
3993
3994static NICKSERV_FUNC(cmd_vacation)
3995{
3996 HANDLE_SET_FLAG(user->handle_info, FROZEN);
3997 reply("NSMSG_ON_VACATION");
3998 return 1;
3999}
4000
4001static int
4002nickserv_saxdb_write(struct saxdb_context *ctx) {
4003 dict_iterator_t it;
4004 struct handle_info *hi;
4005 char flags[33];
4006
4007 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
4008 hi = iter_data(it);
d76ed9a9 4009 saxdb_start_record(ctx, iter_key(it), 0);
0f6fe38c 4010 if (hi->announcements != '?') {
4011 flags[0] = hi->announcements;
4012 flags[1] = 0;
4013 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
4014 }
d76ed9a9 4015 if (hi->cookie) {
4016 struct handle_cookie *cookie = hi->cookie;
4017 char *type;
4018
4019 switch (cookie->type) {
4020 case ACTIVATION: type = KEY_ACTIVATION; break;
4021 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
4022 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
4023 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
4024 default: type = NULL; break;
4025 }
4026 if (type) {
4027 saxdb_start_record(ctx, KEY_COOKIE, 0);
4028 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
4029 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
4030 if (cookie->data)
4031 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
4032 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
4033 saxdb_end_record(ctx);
4034 }
4035 }
4036 if (hi->email_addr)
4037 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
4038 if (hi->epithet)
4039 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
0f6fe38c 4040 if (hi->note) {
4041 saxdb_start_record(ctx, KEY_NOTE_NOTE, 0);
4042 saxdb_write_string(ctx, KEY_NOTE_SETTER, hi->note->setter);
4043 saxdb_write_int(ctx, KEY_NOTE_DATE, hi->note->date);
4044 saxdb_write_string(ctx, KEY_NOTE_NOTE, hi->note->note);
4045 saxdb_end_record(ctx);
4046 }
2362161a 4047
d76ed9a9 4048 if (hi->fakehost)
4049 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
4050 if (hi->flags) {
4051 int ii, flen;
4052
4053 for (ii=flen=0; handle_flags[ii]; ++ii)
4054 if (hi->flags & (1 << ii))
4055 flags[flen++] = handle_flags[ii];
4056 flags[flen] = 0;
4057 saxdb_write_string(ctx, KEY_FLAGS, flags);
4058 }
d76ed9a9 4059 if (hi->infoline)
4060 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
4061 if (hi->last_quit_host[0])
4062 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
4063 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
1136f709 4064 if (hi->karma != 0)
4065 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
d76ed9a9 4066 if (hi->masks->used)
4067 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2fa83595 4068 if (hi->sslfps->used)
4069 saxdb_write_string_list(ctx, KEY_SSLFPS, hi->sslfps);
5177fd21 4070 if (hi->ignores->used)
4071 saxdb_write_string_list(ctx, KEY_IGNORES, hi->ignores);
d76ed9a9 4072 if (hi->maxlogins)
4073 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
4074 if (hi->nicks) {
4075 struct string_list *slist;
4076 struct nick_info *ni;
4077
4078 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
4079 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
4080 saxdb_write_string_list(ctx, KEY_NICKS, slist);
4081 free(slist->list);
4082 free(slist);
ae275267
MB
4083
4084 saxdb_start_record(ctx, KEY_NICKS_EX, 0);
4085 for (ni = hi->nicks; ni; ni = ni->next) {
4086 saxdb_start_record(ctx, ni->nick, 0);
4087 saxdb_write_int(ctx, KEY_REGISTER_ON, ni->registered);
4088 saxdb_write_int(ctx, KEY_LAST_SEEN, ni->lastseen);
4089 saxdb_end_record(ctx);
4090 }
4091 saxdb_end_record(ctx);
d76ed9a9 4092 }
4093 if (hi->opserv_level)
4094 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
4095 if (hi->language != lang_C)
4096 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
4097 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
4098 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
4099 if (hi->screen_width)
4100 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
4101 if (hi->table_width)
4102 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
4103 flags[0] = hi->userlist_style;
4104 flags[1] = 0;
4105 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
4106 saxdb_end_record(ctx);
4107 }
5177fd21 4108
d76ed9a9 4109 return 0;
4110}
4111
4112static handle_merge_func_t *handle_merge_func_list;
50dafce8 4113static void **handle_merge_func_list_extra;
d76ed9a9 4114static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
4115
4116void
50dafce8 4117reg_handle_merge_func(handle_merge_func_t func, void *extra)
d76ed9a9 4118{
4119 if (handle_merge_func_used == handle_merge_func_size) {
4120 if (handle_merge_func_size) {
4121 handle_merge_func_size <<= 1;
4122 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
50dafce8 4123 handle_merge_func_list_extra = realloc(handle_merge_func_list_extra, handle_merge_func_size*sizeof(void*));
d76ed9a9 4124 } else {
4125 handle_merge_func_size = 8;
4126 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
50dafce8 4127 handle_merge_func_list_extra = malloc(handle_merge_func_size*sizeof(void*));
d76ed9a9 4128 }
4129 }
50dafce8 4130 handle_merge_func_list[handle_merge_func_used] = func;
4131 handle_merge_func_list_extra[handle_merge_func_used++] = extra;
d76ed9a9 4132}
4133
4134static NICKSERV_FUNC(cmd_merge)
4135{
4136 struct handle_info *hi_from, *hi_to;
4137 struct userNode *last_user;
4138 struct userData *cList, *cListNext;
4139 unsigned int ii, jj, n;
d76ed9a9 4140
4141 NICKSERV_MIN_PARMS(3);
4142
1136f709 4143 if (!(hi_from = get_victim_oper(user, argv[1])))
d76ed9a9 4144 return 0;
1136f709 4145 if (!(hi_to = get_victim_oper(user, argv[2])))
d76ed9a9 4146 return 0;
4147 if (hi_to == hi_from) {
4148 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
4149 return 0;
4150 }
4151
4152 for (n=0; n<handle_merge_func_used; n++)
50dafce8 4153 handle_merge_func_list[n](user, hi_to, hi_from, handle_merge_func_list_extra[n]);
d76ed9a9 4154
4155 /* Append "from" handle's nicks to "to" handle's nick list. */
4156 if (hi_to->nicks) {
4157 struct nick_info *last_ni;
4158 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
4159 last_ni->next = hi_from->nicks;
4160 }
4161 while (hi_from->nicks) {
4162 hi_from->nicks->owner = hi_to;
4163 hi_from->nicks = hi_from->nicks->next;
4164 }
4165
4166 /* Merge the hostmasks. */
4167 for (ii=0; ii<hi_from->masks->used; ii++) {
4168 char *mask = hi_from->masks->list[ii];
4169 for (jj=0; jj<hi_to->masks->used; jj++)
4170 if (match_ircglobs(hi_to->masks->list[jj], mask))
4171 break;
4172 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
4173 string_list_append(hi_to->masks, strdup(mask));
4174 }
4175
c73514ea 4176 /* Merge the SSL fingerprints. */
4177 for (ii=0; ii<hi_from->sslfps->used; ii++) {
4178 char *sslfp = hi_from->sslfps->list[ii];
4179 for (jj=0; jj<hi_to->sslfps->used; jj++)
4180 if (!irccasecmp(hi_to->sslfps->list[jj], sslfp))
4181 break;
4182 if (jj==hi_to->sslfps->used) /* Nothing from the "to" handle covered this sslfp, so add it. */
4183 string_list_append(hi_to->sslfps, strdup(sslfp));
4184 }
4185
5177fd21 4186 /* Merge the ignores. */
4187 for (ii=0; ii<hi_from->ignores->used; ii++) {
4188 char *ignore = hi_from->ignores->list[ii];
4189 for (jj=0; jj<hi_to->ignores->used; jj++)
4190 if (match_ircglobs(hi_to->ignores->list[jj], ignore))
4191 break;
4192 if (jj==hi_to->ignores->used) /* Nothing from the "to" handle covered this mask, so add it. */
4193 string_list_append(hi_to->ignores, strdup(ignore));
4194 }
4195
d76ed9a9 4196 /* Merge the lists of authed users. */
4197 if (hi_to->users) {
4198 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
4199 last_user->next_authed = hi_from->users;
4200 } else {
4201 hi_to->users = hi_from->users;
4202 }
4203 /* Repoint the old "from" handle's users. */
4204 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
4205 last_user->handle_info = hi_to;
4206 }
4207 hi_from->users = NULL;
4208
4209 /* Merge channel userlists. */
4210 for (cList=hi_from->channels; cList; cList=cListNext) {
4211 struct userData *cList2;
4212 cListNext = cList->u_next;
4213 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
4214 if (cList->channel == cList2->channel)
4215 break;
4216 if (cList2 && (cList2->access >= cList->access)) {
4217 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);
4218 /* keep cList2 in hi_to; remove cList from hi_from */
4219 del_channel_user(cList, 1);
4220 } else {
4221 if (cList2) {
4222 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);
4223 /* remove the lower-ranking cList2 from hi_to */
4224 del_channel_user(cList2, 1);
4225 } else {
4226 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
4227 }
4228 /* cList needs to be moved from hi_from to hi_to */
4229 cList->handle = hi_to;
4230 /* Remove from linked list for hi_from */
4231 assert(!cList->u_prev);
4232 hi_from->channels = cList->u_next;
4233 if (cList->u_next)
4234 cList->u_next->u_prev = cList->u_prev;
4235 /* Add to linked list for hi_to */
4236 cList->u_prev = NULL;
4237 cList->u_next = hi_to->channels;
4238 if (hi_to->channels)
4239 hi_to->channels->u_prev = cList;
4240 hi_to->channels = cList;
4241 }
4242 }
4243
4244 /* Do they get an OpServ level promotion? */
4245 if (hi_from->opserv_level > hi_to->opserv_level)
4246 hi_to->opserv_level = hi_from->opserv_level;
4247
4248 /* What about last seen time? */
4249 if (hi_from->lastseen > hi_to->lastseen)
4250 hi_to->lastseen = hi_from->lastseen;
4251
1136f709 4252 /* New karma is the sum of the two original karmas. */
4253 hi_to->karma += hi_from->karma;
4254
0d16e639 4255 /* Does a fakehost carry over? (This intentionally doesn't set it
4256 * for users previously attached to hi_to. They'll just have to
4257 * reconnect.)
4258 */
4259 if (hi_from->fakehost && !hi_to->fakehost)
4260 hi_to->fakehost = strdup(hi_from->fakehost);
4261
d76ed9a9 4262 /* Notify of success. */
d76ed9a9 4263 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
57692f5e 4264 global_message_args(MESSAGE_RECIPIENT_OPERS, "NSMSG_ACCOUNT_MERGED", user->nick,
4265 user->handle_info->handle, hi_from->handle, hi_to->handle);
d76ed9a9 4266
4267 /* Unregister the "from" handle. */
258d1427 4268 nickserv_unregister_handle(hi_from, NULL, cmd->parent->bot);
73d4cc91 4269 /* TODO: fix it so that if the ldap delete in nickserv_unregister_handle fails,
4270 * the process isn't completed.
4271 */
d76ed9a9 4272
4273 return 1;
4274}
4275
4276struct nickserv_discrim {
d76ed9a9 4277 unsigned long flags_on, flags_off;
4278 time_t min_registered, max_registered;
4279 time_t lastseen;
1136f709 4280 unsigned int limit;
4281 int min_level, max_level;
4282 int min_karma, max_karma;
d76ed9a9 4283 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
4284 const char *nickmask;
4285 const char *hostmask;
4286 const char *handlemask;
4287 const char *emailmask;
73ac2ca6 4288 const char *titlemask;
886bca7c
MB
4289 const char *setwhat;
4290 const char *setval;
4291 struct svccmd *cmd;
b96027ad 4292#ifdef WITH_LDAP
4293 unsigned int inldap;
4294#endif
d76ed9a9 4295};
4296
886bca7c 4297typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi, struct nickserv_discrim *discrim);
d76ed9a9 4298
4299struct discrim_apply_info {
4300 struct nickserv_discrim *discrim;
4301 discrim_search_func func;
4302 struct userNode *source;
4303 unsigned int matched;
4304};
4305
4306static struct nickserv_discrim *
c092fcad 4307nickserv_discrim_create(struct svccmd *cmd, struct userNode *user, unsigned int argc, char *argv[])
d76ed9a9 4308{
4309 unsigned int i;
4310 struct nickserv_discrim *discrim;
4311
4312 discrim = malloc(sizeof(*discrim));
4313 memset(discrim, 0, sizeof(*discrim));
4314 discrim->min_level = 0;
1136f709 4315 discrim->max_level = INT_MAX;
d76ed9a9 4316 discrim->limit = 50;
4317 discrim->min_registered = 0;
4318 discrim->max_registered = INT_MAX;
1136f709 4319 discrim->lastseen = LONG_MAX;
4320 discrim->min_karma = INT_MIN;
4321 discrim->max_karma = INT_MAX;
886bca7c 4322 discrim->cmd = cmd;
b96027ad 4323#ifdef WITH_LDAP
4324 discrim->inldap = 2;
4325#endif
d76ed9a9 4326
4327 for (i=0; i<argc; i++) {
4328 if (i == argc - 1) {
c092fcad 4329 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9 4330 goto fail;
4331 }
4332 if (!irccasecmp(argv[i], "limit")) {
4333 discrim->limit = strtoul(argv[++i], NULL, 0);
4334 } else if (!irccasecmp(argv[i], "flags")) {
4335 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
4336 } else if (!irccasecmp(argv[i], "registered")) {
4337 const char *cmp = argv[++i];
4338 if (cmp[0] == '<') {
4339 if (cmp[1] == '=') {
4340 discrim->min_registered = now - ParseInterval(cmp+2);
4341 } else {
4342 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
4343 }
4344 } else if (cmp[0] == '=') {
4345 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
4346 } else if (cmp[0] == '>') {
4347 if (cmp[1] == '=') {
4348 discrim->max_registered = now - ParseInterval(cmp+2);
4349 } else {
4350 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
4351 }
4352 } else {
c092fcad 4353 reply("MSG_INVALID_CRITERIA", cmp);
d76ed9a9 4354 }
4355 } else if (!irccasecmp(argv[i], "seen")) {
4356 discrim->lastseen = now - ParseInterval(argv[++i]);
4357 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
4358 discrim->nickmask = argv[++i];
886bca7c
MB
4359 } else if (!irccasecmp(argv[i], "setwhat")) {
4360 discrim->setwhat = argv[++i];
4361 if (!(dict_find(nickserv_opt_dict, discrim->setwhat, NULL))) {
4362 reply("NSMSG_INVALID_OPTION", discrim->setwhat);
4363 goto fail;
4364 }
4365 } else if (!irccasecmp(argv[i], "setvalue")) {
4366 discrim->setval = argv[++i];
d76ed9a9 4367 } else if (!irccasecmp(argv[i], "hostmask")) {
4368 i++;
4369 if (!irccasecmp(argv[i], "exact")) {
4370 if (i == argc - 1) {
c092fcad 4371 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9 4372 goto fail;
4373 }
4374 discrim->hostmask_type = EXACT;
4375 } else if (!irccasecmp(argv[i], "subset")) {
4376 if (i == argc - 1) {
c092fcad 4377 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9 4378 goto fail;
4379 }
4380 discrim->hostmask_type = SUBSET;
4381 } else if (!irccasecmp(argv[i], "superset")) {
4382 if (i == argc - 1) {
c092fcad 4383 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9 4384 goto fail;
4385 }
4386 discrim->hostmask_type = SUPERSET;
4387 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
4388 if (i == argc - 1) {
c092fcad 4389 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9 4390 goto fail;
4391 }
4392 discrim->hostmask_type = LASTQUIT;
4393 } else {
4394 i--;
4395 discrim->hostmask_type = SUPERSET;
4396 }
4397 discrim->hostmask = argv[++i];
b96027ad 4398 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask") || !irccasecmp(argv[i], "account")) {
d76ed9a9 4399 if (!irccasecmp(argv[++i], "*")) {
4400 discrim->handlemask = 0;
4401 } else {
4402 discrim->handlemask = argv[i];
4403 }
4404 } else if (!irccasecmp(argv[i], "email")) {
4405 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
c092fcad 4406 reply("MSG_NO_SEARCH_ACCESS", "email");
d76ed9a9 4407 goto fail;
4408 } else if (!irccasecmp(argv[++i], "*")) {
4409 discrim->emailmask = 0;
4410 } else {
4411 discrim->emailmask = argv[i];
4412 }
73ac2ca6
MB
4413 } else if (!irccasecmp(argv[i], "title")) {
4414 if (!irccasecmp(argv[++i], "*")) {
4415 discrim->titlemask = 0;
4416 } else {
4417 discrim->titlemask = argv[i];
4418 }
d76ed9a9 4419 } else if (!irccasecmp(argv[i], "access")) {
4420 const char *cmp = argv[++i];
4421 if (cmp[0] == '<') {
4422 if (discrim->min_level == 0) discrim->min_level = 1;
4423 if (cmp[1] == '=') {
4424 discrim->max_level = strtoul(cmp+2, NULL, 0);
4425 } else {
4426 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
4427 }
4428 } else if (cmp[0] == '=') {
4429 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
4430 } else if (cmp[0] == '>') {
4431 if (cmp[1] == '=') {
4432 discrim->min_level = strtoul(cmp+2, NULL, 0);
4433 } else {
4434 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
4435 }
4436 } else {
c092fcad 4437 reply("MSG_INVALID_CRITERIA", cmp);
d76ed9a9 4438 }
1136f709 4439 } else if (!irccasecmp(argv[i], "karma")) {
4440 const char *cmp = argv[++i];
4441 if (cmp[0] == '<') {
4442 if (cmp[1] == '=') {
4443 discrim->max_karma = strtoul(cmp+2, NULL, 0);
4444 } else {
4445 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
4446 }
4447 } else if (cmp[0] == '=') {
4448 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
4449 } else if (cmp[0] == '>') {
4450 if (cmp[1] == '=') {
4451 discrim->min_karma = strtoul(cmp+2, NULL, 0);
4452 } else {
4453 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
4454 }
4455 } else {
4456 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
4457 }
d5faccba 4458#ifdef WITH_LDAP
b96027ad 4459 } else if (nickserv_conf.ldap_enable && !irccasecmp(argv[i], "inldap")) {
4460 i++;
4461 if(true_string(argv[i])) {
4462 discrim->inldap = 1;
4463 }
4464 else if (false_string(argv[i])) {
4465 discrim->inldap = 0;
4466 }
4467 else {
4468 reply("MSG_INVALID_BINARY", argv[i]);
4469 }
d5faccba 4470#endif
b96027ad 4471 } else {
c092fcad 4472 reply("MSG_INVALID_CRITERIA", argv[i]);
d76ed9a9 4473 goto fail;
4474 }
4475 }
4476 return discrim;
4477 fail:
4478 free(discrim);
4479 return NULL;
4480}
4481
4482static int
4483nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
4484{
73ac2ca6
MB
4485 char *title = NULL;
4486
4487 if (hi->fakehost && (hi->fakehost[0] == '.'))
4488 title = hi->fakehost + 1;
4489
d76ed9a9 4490 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
4491 || (discrim->flags_off & hi->flags)
4492 || (discrim->min_registered > hi->registered)
4493 || (discrim->max_registered < hi->registered)
4494 || (discrim->lastseen < (hi->users?now:hi->lastseen))
4495 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
4496 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
73ac2ca6 4497 || (discrim->titlemask && (!title || !match_ircglob(title, discrim->titlemask)))
d76ed9a9 4498 || (discrim->min_level > hi->opserv_level)
1136f709 4499 || (discrim->max_level < hi->opserv_level)
4500 || (discrim->min_karma > hi->karma)
4501 || (discrim->max_karma < hi->karma)
4502 ) {
d76ed9a9 4503 return 0;
4504 }
4505 if (discrim->hostmask) {
4506 unsigned int i;
4507 for (i=0; i<hi->masks->used; i++) {
4508 const char *mask = hi->masks->list[i];
4509 if ((discrim->hostmask_type == SUBSET)
4510 && (match_ircglobs(discrim->hostmask, mask))) break;
4511 else if ((discrim->hostmask_type == EXACT)
4512 && !irccasecmp(discrim->hostmask, mask)) break;
4513 else if ((discrim->hostmask_type == SUPERSET)
4514 && (match_ircglobs(mask, discrim->hostmask))) break;
4515 else if ((discrim->hostmask_type == LASTQUIT)
4516 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
4517 }
4518 if (i==hi->masks->used) return 0;
4519 }
4520 if (discrim->nickmask) {
4521 struct nick_info *nick = hi->nicks;
4522 while (nick) {
4523 if (match_ircglob(nick->nick, discrim->nickmask)) break;
4524 nick = nick->next;
4525 }
4526 if (!nick) return 0;
4527 }
b96027ad 4528#ifdef WITH_LDAP
4529 if(nickserv_conf.ldap_enable && discrim->inldap != 2) {
4530 int rc;
4531 rc = ldap_get_user_info(hi->handle, NULL);
4532 if(discrim->inldap == 1 && rc != LDAP_SUCCESS)
4533 return 0;
4534 if(discrim->inldap == 0 && rc == LDAP_SUCCESS)
4535 return 0;
4536 }
4537
4538#endif
d76ed9a9 4539 return 1;
4540}
4541
4542static unsigned int
4543nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
4544{
4545 dict_iterator_t it, next;
4546 unsigned int matched;
4547
4548 for (it = dict_first(nickserv_handle_dict), matched = 0;
4549 it && (matched < discrim->limit);
4550 it = next) {
4551 next = iter_next(it);
4552 if (nickserv_discrim_match(discrim, iter_data(it))) {
886bca7c 4553 dsf(source, iter_data(it), discrim);
d76ed9a9 4554 matched++;
4555 }
4556 }
4557 return matched;
4558}
4559
4560static void
886bca7c 4561search_print_func(struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
d76ed9a9 4562{
4563 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
4564}
4565
4566static void
886bca7c 4567search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match), UNUSED_ARG(struct nickserv_discrim *discrim))
d76ed9a9 4568{
4569}
4570
4571static void
886bca7c 4572search_unregister_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
d76ed9a9 4573{
4574 if (oper_has_access(source, nickserv, match->opserv_level, 0))
258d1427 4575 nickserv_unregister_handle(match, source, nickserv); // XXX nickserv hard coded
d76ed9a9 4576}
4577
4cb36ef0 4578#ifdef WITH_LDAP
b96027ad 4579static void
886bca7c 4580search_add2ldap_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
b96027ad 4581{
87677bd8 4582 int rc;
4583 if(match->email_addr && match->passwd && match->handle) {
4584 rc = ldap_do_add(match->handle, match->passwd, match->email_addr);
4585 if(rc != LDAP_SUCCESS) {
4586 send_message(source, nickserv, "NSMSG_LDAP_FAIL_ADD", match->handle, ldap_err2string(rc));
4587 }
b96027ad 4588 }
b96027ad 4589}
4cb36ef0 4590#endif
b96027ad 4591
886bca7c
MB
4592static void
4593search_set_func (struct userNode *source, struct handle_info *match, struct nickserv_discrim *discrim)
4594{
4595 option_func_t *opt;
4596 char *oargv[2];
4597
4598 if (!(opt = dict_find(nickserv_opt_dict, discrim->setwhat, NULL))) {
4599 return;
4600 }
4601
4602 oargv[0] = (char *)discrim->setwhat;
4603 oargv[1] = (char *)discrim->setval;
4604
4605 opt(discrim->cmd, source, match, 1, 1, 2, oargv);
4606}
4607
d76ed9a9 4608static int
4609nickserv_sort_accounts_by_access(const void *a, const void *b)
4610{
4611 const struct handle_info *hi_a = *(const struct handle_info**)a;
4612 const struct handle_info *hi_b = *(const struct handle_info**)b;
4613 if (hi_a->opserv_level != hi_b->opserv_level)
4614 return hi_b->opserv_level - hi_a->opserv_level;
4615 return irccasecmp(hi_a->handle, hi_b->handle);
4616}
4617
4618void
4619nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
4620{
4621 struct handle_info_list hil;
4622 struct helpfile_table tbl;
4623 unsigned int ii;
4624 dict_iterator_t it;
4625 const char **ary;
4626
4627 memset(&hil, 0, sizeof(hil));
4628 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
4629 struct handle_info *hi = iter_data(it);
4630 if (hi->opserv_level)
4631 handle_info_list_append(&hil, hi);
4632 }
4633 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
4634 tbl.length = hil.used + 1;
4635 tbl.width = 2;
a8370a20 4636 tbl.flags = TABLE_NO_FREE | TABLE_REPEAT_ROWS | TABLE_REPEAT_HEADERS;
d76ed9a9 4637 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4638 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
4639 ary[0] = "Account";
4640 ary[1] = "Level";
4641 for (ii = 0; ii < hil.used; ) {
4642 ary = malloc(tbl.width * sizeof(ary[0]));
4643 ary[0] = hil.list[ii]->handle;
4644 ary[1] = strtab(hil.list[ii]->opserv_level);
4645 tbl.contents[++ii] = ary;
4646 }
4647 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
a8370a20 4648 /*reply("MSG_MATCH_COUNT", hil.used); */
d76ed9a9 4649 for (ii = 0; ii < hil.used; ii++)
4650 free(tbl.contents[ii]);
4651 free(tbl.contents);
4652 free(hil.list);
4653}
4654
4655static NICKSERV_FUNC(cmd_search)
4656{
4657 struct nickserv_discrim *discrim;
4658 discrim_search_func action;
4659 struct svccmd *subcmd;
4660 unsigned int matches;
4661 char buf[MAXLEN];
4662
4663 NICKSERV_MIN_PARMS(3);
4664 sprintf(buf, "search %s", argv[1]);
4665 subcmd = dict_find(nickserv_service->commands, buf, NULL);
4666 if (!irccasecmp(argv[1], "print"))
4667 action = search_print_func;
4668 else if (!irccasecmp(argv[1], "count"))
4669 action = search_count_func;
4670 else if (!irccasecmp(argv[1], "unregister"))
4671 action = search_unregister_func;
886bca7c
MB
4672 else if (!irccasecmp(argv[1], "set"))
4673 action = search_set_func;
b96027ad 4674#ifdef WITH_LDAP
4675 else if (nickserv_conf.ldap_enable && !irccasecmp(argv[1], "add2ldap"))
4676 action = search_add2ldap_func;
4677#endif
d76ed9a9 4678 else {
4679 reply("NSMSG_INVALID_ACTION", argv[1]);
4680 return 0;
4681 }
4682
4683 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
4684 return 0;
4685
c092fcad 4686 discrim = nickserv_discrim_create(cmd, user, argc-2, argv+2);
d76ed9a9 4687 if (!discrim)
4688 return 0;
4689
4690 if (action == search_print_func)
4691 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
4692 else if (action == search_count_func)
4693 discrim->limit = INT_MAX;
886bca7c
MB
4694 else if ((action == search_set_func) && (!(discrim->setwhat) || !(discrim->setval)))
4695 return reply("MSG_MISSING_PARAMS", argv[1]);
d76ed9a9 4696
4697 matches = nickserv_discrim_search(discrim, action, user);
4698
4699 if (matches)
4700 reply("MSG_MATCH_COUNT", matches);
4701 else
4702 reply("MSG_NO_MATCHES");
4703
4704 free(discrim);
4705 return 0;
4706}
4707
4708static MODCMD_FUNC(cmd_checkpass)
4709{
4710 struct handle_info *hi;
4711
4712 NICKSERV_MIN_PARMS(3);
4713 if (!(hi = get_handle_info(argv[1]))) {
4714 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4715 return 0;
4716 }
4717 if (checkpass(argv[2], hi->passwd))
4718 reply("CHECKPASS_YES");
4719 else
4720 reply("CHECKPASS_NO");
4721 argv[2] = "****";
4722 return 1;
4723}
4724
1136f709 4725static MODCMD_FUNC(cmd_checkemail)
4726{
4727 struct handle_info *hi;
4728
4729 NICKSERV_MIN_PARMS(3);
4730 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
4731 return 0;
4732 }
4733 if (!hi->email_addr)
4734 reply("CHECKEMAIL_NOT_SET");
4735 else if (!irccasecmp(argv[2], hi->email_addr))
4736 reply("CHECKEMAIL_YES");
4737 else
4738 reply("CHECKEMAIL_NO");
4739 return 1;
4740}
4741
d76ed9a9 4742static void
82b7b0d8 4743nickserv_db_read_handle(char *handle, dict_t obj)
d76ed9a9 4744{
4745 const char *str;
2fa83595 4746 struct string_list *masks, *sslfps, *slist, *ignores;
d76ed9a9 4747 struct handle_info *hi;
4748 struct userNode *authed_users;
1136f709 4749 struct userData *channel_list;
ae275267
MB
4750 struct dict *obj2;
4751 dict_iterator_t it;
d76ed9a9 4752 unsigned long int id;
4753 unsigned int ii;
4754 dict_t subdb;
0f6fe38c 4755 char *setter, *note;
4756 time_t date;
d76ed9a9 4757
4758 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
4759 id = str ? strtoul(str, NULL, 0) : 0;
4760 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
4761 if (!str) {
4762 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
4763 return;
4764 }
4765 if ((hi = get_handle_info(handle))) {
4766 authed_users = hi->users;
1136f709 4767 channel_list = hi->channels;
d76ed9a9 4768 hi->users = NULL;
02c37249 4769 hi->channels = NULL;
d76ed9a9 4770 dict_remove(nickserv_handle_dict, hi->handle);
4771 } else {
4772 authed_users = NULL;
1136f709 4773 channel_list = NULL;
d76ed9a9 4774 }
acb142f0 4775 if(nickserv_conf.force_handles_lowercase)
4776 irc_strtolower(handle);
d76ed9a9 4777 hi = register_handle(handle, str, id);
4778 if (authed_users) {
4779 hi->users = authed_users;
4780 while (authed_users) {
4781 authed_users->handle_info = hi;
4782 authed_users = authed_users->next_authed;
4783 }
4784 }
1136f709 4785 hi->channels = channel_list;
d76ed9a9 4786 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
4787 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
2fa83595 4788 sslfps = database_get_data(obj, KEY_SSLFPS, RECDB_STRING_LIST);
4789 hi->sslfps = sslfps ? string_list_copy(sslfps) : alloc_string_list(1);
5177fd21 4790 ignores = database_get_data(obj, KEY_IGNORES, RECDB_STRING_LIST);
4791 hi->ignores = ignores ? string_list_copy(ignores) : alloc_string_list(1);
d76ed9a9 4792 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
4793 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
4794 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
4795 hi->language = language_find(str ? str : "C");
4796 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
4797 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
4798 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
4799 if (str)
4800 hi->infoline = strdup(str);
4801 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
4802 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
4803 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
4804 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
1136f709 4805 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
4806 hi->karma = str ? strtoul(str, NULL, 0) : 0;
d76ed9a9 4807 /* We want to read the nicks even if disable_nicks is set. This is so
4808 * that we don't lose the nick data entirely. */
ae275267
MB
4809 obj2 = database_get_data(obj, KEY_NICKS_EX, RECDB_OBJECT);
4810 for(it = dict_first(obj2); it; it = iter_next(it))
4811 {
4812 struct record_data *rd = iter_data(it);
4813 struct nick_info* ni;
4814
4815 register_nick(iter_key(it), hi);
4816 ni = get_nick_info(iter_key(it));
4817
4818 if (!(ni))
4819 continue;
4820
4821 str = database_get_data(rd->d.object, KEY_REGISTER_ON, RECDB_QSTRING);
4822 ni->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
4823 str = database_get_data(rd->d.object, KEY_LAST_SEEN, RECDB_QSTRING);
4824 ni->lastseen = str ? (time_t)strtoul(str, NULL, 0) : ni->registered;
4825 }
4826 if (!obj2) {
4827 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
4828 if (slist) {
4829 for (ii=0; ii<slist->used; ii++) {
4830 struct nick_info* ni;
4831
4832 register_nick(slist->list[ii], hi);
4833 ni = get_nick_info(slist->list[ii]);
4834
4835 if (!(ni))
4836 continue;
4837
4838 ni->registered = hi->registered;
4839 ni->lastseen = ni->registered;
4840 }
4841 }
d76ed9a9 4842 }
4843 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
4844 if (str) {
4845 for (ii=0; str[ii]; ii++)
4846 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
4847 }
4848 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
338a82b5 4849 hi->userlist_style = str ? str[0] : HI_DEFAULT_STYLE;
0f6fe38c 4850 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
4851 hi->announcements = str ? str[0] : '?';
d76ed9a9 4852 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
4853 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
4854 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
4855 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
4856 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
4857 if (!str)
4858 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
4859 if (str)
4860 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
4861 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
4862 if (str)
4863 nickserv_set_email_addr(hi, str);
4864 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
4865 if (str)
4866 hi->epithet = strdup(str);
0f6fe38c 4867 subdb = database_get_data(obj, KEY_NOTE_NOTE, RECDB_OBJECT);
4868 if (subdb) {
4869 setter = database_get_data(subdb, KEY_NOTE_SETTER, RECDB_QSTRING);
4870 str = database_get_data(subdb, KEY_NOTE_DATE, RECDB_QSTRING);
4871 date = str ? (time_t)strtoul(str, NULL, 0) : now;
4872 note = database_get_data(subdb, KEY_NOTE_NOTE, RECDB_QSTRING);
4873 if (setter && date && note)
4874 {
4875 if (!(hi->note = nickserv_add_note(setter, date, note)))
4876 hi->note = NULL;
4877 }
4878 }
2362161a 4879
d76ed9a9 4880 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
4881 if (str)
4882 hi->fakehost = strdup(str);
7637f48f 4883
d76ed9a9 4884 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
4885 if (subdb) {
4886 const char *data, *type, *expires, *cookie_str;
4887 struct handle_cookie *cookie;
4888
4889 cookie = calloc(1, sizeof(*cookie));
4890 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
4891 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
4892 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
4893 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
4894 if (!type || !expires || !cookie_str) {
4895 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
4896 goto cookie_out;
4897 }
4898 if (!irccasecmp(type, KEY_ACTIVATION))
4899 cookie->type = ACTIVATION;
4900 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
4901 cookie->type = PASSWORD_CHANGE;
4902 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
4903 cookie->type = EMAIL_CHANGE;
4904 else if (!irccasecmp(type, KEY_ALLOWAUTH))
4905 cookie->type = ALLOWAUTH;
4906 else {
4907 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
4908 goto cookie_out;
4909 }
4910 cookie->expires = strtoul(expires, NULL, 0);
4911 if (cookie->expires < now)
4912 goto cookie_out;
4913 if (data)
4914 cookie->data = strdup(data);
4915 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
4916 cookie->hi = hi;
4917 cookie_out:
4918 if (cookie->hi)
4919 nickserv_bake_cookie(cookie);
4920 else
4921 nickserv_free_cookie(cookie);
4922 }
4923}
4924
4925static int
4926nickserv_saxdb_read(dict_t db) {
4927 dict_iterator_t it;
4928 struct record_data *rd;
82b7b0d8 4929 char *handle;
d76ed9a9 4930
4931 for (it=dict_first(db); it; it=iter_next(it)) {
4932 rd = iter_data(it);
82b7b0d8 4933 handle = strdup(iter_key(it));
4934 nickserv_db_read_handle(handle, rd->d.object);
4935 free(handle);
d76ed9a9 4936 }
4937 return 0;
4938}
4939
4940static NICKSERV_FUNC(cmd_mergedb)
4941{
4942 struct timeval start, stop;
4943 dict_t db;
4944
4945 NICKSERV_MIN_PARMS(2);
4946 gettimeofday(&start, NULL);
4947 if (!(db = parse_database(argv[1]))) {
4948 reply("NSMSG_DB_UNREADABLE", argv[1]);
4949 return 0;
4950 }
4951 nickserv_saxdb_read(db);
4952 free_database(db);
4953 gettimeofday(&stop, NULL);
4954 stop.tv_sec -= start.tv_sec;
4955 stop.tv_usec -= start.tv_usec;
4956 if (stop.tv_usec < 0) {
4957 stop.tv_sec -= 1;
4958 stop.tv_usec += 1000000;
4959 }
4960 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
4961 return 1;
4962}
4963
4964static void
4965expire_handles(UNUSED_ARG(void *data))
4966{
4967 dict_iterator_t it, next;
4968 time_t expiry;
4969 struct handle_info *hi;
4970
4971 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4972 next = iter_next(it);
4973 hi = iter_data(it);
4974 if ((hi->opserv_level > 0)
4975 || hi->users
4976 || HANDLE_FLAGGED(hi, FROZEN)
4977 || HANDLE_FLAGGED(hi, NODELETE)) {
4978 continue;
4979 }
4980 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4981 if ((now - hi->lastseen) > expiry) {
4982 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
258d1427 4983 nickserv_unregister_handle(hi, NULL, NULL);
d76ed9a9 4984 }
4985 }
4986
4987 if (nickserv_conf.handle_expire_frequency)
4988 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4989}
4990
ae275267
MB
4991static void
4992expire_nicks(UNUSED_ARG(void *data))
4993{
4994 dict_iterator_t it, next;
4995 time_t expiry = nickserv_conf.nick_expire_delay;
4996 struct nick_info *ni;
4997 struct userNode *ui;
4998
4999 if (!(nickserv_conf.expire_nicks))
5000 return;
5001
5002 for (it=dict_first(nickserv_nick_dict); it; it=next) {
5003 next = iter_next(it);
5004 ni = iter_data(it);
5005 if ((ni->owner->opserv_level > 0)
5006 || ((ui = GetUserH(ni->nick)) && (ui->handle_info) && (ui->handle_info == ni->owner))
5007 || HANDLE_FLAGGED(ni->owner, FROZEN)
5008 || HANDLE_FLAGGED(ni->owner, NODELETE)) {
5009 continue;
5010 }
5011 if ((now - ni->lastseen) > expiry) {
5012 log_module(NS_LOG, LOG_INFO, "Expiring nick %s for inactivity.", ni->nick);
5013 delete_nick(ni);
5014 }
5015 }
5016
5017 if (nickserv_conf.nick_expire_frequency && nickserv_conf.expire_nicks)
5018 timeq_add(now + nickserv_conf.nick_expire_frequency, expire_nicks, NULL);
5019}
5020
d76ed9a9 5021static void
5022nickserv_load_dict(const char *fname)
5023{
5024 FILE *file;
5025 char line[128];
5026 if (!(file = fopen(fname, "r"))) {
5027 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
5028 return;
5029 }
1136f709 5030 while (fgets(line, sizeof(line), file)) {
d76ed9a9 5031 if (!line[0])
5032 continue;
5033 if (line[strlen(line)-1] == '\n')
5034 line[strlen(line)-1] = 0;
5035 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
5036 }
5037 fclose(file);
5038 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
5039}
5040
5041static enum reclaim_action
5042reclaim_action_from_string(const char *str) {
5043 if (!str)
5044 return RECLAIM_NONE;
5045 else if (!irccasecmp(str, "warn"))
5046 return RECLAIM_WARN;
5047 else if (!irccasecmp(str, "svsnick"))
5048 return RECLAIM_SVSNICK;
5049 else if (!irccasecmp(str, "kill"))
5050 return RECLAIM_KILL;
5051 else
5052 return RECLAIM_NONE;
5053}
5054
5055static void
5056nickserv_conf_read(void)
5057{
5058 dict_t conf_node, child;
5059 const char *str;
5060 dict_iterator_t it;
7637f48f 5061 struct string_list *strlist;
d76ed9a9 5062
5063 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
5064 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
5065 return;
5066 }
5067 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
5068 if (!str)
5069 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
5070 if (nickserv_conf.valid_handle_regex_set)
5071 regfree(&nickserv_conf.valid_handle_regex);
5072 if (str) {
5073 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
5074 nickserv_conf.valid_handle_regex_set = !err;
5075 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
5076 } else {
5077 nickserv_conf.valid_handle_regex_set = 0;
5078 }
5079 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
5080 if (nickserv_conf.valid_nick_regex_set)
5081 regfree(&nickserv_conf.valid_nick_regex);
5082 if (str) {
5083 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
5084 nickserv_conf.valid_nick_regex_set = !err;
5085 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
5086 } else {
5087 nickserv_conf.valid_nick_regex_set = 0;
5088 }
bf93ca8d 5089 str = database_get_data(conf_node, KEY_VALID_FAKEHOST_REGEX, RECDB_QSTRING);
5090 if (nickserv_conf.valid_fakehost_regex_set)
5091 regfree(&nickserv_conf.valid_fakehost_regex);
5092 if (str) {
5093 int err = regcomp(&nickserv_conf.valid_fakehost_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
5094 nickserv_conf.valid_fakehost_regex_set = !err;
5095 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_fakehost_regex (error %d)", err);
5096 } else {
5097 nickserv_conf.valid_fakehost_regex_set = 0;
5098 }
d76ed9a9 5099 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
5100 if (!str)
5101 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
5102 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
5103 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
5104 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
5105 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
5106 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
5107 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
5108 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
5109 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
5110 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
5111 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
5112 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
5113 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
5114 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
5115 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
5116 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
5117 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
5118 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
5119 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
5120 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
5121 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
5122 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
5123 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
5124 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
5125 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
5126 if (!str)
5127 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
5128 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
5129 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
5130 if (!str)
5131 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
5132 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
5133 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
5134 if (!str)
5135 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
5136 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
5137 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
5138 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
5139 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
5140 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
5141 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3c535a4b 5142 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
1136f709 5143 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
5144 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
5145 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
5146 if (!str)
5147 str = "ShgsfnHbu";
5148 nickserv_conf.ounregister_flags = 0;
5149 while(*str) {
5150 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
5151 str++;
5152 if(pos)
5153 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
5154 }
d76ed9a9 5155 if (!nickserv_conf.disable_nicks) {
5156 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
5157 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
5158 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
5159 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
5160 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
5161 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
5162 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
5163 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
ae275267
MB
5164 str = database_get_data(conf_node, KEY_NICK_EXPIRE_FREQ, RECDB_QSTRING);
5165 nickserv_conf.nick_expire_frequency = str ? ParseInterval(str) : 86400;
5166 str = database_get_data(conf_node, KEY_NICK_EXPIRE_DELAY, RECDB_QSTRING);
5167 nickserv_conf.nick_expire_delay = str ? ParseInterval(str) : 86400*30;
5168 str = database_get_data(conf_node, "expire_nicks", RECDB_QSTRING);
5169 nickserv_conf.expire_nicks = str ? enabled_string(str) : 0;
d76ed9a9 5170 }
5171 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
5172 for (it=dict_first(child); it; it=iter_next(it)) {
5173 const char *key = iter_key(it), *value;
5174 unsigned char flag;
5175 int pos;
5176
5177 if (!strncasecmp(key, "uc_", 3))
5178 flag = toupper(key[3]);
5179 else if (!strncasecmp(key, "lc_", 3))
5180 flag = tolower(key[3]);
5181 else
5182 flag = key[0];
5183
5184 if ((pos = handle_inverse_flags[flag])) {
5185 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
5186 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
5187 }
5188 }
5189 if (nickserv_conf.weak_password_dict)
5190 dict_delete(nickserv_conf.weak_password_dict);
5191 nickserv_conf.weak_password_dict = dict_new();
5192 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
5193 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
5194 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
5195 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
5196 if (str)
5197 nickserv_load_dict(str);
5198 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
5199 if (nickserv && str)
5200 NickChange(nickserv, str, 0);
5201 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
5202 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
5203 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
5204 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
5205 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
5206 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
5207 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
5208 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
8dc1d9ae 5209 str = database_get_data(conf_node, KEY_SYNC_LOG, RECDB_QSTRING);
5210 nickserv_conf.sync_log = str ? enabled_string(str) : 0;
d76ed9a9 5211 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
5212 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
5213 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
5214 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
5215 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
5216 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
5217 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
5218 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
5219 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
5220 nickserv_conf.titlehost_suffix = str ? str : "example.net";
5a1daaab 5221
7637f48f 5222 free_string_list(nickserv_conf.denied_fakehost_words);
5223 strlist = database_get_data(conf_node, KEY_DENIED_FAKEHOST_WORDS, RECDB_STRING_LIST);
5224 if(strlist)
5225 strlist = string_list_copy(strlist);
5226 else {
5227 strlist = alloc_string_list(4);
5228 string_list_append(strlist, strdup("sex"));
5229 string_list_append(strlist, strdup("fuck"));
5230 }
5231 nickserv_conf.denied_fakehost_words = strlist;
5232
338a82b5 5233 str = database_get_data(conf_node, KEY_DEFAULT_STYLE, RECDB_QSTRING);
5234 nickserv_conf.default_style = str ? str[0] : HI_DEFAULT_STYLE;
5235
5a1daaab 5236 str = database_get_data(conf_node, KEY_AUTO_OPER, RECDB_QSTRING);
5237 nickserv_conf.auto_oper = str ? str : "";
5238
5239 str = database_get_data(conf_node, KEY_AUTO_ADMIN, RECDB_QSTRING);
5240 nickserv_conf.auto_admin = str ? str : "";
5241
69517d70 5242 str = database_get_data(conf_node, KEY_AUTO_OPER_PRIVS, RECDB_QSTRING);
5243 nickserv_conf.auto_oper_privs = str ? str : "";
5244
5245 str = database_get_data(conf_node, KEY_AUTO_ADMIN_PRIVS, RECDB_QSTRING);
5246 nickserv_conf.auto_admin_privs = str ? str : "";
5247
d76ed9a9 5248 str = conf_get_data("server/network", RECDB_QSTRING);
5249 nickserv_conf.network_name = str ? str : "some IRC network";
5250 if (!nickserv_conf.auth_policer_params) {
5251 nickserv_conf.auth_policer_params = policer_params_new();
5252 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
5253 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
5254 }
5255 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
5256 for (it=dict_first(child); it; it=iter_next(it))
5257 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
e166c31b 5258
5259 str = database_get_data(conf_node, KEY_LDAP_ENABLE, RECDB_QSTRING);
5260 nickserv_conf.ldap_enable = str ? strtoul(str, NULL, 0) : 0;
acb142f0 5261
5262 str = database_get_data(conf_node, KEY_FORCE_HANDLES_LOWERCASE, RECDB_QSTRING);
5263 nickserv_conf.force_handles_lowercase = str ? strtol(str, NULL, 0) : 0;
5264
39edf54a 5265#ifndef WITH_LDAP
5266 if(nickserv_conf.ldap_enable > 0) {
5267 /* ldap is enabled but not compiled in - error out */
5268 log_module(MAIN_LOG, LOG_ERROR, "ldap is enabled in config, but not compiled in!");
5269 nickserv_conf.ldap_enable = 0;
5270 sleep(5);
5271 }
5272#endif
e166c31b 5273
39edf54a 5274#ifdef WITH_LDAP
bec5dd26 5275 str = database_get_data(conf_node, KEY_LDAP_URI, RECDB_QSTRING);
5276 nickserv_conf.ldap_uri = str ? str : "";
e166c31b 5277
e166c31b 5278 str = database_get_data(conf_node, KEY_LDAP_BASE, RECDB_QSTRING);
5279 nickserv_conf.ldap_base = str ? str : "";
5280
5281 str = database_get_data(conf_node, KEY_LDAP_DN_FMT, RECDB_QSTRING);
5282 nickserv_conf.ldap_dn_fmt = str ? str : "";
5283
5284 str = database_get_data(conf_node, KEY_LDAP_VERSION, RECDB_QSTRING);
5285 nickserv_conf.ldap_version = str ? strtoul(str, NULL, 0) : 3;
5286
5287 str = database_get_data(conf_node, KEY_LDAP_AUTOCREATE, RECDB_QSTRING);
5288 nickserv_conf.ldap_autocreate = str ? strtoul(str, NULL, 0) : 0;
ea02341b 5289
ddcb3eb3 5290 str = database_get_data(conf_node, KEY_LDAP_TIMEOUT, RECDB_QSTRING);
5291 nickserv_conf.ldap_timeout = str ? strtoul(str, NULL, 0) : 5;
5292
ea02341b 5293 str = database_get_data(conf_node, KEY_LDAP_ADMIN_DN, RECDB_QSTRING);
5294 nickserv_conf.ldap_admin_dn = str ? str : "";
5295
5296 str = database_get_data(conf_node, KEY_LDAP_ADMIN_PASS, RECDB_QSTRING);
5297 nickserv_conf.ldap_admin_pass = str ? str : "";
5298
5299 str = database_get_data(conf_node, KEY_LDAP_FIELD_ACCOUNT, RECDB_QSTRING);
5300 nickserv_conf.ldap_field_account = str ? str : "";
5301
5302 str = database_get_data(conf_node, KEY_LDAP_FIELD_PASSWORD, RECDB_QSTRING);
5303 nickserv_conf.ldap_field_password = str ? str : "";
5304
5305 str = database_get_data(conf_node, KEY_LDAP_FIELD_EMAIL, RECDB_QSTRING);
5306 nickserv_conf.ldap_field_email = str ? str : "";
5307
35ea100f 5308 str = database_get_data(conf_node, KEY_LDAP_FIELD_OSLEVEL, RECDB_QSTRING);
5309 nickserv_conf.ldap_field_oslevel = str ? str : "";
5310
8a729617 5311 str = database_get_data(conf_node, KEY_LDAP_OPER_GROUP_DN, RECDB_QSTRING);
5312 nickserv_conf.ldap_oper_group_dn = str ? str : "";
5313
17d4a698 5314 str = database_get_data(conf_node, KEY_LDAP_OPER_GROUP_LEVEL, RECDB_QSTRING);
5315 nickserv_conf.ldap_oper_group_level = str ? strtoul(str, NULL, 0) : 99;
5316
8a729617 5317 str = database_get_data(conf_node, KEY_LDAP_FIELD_GROUP_MEMBER, RECDB_QSTRING);
5318 nickserv_conf.ldap_field_group_member = str ? str : "";
5319
73d4cc91 5320 free_string_list(nickserv_conf.ldap_object_classes);
5321 strlist = database_get_data(conf_node, KEY_LDAP_OBJECT_CLASSES, RECDB_STRING_LIST);
5322 if(strlist)
5323 strlist = string_list_copy(strlist);
5324 else {
5325 strlist = alloc_string_list(4);
5326 string_list_append(strlist, strdup("top"));
5327 }
5328 nickserv_conf.ldap_object_classes = strlist;
5329
39edf54a 5330#endif
e166c31b 5331
d76ed9a9 5332}
5333
5334static void
5335nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
5336 const char *msg;
5337 char newnick[NICKLEN+1];
5338
5339 assert(user);
5340 assert(ni);
5341 switch (action) {
5342 case RECLAIM_NONE:
5343 /* do nothing */
5344 break;
5345 case RECLAIM_WARN:
5346 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
72971fc8 5347 send_message(user, nickserv, "NSMSG_RECLAIM_HOWTO", ni->owner->handle, nickserv->nick, self->name, ni->owner->handle);
d76ed9a9 5348 break;
5349 case RECLAIM_SVSNICK:
5350 do {
5351 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
5352 } while (GetUserH(newnick));
5353 irc_svsnick(nickserv, user, newnick);
5354 break;
5355 case RECLAIM_KILL:
5356 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
1136f709 5357 DelUser(user, nickserv, 1, msg);
d76ed9a9 5358 break;
5359 }
5360}
5361
5362static void
5363nickserv_reclaim_p(void *data) {
5364 struct userNode *user = data;
5365 struct nick_info *ni = get_nick_info(user->nick);
5366 if (ni)
5367 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
5368}
5369
5370static int
f0fb2e2d 5371check_user_nick(struct userNode *user, UNUSED_ARG(void *extra)) {
d76ed9a9 5372 struct nick_info *ni;
5373 user->modes &= ~FLAGS_REGNICK;
5374 if (!(ni = get_nick_info(user->nick)))
5375 return 0;
5376 if (user->handle_info == ni->owner) {
5377 user->modes |= FLAGS_REGNICK;
5378 irc_regnick(user);
5379 return 0;
5380 }
5381 if (nickserv_conf.warn_nick_owned)
5382 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
72971fc8 5383 send_message(user, nickserv, "NSMSG_RECLAIM_HOWTO", ni->owner->handle, nickserv->nick, self->name, ni->owner->handle);
d76ed9a9 5384 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
5385 return 0;
5386 if (nickserv_conf.auto_reclaim_delay)
5387 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
5388 else
5389 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
d76ed9a9 5390
1136f709 5391 return 0;
d76ed9a9 5392}
5393
5394void
5395handle_account(struct userNode *user, const char *stamp)
5396{
5397 struct handle_info *hi;
b21e2cfe 5398 char *colon;
d76ed9a9 5399
5400#ifdef WITH_PROTOCOL_P10
a9b5e3de 5401 time_t timestamp = 0;
5402
5403 colon = strchr(stamp, ':');
5404 if(colon && colon[1])
5405 {
5406 *colon = 0;
5407 timestamp = atoi(colon+1);
5408 }
d76ed9a9 5409 hi = dict_find(nickserv_handle_dict, stamp, NULL);
c0e47c86 5410 if(hi && timestamp && hi->registered != timestamp)
a9b5e3de 5411 {
4cb36ef0 5412 log_module(MAIN_LOG, LOG_WARNING, "%s using account %s but timestamp does not match %s is not %s.", user->nick, stamp, ctime(&timestamp),
5413ctime(&hi->registered));
a9b5e3de 5414 return;
5415 }
d76ed9a9 5416#else
5417 hi = dict_find(nickserv_id_dict, stamp, NULL);
9b2d838a 5418 log_module(MAIN_LOG, LOG_WARNING, "Using non-P10 code in accounts, not tested at all!");
d76ed9a9 5419#endif
5420
5421 if (hi) {
5422 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
5423 return;
5424 }
5425 set_user_handle_info(user, hi, 0);
5426 } else {
5427 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
5428 }
5429}
5430
5431void
63189c10 5432handle_nick_change(struct userNode *user, const char *old_nick, UNUSED_ARG(void *extra))
d76ed9a9 5433{
5434 struct handle_info *hi;
5435
5436 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
5437 dict_remove(nickserv_allow_auth_dict, old_nick);
5438 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
5439 }
5440 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
f0fb2e2d 5441 check_user_nick(user, NULL);
d76ed9a9 5442}
5443
5444void
a6bcc929 5445nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why), UNUSED_ARG(void *extra))
d76ed9a9 5446{
5447 dict_remove(nickserv_allow_auth_dict, user->nick);
5448 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
5449 set_user_handle_info(user, NULL, 0);
5450}
5451
5452static struct modcmd *
5453nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
5454{
5455 if (min_level > 0) {
5456 char buf[16];
5457 sprintf(buf, "%u", min_level);
5458 if (must_be_qualified) {
5459 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
5460 } else {
5461 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
5462 }
5463 } else if (min_level == 0) {
5464 if (must_be_qualified) {
5465 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
5466 } else {
5467 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
5468 }
5469 } else {
5470 if (must_be_qualified) {
5471 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
5472 } else {
5473 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
5474 }
5475 }
5476}
5477
5478static void
30874d66 5479nickserv_db_cleanup(UNUSED_ARG(void* extra))
d76ed9a9 5480{
a6bcc929 5481 unreg_del_user_func(nickserv_remove_user, NULL);
d76ed9a9 5482 userList_clean(&curr_helpers);
5483 policer_params_delete(nickserv_conf.auth_policer_params);
5484 dict_delete(nickserv_handle_dict);
5485 dict_delete(nickserv_nick_dict);
5486 dict_delete(nickserv_opt_dict);
5487 dict_delete(nickserv_allow_auth_dict);
5488 dict_delete(nickserv_email_dict);
5489 dict_delete(nickserv_id_dict);
5490 dict_delete(nickserv_conf.weak_password_dict);
5491 free(auth_func_list);
81ac4787 5492 free(auth_func_list_extra);
d76ed9a9 5493 free(unreg_func_list);
974d3831 5494 free(unreg_func_list_extra);
d76ed9a9 5495 free(rf_list);
3070719a 5496 free(rf_list_extra);
d76ed9a9 5497 free(allowauth_func_list);
99c332f8 5498 free(allowauth_func_list_extra);
d76ed9a9 5499 free(handle_merge_func_list);
50dafce8 5500 free(handle_merge_func_list_extra);
d76ed9a9 5501 free(failpw_func_list);
c8b793cb 5502 free(failpw_func_list_extra);
d76ed9a9 5503 if (nickserv_conf.valid_handle_regex_set)
5504 regfree(&nickserv_conf.valid_handle_regex);
5505 if (nickserv_conf.valid_nick_regex_set)
5506 regfree(&nickserv_conf.valid_nick_regex);
5507}
5508
81ac4787 5509void handle_loc_auth_oper(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle), UNUSED_ARG(void *extra)) {
c3915bdc 5510 char *privv[MAXNUMPARAMS];
5511 int privc, i;
5512
10be9be0 5513 if (!*nickserv_conf.auto_oper || !user->handle_info)
5514 return;
5515
5516 if (!IsOper(user)) {
5517 if (*nickserv_conf.auto_admin && user->handle_info->opserv_level >= opserv_conf_admin_level()) {
c3915bdc 5518 if (nickserv_conf.auto_admin_privs[0]) {
5519 irc_raw_privs(user, nickserv_conf.auto_admin_privs);
5520 privc = split_line(strdup(nickserv_conf.auto_admin_privs), false, MAXNUMPARAMS, privv);
5521 for (i = 0; i < privc; i++) {
5522 client_modify_priv_by_name(user, privv[i], 1);
5523 }
5524 }
10be9be0 5525 irc_umode(user, nickserv_conf.auto_admin);
5526 irc_sno(0x1, "%s (%s@%s) is now an IRC Administrator",
5527 user->nick, user->ident, user->hostname);
c3915bdc 5528 send_message(user, nickserv, "NSMSG_AUTO_OPER_ADMIN");
10be9be0 5529 } else if (*nickserv_conf.auto_oper && user->handle_info->opserv_level) {
c3915bdc 5530 if (nickserv_conf.auto_oper_privs[0]) {
5531 irc_raw_privs(user, nickserv_conf.auto_oper_privs);
5532 privc = split_line(strdup(nickserv_conf.auto_oper_privs), false, MAXNUMPARAMS, privv);
5533 for (i = 0; i < privc; i++) {
5534 client_modify_priv_by_name(user, privv[i], 1);
5535 }
5536 }
10be9be0 5537 irc_umode(user, nickserv_conf.auto_oper);
5538 irc_sno(0x1, "%s (%s@%s) is now an IRC Operator",
5539 user->nick, user->ident, user->hostname);
c3915bdc 5540 send_message(user, nickserv, "NSMSG_AUTO_OPER");
10be9be0 5541 }
5542 }
5543}
5544
d76ed9a9 5545void
5546init_nickserv(const char *nick)
5547{
7637f48f 5548 struct chanNode *chan;
d76ed9a9 5549 unsigned int i;
5550 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
f0fb2e2d 5551 reg_new_user_func(check_user_nick, NULL);
63189c10 5552 reg_nick_change_func(handle_nick_change, NULL);
a6bcc929 5553 reg_del_user_func(nickserv_remove_user, NULL);
d76ed9a9 5554 reg_account_func(handle_account);
81ac4787 5555 reg_auth_func(handle_loc_auth_oper, NULL);
d76ed9a9 5556
5557 /* set up handle_inverse_flags */
5558 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
5559 for (i=0; handle_flags[i]; i++) {
5560 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
5561 flag_access_levels[i] = 0;
5562 }
5563
5564 conf_register_reload(nickserv_conf_read);
5565 nickserv_opt_dict = dict_new();
5566 nickserv_email_dict = dict_new();
5177fd21 5567
d76ed9a9 5568 dict_set_free_keys(nickserv_email_dict, free);
5569 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
5570
5571 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4048352e 5572/* Removed qualified_host as default requirement for AUTH, REGISTER, PASS, etc. nets
5573 * can enable it per command using modcmd. (its a shitty default IMO, and now in 1.3
5574 * a big pain to disable since its nolonger in the config file. ) -Rubin
5575 */
5576 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+loghostmask", NULL);
d76ed9a9 5577 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4048352e 5578 nickserv_define_func("REGISTER", cmd_register, -1, 0, 0);
d76ed9a9 5579 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4048352e 5580 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 0);
d76ed9a9 5581 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
5582 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
5583 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
5584 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
5585 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
2fa83595 5586 nickserv_define_func("ADDSSLFP", cmd_addsslfp, -1, 1, 0);
5587 nickserv_define_func("OADDSSLFP", cmd_oaddsslfp, 0, 1, 0);
5588 nickserv_define_func("DELSSLFP", cmd_delsslfp, -1, 1, 0);
5589 nickserv_define_func("ODELSSLFP", cmd_odelsslfp, 0, 1, 0);
4048352e 5590 nickserv_define_func("PASS", cmd_pass, -1, 1, 0);
d76ed9a9 5591 nickserv_define_func("SET", cmd_set, -1, 1, 0);
5592 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
5593 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
5594 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
5595 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
5596 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
1136f709 5597 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
d76ed9a9 5598 if (!nickserv_conf.disable_nicks) {
5599 /* nick management commands */
5600 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
5601 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
5602 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
5603 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
5604 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
5605 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
5606 }
5607 if (nickserv_conf.email_enabled) {
5608 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4048352e 5609 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 0);
5610 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 0);
d76ed9a9 5611 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
34938510 5612 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
d76ed9a9 5613 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
5614 }
5615 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
5177fd21 5616 /* ignore commands */
5617 nickserv_define_func("ADDIGNORE", cmd_addignore, -1, 1, 0);
5618 nickserv_define_func("OADDIGNORE", cmd_oaddignore, 0, 1, 0);
5619 nickserv_define_func("DELIGNORE", cmd_delignore, -1, 1, 0);
5620 nickserv_define_func("ODELIGNORE", cmd_odelignore, 0, 1, 0);
d76ed9a9 5621 /* miscellaneous commands */
5622 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
5623 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
5624 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
5625 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
5626 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
1136f709 5627 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
d76ed9a9 5628 /* other options */
5629 dict_insert(nickserv_opt_dict, "INFO", opt_info);
5630 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
5631 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
5632 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
5633 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
7fdb7639 5634 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
338a82b5 5635 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
d76ed9a9 5636 dict_insert(nickserv_opt_dict, "PASS", opt_password);
5637 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
5638 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
5639 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
5640 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
5641 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
0f6fe38c 5642 dict_insert(nickserv_opt_dict, "NOTE", opt_note);
d76ed9a9 5643 if (nickserv_conf.titlehost_suffix) {
5644 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
5645 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
5646 }
0f6fe38c 5647 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
d76ed9a9 5648 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
0b587959 5649 dict_insert(nickserv_opt_dict, "ADVANCED", opt_advanced);
d76ed9a9 5650 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
1136f709 5651 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
d76ed9a9 5652
5653 nickserv_handle_dict = dict_new();
5654 dict_set_free_keys(nickserv_handle_dict, free);
5655 dict_set_free_data(nickserv_handle_dict, free_handle_info);
5656
5657 nickserv_id_dict = dict_new();
5658 dict_set_free_keys(nickserv_id_dict, free);
5659
5660 nickserv_nick_dict = dict_new();
1117fc5a 5661 dict_set_free_data(nickserv_nick_dict, free);
d76ed9a9 5662
5663 nickserv_allow_auth_dict = dict_new();
5664
5665 userList_init(&curr_helpers);
5666
5667 if (nick) {
a32da4c7 5668 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
1136f709 5669 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
d76ed9a9 5670 nickserv_service = service_register(nickserv);
5671 }
5672 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
30874d66 5673 reg_exit_func(nickserv_db_cleanup, NULL);
d76ed9a9 5674 if(nickserv_conf.handle_expire_frequency)
5675 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
ae275267
MB
5676 if(nickserv_conf.nick_expire_frequency && nickserv_conf.expire_nicks)
5677 timeq_add(now + nickserv_conf.nick_expire_frequency, expire_nicks, NULL);
7637f48f 5678
5679 if(autojoin_channels && nickserv) {
5680 for (i = 0; i < autojoin_channels->used; i++) {
5681 chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
5682 AddChannelUser(nickserv, chan)->modes |= MODE_CHANOP;
5683 }
5684 }
e166c31b 5685#ifdef WITH_LDAP
5686 ldap_do_init(nickserv_conf);
5687#endif
7637f48f 5688
d76ed9a9 5689 message_register_table(msgtab);
5690}