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