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