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