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