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