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