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