]> jfr.im git - irc/evilnet/x3.git/blame - src/nickserv.c
Author: Rubin
[irc/evilnet/x3.git] / src / nickserv.c
CommitLineData
d76ed9a9 1/* nickserv.c - Nick/authentication service
2 * Copyright 2000-2004 srvx Development Team
3 *
4 * This file is part of srvx.
5 *
6 * srvx is free software; you can redistribute it and/or modify
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"
23#include "global.h"
24#include "modcmd.h"
25#include "opserv.h" /* for gag_create(), opserv_bad_channel() */
26#include "saxdb.h"
27#include "sendmail.h"
28#include "timeq.h"
29
30#ifdef HAVE_REGEX_H
31#include <regex.h>
32#endif
33
34#define NICKSERV_CONF_NAME "services/nickserv"
35
36#define KEY_DISABLE_NICKS "disable_nicks"
37#define KEY_DEFAULT_HOSTMASK "default_hostmask"
38#define KEY_NICKS_PER_HANDLE "nicks_per_handle"
39#define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
40#define KEY_PASSWORD_MIN_LENGTH "password_min_length"
41#define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
42#define KEY_PASSWORD_MIN_UPPER "password_min_upper"
43#define KEY_PASSWORD_MIN_LOWER "password_min_lower"
44#define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
45#define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
46#define KEY_VALID_NICK_REGEX "valid_nick_regex"
47#define KEY_DB_BACKUP_FREQ "db_backup_freq"
48#define KEY_MODOPER_LEVEL "modoper_level"
49#define KEY_SET_EPITHET_LEVEL "set_epithet_level"
50#define KEY_SET_TITLE_LEVEL "set_title_level"
51#define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
52#define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
53#define KEY_FLAG_LEVELS "flag_levels"
54#define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
55#define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
56#define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
57#define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
58#define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
59#define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
60#define KEY_DICT_FILE "dict_file"
61#define KEY_NICK "nick"
62#define KEY_LANGUAGE "language"
63#define KEY_AUTOGAG_ENABLED "autogag_enabled"
64#define KEY_AUTOGAG_DURATION "autogag_duration"
65#define KEY_AUTH_POLICER "auth_policer"
66#define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
67#define KEY_EMAIL_ENABLED "email_enabled"
68#define KEY_EMAIL_REQUIRED "email_required"
69#define KEY_COOKIE_TIMEOUT "cookie_timeout"
70#define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
71#define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
72
73#define KEY_ID "id"
74#define KEY_PASSWD "passwd"
75#define KEY_NICKS "nicks"
76#define KEY_MASKS "masks"
77#define KEY_OPSERV_LEVEL "opserv_level"
78#define KEY_FLAGS "flags"
79#define KEY_REGISTER_ON "register"
80#define KEY_LAST_SEEN "lastseen"
81#define KEY_INFO "info"
82#define KEY_USERLIST_STYLE "user_style"
83#define KEY_SCREEN_WIDTH "screen_width"
84#define KEY_LAST_AUTHED_HOST "last_authed_host"
85#define KEY_LAST_QUIT_HOST "last_quit_host"
86#define KEY_EMAIL_ADDR "email_addr"
87#define KEY_COOKIE "cookie"
88#define KEY_COOKIE_DATA "data"
89#define KEY_COOKIE_TYPE "type"
90#define KEY_COOKIE_EXPIRES "expires"
91#define KEY_ACTIVATION "activation"
92#define KEY_PASSWORD_CHANGE "password change"
93#define KEY_EMAIL_CHANGE "email change"
94#define KEY_ALLOWAUTH "allowauth"
95#define KEY_EPITHET "epithet"
96#define KEY_TABLE_WIDTH "table_width"
97#define KEY_ANNOUNCEMENTS "announcements"
98#define KEY_MAXLOGINS "maxlogins"
99#define KEY_FAKEHOST "fakehost"
100
101#define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
102
103#define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
104#define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
105typedef OPTION_FUNC(option_func_t);
106
107DEFINE_LIST(handle_info_list, struct handle_info*);
108
109#define NICKSERV_MIN_PARMS(N) do { \
110 if (argc < N) { \
111 reply("MSG_MISSING_PARAMS", argv[0]); \
112 svccmd_send_help(user, nickserv, cmd); \
113 return 0; \
114 } } while (0)
115
116struct userNode *nickserv;
117struct userList curr_helpers;
118const char *handle_flags = HANDLE_FLAGS;
119
120static struct module *nickserv_module;
121static struct service *nickserv_service;
122static struct log_type *NS_LOG;
123static dict_t nickserv_handle_dict; /* contains struct handle_info* */
124static dict_t nickserv_id_dict; /* contains struct handle_info* */
125static dict_t nickserv_nick_dict; /* contains struct nick_info* */
126static dict_t nickserv_opt_dict; /* contains option_func_t* */
127static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
128static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
129static char handle_inverse_flags[256];
130static unsigned int flag_access_levels[32];
131static const struct message_entry msgtab[] = {
132 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
133 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
134 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
135 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
136 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
137 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
138 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
139 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
140 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
141 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
142 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
143 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
144 { "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." },
145 { "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." },
146 { "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." },
147 { "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." },
148 { "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." },
149 { "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." },
150 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
151 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
152 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
153 { "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." },
154 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
155 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
156 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
157 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
158 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
159 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
160 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
161 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
162 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
163 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
164 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
165 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
166 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
167 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
168 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
169 { "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)" },
170 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
171 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
172 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
173 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
174 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
175 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
176 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
177 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
178 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
179 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
180 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
181 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
182 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
183 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
184 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
185 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
186 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
187 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
188 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
189 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
190 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
191 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
192 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
193 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
194 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
195 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
196 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
197 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
198 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
199 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
200 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
201 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
202 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
203 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
204 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
205 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
206 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
207 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
208 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
209 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
210 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
211 { "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)." },
212 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
213 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
214 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
215 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
216 { "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." },
217 { "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." },
218 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
219 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
220 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
221 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
222 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
223 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
224 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
225 { "NSMSG_PASS_SUCCESS", "Password changed." },
226 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
227 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
228 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
229 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
230 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
231 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
232 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
233 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
234 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
235 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
236 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
237 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
238 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
239 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
240 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
241 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
242 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
243 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
244 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
245 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
246 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
247 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
248 { "NSMSG_NO_ACCESS", "Access denied." },
249 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
250 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
251 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
252 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
253 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
254 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
255 { "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." },
256 { "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." },
257 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
258 { "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." },
259 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
260 { "NSMSG_SEARCH_MATCH", "Match: %s" },
261 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
262 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
263 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
264 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
265 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
266 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
267 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
268 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
269 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
270 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
271 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
272 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
273 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
274 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
275 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
276 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
277 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
278 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
279 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
280 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
281 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
282 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
283 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
284 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
285 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
286 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
287 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
288 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
289 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
290 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
291 { "NSEMAIL_ACTIVATION_BODY", "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 %2$s\nTo verify your email address and complete the account registration, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nThis 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 /msg %3$s@%4$s AUTH %5$s your-password\n Please remember to fill in 'your-password' with the actual password you gave to us when you registered.\n\nIf you did NOT request this account, you do not need to do anything. Please contact the %1$s staff if you have questions, and be sure to check our website." },
292 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
293 { "NSEMAIL_PASSWORD_CHANGE_BODY", "This email has been sent to verify that you wish to change the password on your account %5$s. Your cookie is %2$s.\nTo complete the password change, 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 your password to be changed, you do not need to do anything. Please contact the %1$s staff if you have questions." },
294 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
295 { "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." },
296 { "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." },
297 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
298 { "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." },
299 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
300 { "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." },
301 { "CHECKPASS_YES", "Yes." },
302 { "CHECKPASS_NO", "No." },
303 { NULL, NULL }
304};
305
306enum reclaim_action {
307 RECLAIM_NONE,
308 RECLAIM_WARN,
309 RECLAIM_SVSNICK,
310 RECLAIM_KILL
311};
312static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
313static void nickserv_reclaim_p(void *data);
314
315static struct {
316 unsigned int disable_nicks : 1;
317 unsigned int valid_handle_regex_set : 1;
318 unsigned int valid_nick_regex_set : 1;
319 unsigned int autogag_enabled : 1;
320 unsigned int email_enabled : 1;
321 unsigned int email_required : 1;
322 unsigned int default_hostmask : 1;
323 unsigned int warn_nick_owned : 1;
324 unsigned int warn_clone_auth : 1;
325 unsigned long nicks_per_handle;
326 unsigned long password_min_length;
327 unsigned long password_min_digits;
328 unsigned long password_min_upper;
329 unsigned long password_min_lower;
330 unsigned long db_backup_frequency;
331 unsigned long handle_expire_frequency;
332 unsigned long autogag_duration;
333 unsigned long email_visible_level;
334 unsigned long cookie_timeout;
335 unsigned long handle_expire_delay;
336 unsigned long nochan_handle_expire_delay;
337 unsigned long modoper_level;
338 unsigned long set_epithet_level;
339 unsigned long set_title_level;
340 unsigned long set_fakehost_level;
341 unsigned long handles_per_email;
342 unsigned long email_search_level;
343 const char *network_name;
344 const char *titlehost_suffix;
345 regex_t valid_handle_regex;
346 regex_t valid_nick_regex;
347 dict_t weak_password_dict;
348 struct policer_params *auth_policer_params;
349 enum reclaim_action reclaim_action;
350 enum reclaim_action auto_reclaim_action;
351 unsigned long auto_reclaim_delay;
352 unsigned char default_maxlogins;
353 unsigned char hard_maxlogins;
354} nickserv_conf;
355
356/* We have 2^32 unique account IDs to use. */
357unsigned long int highest_id = 0;
358
359static char *
360canonicalize_hostmask(char *mask)
361{
362 char *out = mask, *temp;
363 if ((temp = strchr(mask, '!'))) {
364 temp++;
365 while (*temp) *out++ = *temp++;
366 *out++ = 0;
367 }
368 return mask;
369}
370
371static struct handle_info *
372register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
373{
374 struct handle_info *hi;
375
376#ifdef WITH_PROTOCOL_BAHAMUT
377 char id_base64[IDLEN + 1];
378 do
379 {
380 /* Assign a unique account ID to the account; note that 0 is
381 an invalid account ID. 1 is therefore the first account ID. */
382 if (!id) {
383 id = 1 + highest_id++;
384 } else {
385 /* Note: highest_id is and must always be the highest ID. */
386 if(id > highest_id) {
387 highest_id = id;
388 }
389 }
390 inttobase64(id_base64, id, IDLEN);
391
392 /* Make sure an account with the same ID doesn't exist. If a
393 duplicate is found, log some details and assign a new one.
394 This should be impossible, but it never hurts to expect it. */
395 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
396 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
397 id = 0;
398 }
399 } while(!id);
400#endif
401
402 hi = calloc(1, sizeof(*hi));
403 hi->userlist_style = HI_DEFAULT_STYLE;
404 hi->announcements = '?';
405 hi->handle = strdup(handle);
406 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
407 hi->infoline = NULL;
408 dict_insert(nickserv_handle_dict, hi->handle, hi);
409
410#ifdef WITH_PROTOCOL_BAHAMUT
411 hi->id = id;
412 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
413#endif
414
415 return hi;
416}
417
418static void
419register_nick(const char *nick, struct handle_info *owner)
420{
421 struct nick_info *ni;
422 ni = malloc(sizeof(struct nick_info));
423 safestrncpy(ni->nick, nick, sizeof(ni->nick));
424 ni->owner = owner;
425 ni->next = owner->nicks;
426 owner->nicks = ni;
427 dict_insert(nickserv_nick_dict, ni->nick, ni);
428}
429
430static void
431free_nick_info(void *vni)
432{
433 struct nick_info *ni = vni;
434 free(ni);
435}
436
437static void
438delete_nick(struct nick_info *ni)
439{
440 struct nick_info *last, *next;
441 struct userNode *user;
442 /* Check to see if we should mark a user as unregistered. */
443 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
444 user->modes &= ~FLAGS_REGNICK;
445 irc_regnick(user);
446 }
447 /* Remove ni from the nick_info linked list. */
448 if (ni == ni->owner->nicks) {
449 ni->owner->nicks = ni->next;
450 } else {
451 last = ni->owner->nicks;
452 next = last->next;
453 while (next != ni) {
454 last = next;
455 next = last->next;
456 }
457 last->next = next->next;
458 }
459 dict_remove(nickserv_nick_dict, ni->nick);
460}
461
462static unreg_func_t *unreg_func_list;
463static unsigned int unreg_func_size = 0, unreg_func_used = 0;
464
465void
466reg_unreg_func(unreg_func_t func)
467{
468 if (unreg_func_used == unreg_func_size) {
469 if (unreg_func_size) {
470 unreg_func_size <<= 1;
471 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
472 } else {
473 unreg_func_size = 8;
474 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
475 }
476 }
477 unreg_func_list[unreg_func_used++] = func;
478}
479
480static void
481nickserv_free_cookie(void *data)
482{
483 struct handle_cookie *cookie = data;
484 if (cookie->hi) cookie->hi->cookie = NULL;
485 if (cookie->data) free(cookie->data);
486 free(cookie);
487}
488
489static void
490free_handle_info(void *vhi)
491{
492 struct handle_info *hi = vhi;
493
494#ifdef WITH_PROTOCOL_BAHAMUT
495 char id[IDLEN + 1];
496
497 inttobase64(id, hi->id, IDLEN);
498 dict_remove(nickserv_id_dict, id);
499#endif
500
501 free_string_list(hi->masks);
502 assert(!hi->users);
503
504 while (hi->nicks)
505 delete_nick(hi->nicks);
506 free(hi->infoline);
507 free(hi->epithet);
508 free(hi->fakehost);
509 if (hi->cookie) {
510 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
511 nickserv_free_cookie(hi->cookie);
512 }
513 if (hi->email_addr) {
514 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
515 handle_info_list_remove(hil, hi);
516 if (!hil->used)
517 dict_remove(nickserv_email_dict, hi->email_addr);
518 }
519 free(hi);
520}
521
522static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
523
524static void
525nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
526{
527 unsigned int n;
528
529 for (n=0; n<unreg_func_used; n++)
530 unreg_func_list[n](notify, hi);
531 while (hi->users)
532 set_user_handle_info(hi->users, NULL, 0);
533 if (notify) {
534 if (nickserv_conf.disable_nicks)
535 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
536 else
537 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
538 }
539 dict_remove(nickserv_handle_dict, hi->handle);
540}
541
542struct handle_info*
543get_handle_info(const char *handle)
544{
545 return dict_find(nickserv_handle_dict, handle, 0);
546}
547
548struct nick_info*
549get_nick_info(const char *nick)
550{
551 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
552}
553
554struct modeNode *
555find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
556{
557 unsigned int nn;
558 struct modeNode *mn;
559
560 for (nn=0; nn<channel->members.used; ++nn) {
561 mn = channel->members.list[nn];
562 if ((mn->user != except) && (mn->user->handle_info == handle))
563 return mn;
564 }
565 return NULL;
566}
567
568int
569oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
570 if (!user->handle_info) {
571 if (!quiet)
572 send_message(user, bot, "MSG_AUTHENTICATE");
573 return 0;
574 }
575
576 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
577 if (!quiet)
578 send_message(user, bot, "NSMSG_NO_ACCESS");
579 return 0;
580 }
581
582 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
583 if (!quiet)
584 send_message(user, bot, "MSG_OPER_SUSPENDED");
585 return 0;
586 }
587
588 if (user->handle_info->opserv_level < min_level) {
589 if (!quiet)
590 send_message(user, bot, "NSMSG_NO_ACCESS");
591 return 0;
592 }
593
594 return 1;
595}
596
597static int
598is_valid_handle(const char *handle)
599{
600 struct userNode *user;
601 /* cant register a juped nick/service nick as handle, to prevent confusion */
602 user = GetUserH(handle);
603 if (user && IsLocal(user))
604 return 0;
605 /* check against maximum length */
606 if (strlen(handle) > NICKSERV_HANDLE_LEN)
607 return 0;
608 /* for consistency, only allow account names that could be nicks */
609 if (!is_valid_nick(handle))
610 return 0;
611 /* disallow account names that look like bad words */
612 if (opserv_bad_channel(handle))
613 return 0;
614 /* test either regex or containing all valid chars */
615 if (nickserv_conf.valid_handle_regex_set) {
616 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
617 if (err) {
618 char buff[256];
619 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
620 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
621 }
622 return !err;
623 } else {
624 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
625 }
626}
627
628static int
629is_registerable_nick(const char *nick)
630{
631 /* make sure it could be used as an account name */
632 if (!is_valid_handle(nick))
633 return 0;
634 /* check length */
635 if (strlen(nick) > NICKLEN)
636 return 0;
637 /* test either regex or as valid handle */
638 if (nickserv_conf.valid_nick_regex_set) {
639 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
640 if (err) {
641 char buff[256];
642 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
643 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
644 }
645 return !err;
646 }
647 return 1;
648}
649
650static int
651is_valid_email_addr(const char *email)
652{
653 return strchr(email, '@') != NULL;
654}
655
656static const char *
657visible_email_addr(struct userNode *user, struct handle_info *hi)
658{
659 if (hi->email_addr) {
660 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
661 return hi->email_addr;
662 } else {
663 return "Set.";
664 }
665 } else {
666 return "Not set.";
667 }
668}
669
670struct handle_info *
671smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
672{
673 struct handle_info *hi;
674 struct userNode *target;
675
676 switch (*name) {
677 case '*':
678 if (!(hi = get_handle_info(++name))) {
679 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
680 return 0;
681 }
682 return hi;
683 default:
684 if (!(target = GetUserH(name))) {
685 send_message(user, service, "MSG_NICK_UNKNOWN", name);
686 return 0;
687 }
688 if (IsLocal(target)) {
689 if (IsService(target))
690 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
691 else
692 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
693 return 0;
694 }
695 if (!(hi = target->handle_info)) {
696 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
697 return 0;
698 }
699 return hi;
700 }
701}
702
703int
704oper_outranks(struct userNode *user, struct handle_info *hi) {
705 if (user->handle_info->opserv_level > hi->opserv_level)
706 return 1;
707 if (user->handle_info->opserv_level == hi->opserv_level) {
708 if ((user->handle_info->opserv_level == 1000)
709 || (user->handle_info == hi)
710 || ((user->handle_info->opserv_level == 0)
711 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
712 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
713 return 1;
714 }
715 }
716 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
717 return 0;
718}
719
720static struct handle_info *
721get_victim_oper(struct userNode *user, const char *target)
722{
723 struct handle_info *hi;
724 if (!(hi = smart_get_handle_info(nickserv, user, target)))
725 return 0;
726 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
727 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
728 return 0;
729 }
730 return oper_outranks(user, hi) ? hi : NULL;
731}
732
733static int
734valid_user_for(struct userNode *user, struct handle_info *hi)
735{
736 unsigned int ii;
737
738 /* If no hostmasks on the account, allow it. */
739 if (!hi->masks->used)
740 return 1;
741 /* If any hostmask matches, allow it. */
742 for (ii=0; ii<hi->masks->used; ii++)
743 if (user_matches_glob(user, hi->masks->list[ii], 0))
744 return 1;
745 /* If they are allowauthed to this account, allow it (removing the aa). */
746 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
747 dict_remove(nickserv_allow_auth_dict, user->nick);
748 return 2;
749 }
750 /* The user is not allowed to use this account. */
751 return 0;
752}
753
754static int
755is_secure_password(const char *handle, const char *pass, struct userNode *user)
756{
757 unsigned int i, len;
758 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
759 len = strlen(pass);
760 if (len < nickserv_conf.password_min_length) {
761 if (user)
762 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
763 return 0;
764 }
765 if (!irccasecmp(pass, handle)) {
766 if (user)
767 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
768 return 0;
769 }
770 dict_find(nickserv_conf.weak_password_dict, pass, &i);
771 if (i) {
772 if (user)
773 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
774 return 0;
775 }
776 for (i=0; i<len; i++) {
777 if (isdigit(pass[i]))
778 cnt_digits++;
779 if (isupper(pass[i]))
780 cnt_upper++;
781 if (islower(pass[i]))
782 cnt_lower++;
783 }
784 if ((cnt_lower < nickserv_conf.password_min_lower)
785 || (cnt_upper < nickserv_conf.password_min_upper)
786 || (cnt_digits < nickserv_conf.password_min_digits)) {
787 if (user)
788 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
789 return 0;
790 }
791 return 1;
792}
793
794static auth_func_t *auth_func_list;
795static unsigned int auth_func_size = 0, auth_func_used = 0;
796
797void
798reg_auth_func(auth_func_t func)
799{
800 if (auth_func_used == auth_func_size) {
801 if (auth_func_size) {
802 auth_func_size <<= 1;
803 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
804 } else {
805 auth_func_size = 8;
806 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
807 }
808 }
809 auth_func_list[auth_func_used++] = func;
810}
811
812static handle_rename_func_t *rf_list;
813static unsigned int rf_list_size, rf_list_used;
814
815void
816reg_handle_rename_func(handle_rename_func_t func)
817{
818 if (rf_list_used == rf_list_size) {
819 if (rf_list_size) {
820 rf_list_size <<= 1;
821 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
822 } else {
823 rf_list_size = 8;
824 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
825 }
826 }
827 rf_list[rf_list_used++] = func;
828}
829
830static char *
831generate_fakehost(struct handle_info *handle)
832{
833 extern const char *hidden_host_suffix;
834 static char buffer[HOSTLEN+1];
835
836 if (!handle->fakehost) {
837 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
838 return buffer;
839 } else if (handle->fakehost[0] == '.') {
840 /* A leading dot indicates the stored value is actually a title. */
841 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
842 return buffer;
843 }
844 return handle->fakehost;
845}
846
847static void
848apply_fakehost(struct handle_info *handle)
849{
850 struct userNode *target;
851 char *fake;
852
853 if (!handle->users)
854 return;
855 fake = generate_fakehost(handle);
856 for (target = handle->users; target; target = target->next_authed)
857 assign_fakehost(target, fake, 1);
858}
859
860static void
861set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
862{
863 unsigned int n;
864 struct handle_info *old_info;
865
866 /* This can happen if somebody uses COOKIE while authed, or if
867 * they re-auth to their current handle (which is silly, but users
868 * are like that). */
869 if (user->handle_info == hi)
870 return;
871
872 if (user->handle_info) {
873 struct userNode *other;
874
875 if (IsHelper(user))
876 userList_remove(&curr_helpers, user);
877
878 /* remove from next_authed linked list */
879 if (user->handle_info->users == user) {
880 user->handle_info->users = user->next_authed;
881 } else {
882 for (other = user->handle_info->users;
883 other->next_authed != user;
884 other = other->next_authed) ;
885 other->next_authed = user->next_authed;
886 }
887 /* if nobody left on old handle, and they're not an oper, remove !god */
888 if (!user->handle_info->users && !user->handle_info->opserv_level)
889 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
890 /* record them as being last seen at this time */
891 user->handle_info->lastseen = now;
892 /* and record their hostmask */
893 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
894 }
895 old_info = user->handle_info;
896 user->handle_info = hi;
897 if (hi && !hi->users && !hi->opserv_level)
898 HANDLE_CLEAR_FLAG(hi, HELPING);
899 for (n=0; n<auth_func_used; n++)
900 auth_func_list[n](user, old_info);
901 if (hi) {
902 struct nick_info *ni;
903
904 HANDLE_CLEAR_FLAG(hi, FROZEN);
905 if (nickserv_conf.warn_clone_auth) {
906 struct userNode *other;
907 for (other = hi->users; other; other = other->next_authed)
908 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
909 }
910 user->next_authed = hi->users;
911 hi->users = user;
912 hi->lastseen = now;
913 if (IsHelper(user))
914 userList_append(&curr_helpers, user);
915
916 if (hi->fakehost || old_info)
917 apply_fakehost(hi);
918
919 if (stamp) {
920#ifdef WITH_PROTOCOL_BAHAMUT
921 /* Stamp users with their account ID. */
922 char id[IDLEN + 1];
923 inttobase64(id, hi->id, IDLEN);
924#elif WITH_PROTOCOL_P10
925 /* Stamp users with their account name. */
926 char *id = hi->handle;
927#else
928 const char *id = "???";
929#endif
930 if (!nickserv_conf.disable_nicks) {
931 struct nick_info *ni;
932 for (ni = hi->nicks; ni; ni = ni->next) {
933 if (!irccasecmp(user->nick, ni->nick)) {
934 user->modes |= FLAGS_REGNICK;
935 break;
936 }
937 }
938 }
939 StampUser(user, id);
940 }
941
942 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
943 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
944 } else {
945 /* We cannot clear the user's account ID, unfortunately. */
946 user->next_authed = NULL;
947 }
948}
949
950static struct handle_info*
951nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
952{
953 struct handle_info *hi;
954 struct nick_info *ni;
955 char crypted[MD5_CRYPT_LENGTH];
956
957 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
958 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
959 return 0;
960 }
961
962 if (!is_secure_password(handle, passwd, user))
963 return 0;
964
965 cryptpass(passwd, crypted);
966 hi = register_handle(handle, crypted, 0);
967 hi->masks = alloc_string_list(1);
968 hi->users = NULL;
969 hi->language = lang_C;
970 hi->registered = now;
971 hi->lastseen = now;
972 hi->flags = HI_DEFAULT_FLAGS;
973 if (settee && !no_auth)
974 set_user_handle_info(settee, hi, 1);
975
976 if (user != settee)
977 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
978 else if (nickserv_conf.disable_nicks)
979 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
980 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
981 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
982 else {
983 register_nick(user->nick, hi);
984 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
985 }
986 if (settee && (user != settee))
987 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
988 return hi;
989}
990
991static void
992nickserv_bake_cookie(struct handle_cookie *cookie)
993{
994 cookie->hi->cookie = cookie;
995 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
996}
997
998static void
999nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1000{
1001 struct handle_cookie *cookie;
1002 char subject[128], body[4096], *misc;
1003 const char *netname, *fmt;
1004 int first_time = 0;
1005
1006 if (hi->cookie) {
1007 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1008 return;
1009 }
1010
1011 cookie = calloc(1, sizeof(*cookie));
1012 cookie->hi = hi;
1013 cookie->type = type;
1014 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1015 cookie->expires = now + nickserv_conf.cookie_timeout;
1016 inttobase64(cookie->cookie, rand(), 5);
1017 inttobase64(cookie->cookie+5, rand(), 5);
1018
1019 netname = nickserv_conf.network_name;
1020 subject[0] = 0;
1021
1022 switch (cookie->type) {
1023 case ACTIVATION:
1024 hi->passwd[0] = 0; /* invalidate password */
1025 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1026 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1027 snprintf(subject, sizeof(subject), fmt, netname);
1028 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1029 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1030 first_time = 1;
1031 break;
1032 case PASSWORD_CHANGE:
1033 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1034 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1035 snprintf(subject, sizeof(subject), fmt, netname);
1036 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1037 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1038 break;
1039 case EMAIL_CHANGE:
1040 misc = hi->email_addr;
1041 hi->email_addr = cookie->data;
1042 if (misc) {
1043 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1044 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1045 snprintf(subject, sizeof(subject), fmt, netname);
1046 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1047 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1048 sendmail(nickserv, hi, subject, body, 1);
1049 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1050 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1051 } else {
1052 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1053 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1054 snprintf(subject, sizeof(subject), fmt, netname);
1055 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1056 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1057 sendmail(nickserv, hi, subject, body, 1);
1058 subject[0] = 0;
1059 }
1060 hi->email_addr = misc;
1061 break;
1062 case ALLOWAUTH:
1063 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1064 snprintf(subject, sizeof(subject), fmt, netname);
1065 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1066 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1067 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1068 break;
1069 default:
1070 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1071 break;
1072 }
1073 if (subject[0])
1074 sendmail(nickserv, hi, subject, body, first_time);
1075 nickserv_bake_cookie(cookie);
1076}
1077
1078static void
1079nickserv_eat_cookie(struct handle_cookie *cookie)
1080{
1081 cookie->hi->cookie = NULL;
1082 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1083 nickserv_free_cookie(cookie);
1084}
1085
1086static void
1087nickserv_free_email_addr(void *data)
1088{
1089 handle_info_list_clean(data);
1090 free(data);
1091}
1092
1093static void
1094nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1095{
1096 struct handle_info_list *hil;
1097 /* Remove from old handle_info_list ... */
1098 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1099 handle_info_list_remove(hil, hi);
1100 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1101 hi->email_addr = NULL;
1102 }
1103 /* Add to the new list.. */
1104 if (new_email_addr) {
1105 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1106 hil = calloc(1, sizeof(*hil));
1107 hil->tag = strdup(new_email_addr);
1108 handle_info_list_init(hil);
1109 dict_insert(nickserv_email_dict, hil->tag, hil);
1110 }
1111 handle_info_list_append(hil, hi);
1112 hi->email_addr = hil->tag;
1113 }
1114}
1115
1116static NICKSERV_FUNC(cmd_register)
1117{
1118 struct handle_info *hi;
1119 const char *email_addr, *password;
1120 int no_auth;
1121
1122 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1123 /* Require the first handle registered to belong to someone +o. */
1124 reply("NSMSG_REQUIRE_OPER");
1125 return 0;
1126 }
1127
1128 if (user->handle_info) {
1129 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1130 return 0;
1131 }
1132
1133 if (IsRegistering(user)) {
1134 reply("NSMSG_ALREADY_REGISTERING");
1135 return 0;
1136 }
1137
1138 if (IsStamped(user)) {
1139 /* Unauthenticated users might still have been stamped
1140 previously and could therefore have a hidden host;
1141 do not allow them to register a new account. */
1142 reply("NSMSG_STAMPED_REGISTER");
1143 return 0;
1144 }
1145
1146 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1147
1148 if (!is_valid_handle(argv[1])) {
1149 reply("NSMSG_BAD_HANDLE", argv[1]);
1150 return 0;
1151 }
1152
1153 if ((argc >= 4) && nickserv_conf.email_enabled) {
1154 struct handle_info_list *hil;
1155 const char *str;
1156
1157 /* Remember email address. */
1158 email_addr = argv[3];
1159
1160 /* Check that the email address looks valid.. */
1161 if (!is_valid_email_addr(email_addr)) {
1162 reply("NSMSG_BAD_EMAIL_ADDR");
1163 return 0;
1164 }
1165
1166 /* .. and that we are allowed to send to it. */
1167 if ((str = sendmail_prohibited_address(email_addr))) {
1168 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1169 return 0;
1170 }
1171
1172 /* If we do email verify, make sure we don't spam the address. */
1173 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1174 unsigned int nn;
1175 for (nn=0; nn<hil->used; nn++) {
1176 if (hil->list[nn]->cookie) {
1177 reply("NSMSG_EMAIL_UNACTIVATED");
1178 return 0;
1179 }
1180 }
1181 if (hil->used >= nickserv_conf.handles_per_email) {
1182 reply("NSMSG_EMAIL_OVERUSED");
1183 return 0;
1184 }
1185 }
1186
1187 no_auth = 1;
1188 } else {
1189 email_addr = 0;
1190 no_auth = 0;
1191 }
1192
1193 password = argv[2];
1194 argv[2] = "****";
1195 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1196 return 0;
1197 /* Add any masks they should get. */
1198 if (nickserv_conf.default_hostmask) {
1199 string_list_append(hi->masks, strdup("*@*"));
1200 } else {
1201 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1202 if (user->ip.s_addr && user->hostname[strspn(user->hostname, "0123456789.")])
1203 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1204 }
1205
1206 /* If they're the first to register, give them level 1000. */
1207 if (dict_size(nickserv_handle_dict) == 1) {
1208 hi->opserv_level = 1000;
1209 reply("NSMSG_ROOT_HANDLE", argv[1]);
1210 }
1211
1212 /* Set their email address. */
1213 if (email_addr)
1214 nickserv_set_email_addr(hi, email_addr);
1215
1216 /* If they need to do email verification, tell them. */
1217 if (no_auth)
1218 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1219
1220 /* Set registering flag.. */
1221 user->modes |= FLAGS_REGISTERING;
1222
1223 return 1;
1224}
1225
1226static NICKSERV_FUNC(cmd_oregister)
1227{
1228 char *mask;
1229 struct userNode *settee;
1230 struct handle_info *hi;
1231
1232 NICKSERV_MIN_PARMS(4);
1233
1234 if (!is_valid_handle(argv[1])) {
1235 reply("NSMSG_BAD_HANDLE", argv[1]);
1236 return 0;
1237 }
1238
1239 if (strchr(argv[3], '@')) {
1240 mask = canonicalize_hostmask(strdup(argv[3]));
1241 if (argc > 4) {
1242 settee = GetUserH(argv[4]);
1243 if (!settee) {
1244 reply("MSG_NICK_UNKNOWN", argv[4]);
1245 free(mask);
1246 return 0;
1247 }
1248 } else {
1249 settee = NULL;
1250 }
1251 } else if ((settee = GetUserH(argv[3]))) {
1252 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1253 } else {
1254 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1255 return 0;
1256 }
1257 if (settee && settee->handle_info) {
1258 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1259 free(mask);
1260 return 0;
1261 }
1262 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1263 free(mask);
1264 return 0;
1265 }
1266 string_list_append(hi->masks, mask);
1267 return 1;
1268}
1269
1270static NICKSERV_FUNC(cmd_handleinfo)
1271{
1272 char buff[400];
1273 unsigned int i, pos=0, herelen;
1274 struct userNode *target, *next_un;
1275 struct handle_info *hi;
1276 const char *nsmsg_none;
1277
1278 if (argc < 2) {
1279 if (!(hi = user->handle_info)) {
1280 reply("NSMSG_MUST_AUTH");
1281 return 0;
1282 }
1283 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1284 return 0;
1285 }
1286
1287 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1288 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1289#ifdef WITH_PROTOCOL_BAHAMUT
1290 reply("NSMSG_HANDLEINFO_ID", hi->id);
1291#endif
1292 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1293
1294 if (!hi->users) {
1295 intervalString(buff, now - hi->lastseen, user->handle_info);
1296 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1297 } else {
1298 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1299 }
1300
1301 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1302 if (HANDLE_FLAGGED(hi, FROZEN))
1303 reply("NSMSG_HANDLEINFO_VACATION");
1304
1305 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1306 struct do_not_register *dnr;
1307 if ((dnr = chanserv_is_dnr(NULL, hi)))
1308 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1309 if (!oper_outranks(user, hi))
1310 return 1;
1311 } else if (hi != user->handle_info)
1312 return 1;
1313
1314 if (nickserv_conf.email_enabled)
1315 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1316
1317 if (hi->cookie) {
1318 const char *type;
1319 switch (hi->cookie->type) {
1320 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1321 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1322 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1323 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1324 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1325 }
1326 reply(type);
1327 }
1328
1329 if (hi->flags) {
1330 unsigned long flen = 1;
1331 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1332 flags[0] = '+';
1333 for (i=0, flen=1; handle_flags[i]; i++)
1334 if (hi->flags & 1 << i)
1335 flags[flen++] = handle_flags[i];
1336 flags[flen] = 0;
1337 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1338 } else {
1339 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1340 }
1341
1342 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1343 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1344 || (hi->opserv_level > 0)) {
1345 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1346 }
1347
1348 if (hi->fakehost)
1349 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1350
1351 if (hi->last_quit_host[0])
1352 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1353 else
1354 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1355
1356 if (nickserv_conf.disable_nicks) {
1357 /* nicks disabled; don't show anything about registered nicks */
1358 } else if (hi->nicks) {
1359 struct nick_info *ni, *next_ni;
1360 for (ni = hi->nicks; ni; ni = next_ni) {
1361 herelen = strlen(ni->nick);
1362 if (pos + herelen + 1 > ArrayLength(buff)) {
1363 next_ni = ni;
1364 goto print_nicks_buff;
1365 } else {
1366 next_ni = ni->next;
1367 }
1368 memcpy(buff+pos, ni->nick, herelen);
1369 pos += herelen; buff[pos++] = ' ';
1370 if (!next_ni) {
1371 print_nicks_buff:
1372 buff[pos-1] = 0;
1373 reply("NSMSG_HANDLEINFO_NICKS", buff);
1374 pos = 0;
1375 }
1376 }
1377 } else {
1378 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1379 }
1380
1381 if (hi->masks->used) {
1382 for (i=0; i < hi->masks->used; i++) {
1383 herelen = strlen(hi->masks->list[i]);
1384 if (pos + herelen + 1 > ArrayLength(buff)) {
1385 i--;
1386 goto print_mask_buff;
1387 }
1388 memcpy(buff+pos, hi->masks->list[i], herelen);
1389 pos += herelen; buff[pos++] = ' ';
1390 if (i+1 == hi->masks->used) {
1391 print_mask_buff:
1392 buff[pos-1] = 0;
1393 reply("NSMSG_HANDLEINFO_MASKS", buff);
1394 pos = 0;
1395 }
1396 }
1397 } else {
1398 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1399 }
1400
1401 if (hi->channels) {
1402 struct userData *channel, *next;
1403 char *name;
1404
1405 for (channel = hi->channels; channel; channel = next) {
1406 next = channel->u_next;
1407 name = channel->channel->channel->name;
1408 herelen = strlen(name);
1409 if (pos + herelen + 7 > ArrayLength(buff)) {
1410 next = channel;
1411 goto print_chans_buff;
1412 }
1413 if (IsUserSuspended(channel))
1414 buff[pos++] = '-';
1415 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1416 if (next == NULL) {
1417 print_chans_buff:
1418 buff[pos-1] = 0;
1419 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1420 pos = 0;
1421 }
1422 }
1423 } else {
1424 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1425 }
1426
1427 for (target = hi->users; target; target = next_un) {
1428 herelen = strlen(target->nick);
1429 if (pos + herelen + 1 > ArrayLength(buff)) {
1430 next_un = target;
1431 goto print_cnick_buff;
1432 } else {
1433 next_un = target->next_authed;
1434 }
1435 memcpy(buff+pos, target->nick, herelen);
1436 pos += herelen; buff[pos++] = ' ';
1437 if (!next_un) {
1438 print_cnick_buff:
1439 buff[pos-1] = 0;
1440 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1441 pos = 0;
1442 }
1443 }
1444
1445 return 1;
1446}
1447
1448static NICKSERV_FUNC(cmd_userinfo)
1449{
1450 struct userNode *target;
1451
1452 NICKSERV_MIN_PARMS(2);
1453 if (!(target = GetUserH(argv[1]))) {
1454 reply("MSG_NICK_UNKNOWN", argv[1]);
1455 return 0;
1456 }
1457 if (target->handle_info)
1458 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1459 else
1460 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1461 return 1;
1462}
1463
1464static NICKSERV_FUNC(cmd_nickinfo)
1465{
1466 struct nick_info *ni;
1467
1468 NICKSERV_MIN_PARMS(2);
1469 if (!(ni = get_nick_info(argv[1]))) {
1470 reply("MSG_NICK_UNKNOWN", argv[1]);
1471 return 0;
1472 }
1473 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1474 return 1;
1475}
1476
1477static NICKSERV_FUNC(cmd_rename_handle)
1478{
1479 struct handle_info *hi;
1480 char msgbuf[MAXLEN], *old_handle;
1481 unsigned int nn;
1482
1483 NICKSERV_MIN_PARMS(3);
1484 if (!(hi = get_victim_oper(user, argv[1])))
1485 return 0;
1486 if (!is_valid_handle(argv[2])) {
1487 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1488 return 0;
1489 }
1490 if (get_handle_info(argv[2])) {
1491 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1492 return 0;
1493 }
1494
1495 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1496 hi->handle = strdup(argv[2]);
1497 dict_insert(nickserv_handle_dict, hi->handle, hi);
1498 for (nn=0; nn<rf_list_used; nn++)
1499 rf_list[nn](hi, old_handle);
1500 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1501 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1502 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1503 free(old_handle);
1504 return 1;
1505}
1506
1507static failpw_func_t *failpw_func_list;
1508static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1509
1510void
1511reg_failpw_func(failpw_func_t func)
1512{
1513 if (failpw_func_used == failpw_func_size) {
1514 if (failpw_func_size) {
1515 failpw_func_size <<= 1;
1516 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1517 } else {
1518 failpw_func_size = 8;
1519 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1520 }
1521 }
1522 failpw_func_list[failpw_func_used++] = func;
1523}
1524
1525static NICKSERV_FUNC(cmd_auth)
1526{
1527 int pw_arg, used, maxlogins;
1528 struct handle_info *hi;
1529 const char *passwd;
1530 struct userNode *other;
1531
1532 if (user->handle_info) {
1533 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1534 return 0;
1535 }
1536 if (IsStamped(user)) {
1537 /* Unauthenticated users might still have been stamped
1538 previously and could therefore have a hidden host;
1539 do not allow them to authenticate. */
1540 reply("NSMSG_STAMPED_AUTH");
1541 return 0;
1542 }
1543 if (argc == 3) {
1544 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1545 pw_arg = 2;
1546 } else if (argc == 2) {
1547 if (nickserv_conf.disable_nicks) {
1548 if (!(hi = get_handle_info(user->nick))) {
1549 reply("NSMSG_HANDLE_NOT_FOUND");
1550 return 0;
1551 }
1552 } else {
1553 /* try to look up their handle from their nick */
1554 struct nick_info *ni;
1555 ni = get_nick_info(user->nick);
1556 if (!ni) {
1557 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1558 return 0;
1559 }
1560 hi = ni->owner;
1561 }
1562 pw_arg = 1;
1563 } else {
1564 reply("MSG_MISSING_PARAMS", argv[0]);
1565 svccmd_send_help(user, nickserv, cmd);
1566 return 0;
1567 }
1568 if (!hi) {
1569 reply("NSMSG_HANDLE_NOT_FOUND");
1570 return 0;
1571 }
1572 /* Responses from here on look up the language used by the handle they asked about. */
1573 passwd = argv[pw_arg];
1574 if (!valid_user_for(user, hi)) {
1575 if (hi->email_addr && nickserv_conf.email_enabled)
1576 send_message_type(4, user, cmd->parent->bot,
1577 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1578 hi->handle);
1579 else
1580 send_message_type(4, user, cmd->parent->bot,
1581 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1582 hi->handle);
1583 argv[pw_arg] = "BADMASK";
1584 return 1;
1585 }
1586 if (!checkpass(passwd, hi->passwd)) {
1587 unsigned int n;
1588 send_message_type(4, user, cmd->parent->bot,
1589 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1590 argv[pw_arg] = "BADPASS";
1591 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1592 if (nickserv_conf.autogag_enabled) {
1593 if (!user->auth_policer.params) {
1594 user->auth_policer.last_req = now;
1595 user->auth_policer.params = nickserv_conf.auth_policer_params;
1596 }
1597 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1598 char *hostmask;
1599 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1600 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1601 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1602 free(hostmask);
1603 argv[pw_arg] = "GAGGED";
1604 }
1605 }
1606 return 1;
1607 }
1608 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1609 send_message_type(4, user, cmd->parent->bot,
1610 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1611 argv[pw_arg] = "SUSPENDED";
1612 return 1;
1613 }
1614 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1615 for (used = 0, other = hi->users; other; other = other->next_authed) {
1616 if (++used >= maxlogins) {
1617 send_message_type(4, user, cmd->parent->bot,
1618 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1619 maxlogins);
1620 argv[pw_arg] = "MAXLOGINS";
1621 return 1;
1622 }
1623 }
1624
1625 set_user_handle_info(user, hi, 1);
1626 if (nickserv_conf.email_required && !hi->email_addr)
1627 reply("NSMSG_PLEASE_SET_EMAIL");
1628 if (!is_secure_password(hi->handle, passwd, NULL))
1629 reply("NSMSG_WEAK_PASSWORD");
1630 if (hi->passwd[0] != '$')
1631 cryptpass(passwd, hi->passwd);
1632 reply("NSMSG_AUTH_SUCCESS");
1633 argv[pw_arg] = "****";
1634 return 1;
1635}
1636
1637static allowauth_func_t *allowauth_func_list;
1638static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1639
1640void
1641reg_allowauth_func(allowauth_func_t func)
1642{
1643 if (allowauth_func_used == allowauth_func_size) {
1644 if (allowauth_func_size) {
1645 allowauth_func_size <<= 1;
1646 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1647 } else {
1648 allowauth_func_size = 8;
1649 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1650 }
1651 }
1652 allowauth_func_list[allowauth_func_used++] = func;
1653}
1654
1655static NICKSERV_FUNC(cmd_allowauth)
1656{
1657 struct userNode *target;
1658 struct handle_info *hi;
1659 unsigned int n;
1660
1661 NICKSERV_MIN_PARMS(2);
1662 if (!(target = GetUserH(argv[1]))) {
1663 reply("MSG_NICK_UNKNOWN", argv[1]);
1664 return 0;
1665 }
1666 if (target->handle_info) {
1667 reply("NSMSG_USER_PREV_AUTH", target->nick);
1668 return 0;
1669 }
1670 if (IsStamped(target)) {
1671 /* Unauthenticated users might still have been stamped
1672 previously and could therefore have a hidden host;
1673 do not allow them to authenticate to an account. */
1674 reply("NSMSG_USER_PREV_STAMP", target->nick);
1675 return 0;
1676 }
1677 if (argc == 2)
1678 hi = NULL;
1679 else if (!(hi = get_handle_info(argv[2]))) {
1680 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1681 return 0;
1682 }
1683 if (hi) {
1684 if (hi->opserv_level > user->handle_info->opserv_level) {
1685 reply("MSG_USER_OUTRANKED", hi->handle);
1686 return 0;
1687 }
1688 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1689 || (hi->opserv_level > 0))
1690 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1691 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1692 return 0;
1693 }
1694 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1695 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1696 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1697 if (nickserv_conf.email_enabled)
1698 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1699 } else {
1700 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1701 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1702 else
1703 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1704 }
1705 for (n=0; n<allowauth_func_used; n++)
1706 allowauth_func_list[n](user, target, hi);
1707 return 1;
1708}
1709
1710static NICKSERV_FUNC(cmd_authcookie)
1711{
1712 struct handle_info *hi;
1713
1714 NICKSERV_MIN_PARMS(2);
1715 if (user->handle_info) {
1716 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1717 return 0;
1718 }
1719 if (IsStamped(user)) {
1720 /* Unauthenticated users might still have been stamped
1721 previously and could therefore have a hidden host;
1722 do not allow them to authenticate to an account. */
1723 reply("NSMSG_STAMPED_AUTHCOOKIE");
1724 return 0;
1725 }
1726 if (!(hi = get_handle_info(argv[1]))) {
1727 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1728 return 0;
1729 }
1730 if (!hi->email_addr) {
1731 reply("MSG_SET_EMAIL_ADDR");
1732 return 0;
1733 }
1734 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1735 return 1;
1736}
1737
1738static NICKSERV_FUNC(cmd_delcookie)
1739{
1740 struct handle_info *hi;
1741
1742 hi = user->handle_info;
1743 if (!hi->cookie) {
1744 reply("NSMSG_NO_COOKIE");
1745 return 0;
1746 }
1747 switch (hi->cookie->type) {
1748 case ACTIVATION:
1749 case EMAIL_CHANGE:
1750 reply("NSMSG_MUST_TIME_OUT");
1751 break;
1752 default:
1753 nickserv_eat_cookie(hi->cookie);
1754 reply("NSMSG_ATE_COOKIE");
1755 break;
1756 }
1757 return 1;
1758}
1759
1760static NICKSERV_FUNC(cmd_resetpass)
1761{
1762 struct handle_info *hi;
1763 char crypted[MD5_CRYPT_LENGTH];
1764
1765 NICKSERV_MIN_PARMS(3);
1766 if (user->handle_info) {
1767 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1768 return 0;
1769 }
1770 if (IsStamped(user)) {
1771 /* Unauthenticated users might still have been stamped
1772 previously and could therefore have a hidden host;
1773 do not allow them to activate an account. */
1774 reply("NSMSG_STAMPED_RESETPASS");
1775 return 0;
1776 }
1777 if (!(hi = get_handle_info(argv[1]))) {
1778 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1779 return 0;
1780 }
1781 if (!hi->email_addr) {
1782 reply("MSG_SET_EMAIL_ADDR");
1783 return 0;
1784 }
1785 cryptpass(argv[2], crypted);
1786 argv[2] = "****";
1787 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1788 return 1;
1789}
1790
1791static NICKSERV_FUNC(cmd_cookie)
1792{
1793 struct handle_info *hi;
1794 const char *cookie;
1795
1796 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1797 cookie = argv[1];
1798 } else {
1799 NICKSERV_MIN_PARMS(3);
1800 if (!(hi = get_handle_info(argv[1]))) {
1801 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1802 return 0;
1803 }
1804 cookie = argv[2];
1805 }
1806
1807 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1808 reply("NSMSG_HANDLE_SUSPENDED");
1809 return 0;
1810 }
1811
1812 if (!hi->cookie) {
1813 reply("NSMSG_NO_COOKIE");
1814 return 0;
1815 }
1816
1817 /* Check validity of operation before comparing cookie to
1818 * prohibit guessing by authed users. */
1819 if (user->handle_info
1820 && (hi->cookie->type != EMAIL_CHANGE)
1821 && (hi->cookie->type != PASSWORD_CHANGE)) {
1822 reply("NSMSG_CANNOT_COOKIE");
1823 return 0;
1824 }
1825
1826 if (strcmp(cookie, hi->cookie->cookie)) {
1827 reply("NSMSG_BAD_COOKIE");
1828 return 0;
1829 }
1830
1831 switch (hi->cookie->type) {
1832 case ACTIVATION:
1833 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1834 set_user_handle_info(user, hi, 1);
1835 reply("NSMSG_HANDLE_ACTIVATED");
1836 break;
1837 case PASSWORD_CHANGE:
1838 set_user_handle_info(user, hi, 1);
1839 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1840 reply("NSMSG_PASSWORD_CHANGED");
1841 break;
1842 case EMAIL_CHANGE:
1843 nickserv_set_email_addr(hi, hi->cookie->data);
1844 reply("NSMSG_EMAIL_CHANGED");
1845 break;
1846 case ALLOWAUTH:
1847 set_user_handle_info(user, hi, 1);
1848 reply("NSMSG_AUTH_SUCCESS");
1849 break;
1850 default:
1851 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1852 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1853 break;
1854 }
1855
1856 nickserv_eat_cookie(hi->cookie);
1857
1858 return 1;
1859}
1860
1861static NICKSERV_FUNC(cmd_oregnick) {
1862 const char *nick;
1863 struct handle_info *target;
1864 struct nick_info *ni;
1865
1866 NICKSERV_MIN_PARMS(3);
1867 if (!(target = modcmd_get_handle_info(user, argv[1])))
1868 return 0;
1869 nick = argv[2];
1870 if (!is_registerable_nick(nick)) {
1871 reply("NSMSG_BAD_NICK", nick);
1872 return 0;
1873 }
1874 ni = dict_find(nickserv_nick_dict, nick, NULL);
1875 if (ni) {
1876 reply("NSMSG_NICK_EXISTS", nick);
1877 return 0;
1878 }
1879 register_nick(nick, target);
1880 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1881 return 1;
1882}
1883
1884static NICKSERV_FUNC(cmd_regnick) {
1885 unsigned n;
1886 struct nick_info *ni;
1887
1888 if (!is_registerable_nick(user->nick)) {
1889 reply("NSMSG_BAD_NICK", user->nick);
1890 return 0;
1891 }
1892 /* count their nicks, see if it's too many */
1893 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1894 if (n >= nickserv_conf.nicks_per_handle) {
1895 reply("NSMSG_TOO_MANY_NICKS");
1896 return 0;
1897 }
1898 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1899 if (ni) {
1900 reply("NSMSG_NICK_EXISTS", user->nick);
1901 return 0;
1902 }
1903 register_nick(user->nick, user->handle_info);
1904 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1905 return 1;
1906}
1907
1908static NICKSERV_FUNC(cmd_pass)
1909{
1910 struct handle_info *hi;
1911 const char *old_pass, *new_pass;
1912
1913 NICKSERV_MIN_PARMS(3);
1914 hi = user->handle_info;
1915 old_pass = argv[1];
1916 new_pass = argv[2];
1917 argv[2] = "****";
1918 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1919 if (!checkpass(old_pass, hi->passwd)) {
1920 argv[1] = "BADPASS";
1921 reply("NSMSG_PASSWORD_INVALID");
1922 return 0;
1923 }
1924 cryptpass(new_pass, hi->passwd);
1925 argv[1] = "****";
1926 reply("NSMSG_PASS_SUCCESS");
1927 return 1;
1928}
1929
1930static int
1931nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1932{
1933 unsigned int i;
1934 char *new_mask = canonicalize_hostmask(strdup(mask));
1935 for (i=0; i<hi->masks->used; i++) {
1936 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1937 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1938 free(new_mask);
1939 return 0;
1940 }
1941 }
1942 string_list_append(hi->masks, new_mask);
1943 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
1944 return 1;
1945}
1946
1947static NICKSERV_FUNC(cmd_addmask)
1948{
1949 if (argc < 2) {
1950 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1951 int res = nickserv_addmask(user, user->handle_info, mask);
1952 free(mask);
1953 return res;
1954 } else {
1955 if (!is_gline(argv[1])) {
1956 reply("NSMSG_MASK_INVALID", argv[1]);
1957 return 0;
1958 }
1959 return nickserv_addmask(user, user->handle_info, argv[1]);
1960 }
1961}
1962
1963static NICKSERV_FUNC(cmd_oaddmask)
1964{
1965 struct handle_info *hi;
1966
1967 NICKSERV_MIN_PARMS(3);
1968 if (!(hi = get_victim_oper(user, argv[1])))
1969 return 0;
1970 return nickserv_addmask(user, hi, argv[2]);
1971}
1972
1973static int
1974nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
1975{
1976 unsigned int i;
1977 for (i=0; i<hi->masks->used; i++) {
1978 if (!strcmp(del_mask, hi->masks->list[i])) {
1979 char *old_mask = hi->masks->list[i];
1980 if (hi->masks->used == 1) {
1981 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
1982 return 0;
1983 }
1984 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
1985 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
1986 free(old_mask);
1987 return 1;
1988 }
1989 }
1990 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
1991 return 0;
1992}
1993
1994static NICKSERV_FUNC(cmd_delmask)
1995{
1996 NICKSERV_MIN_PARMS(2);
1997 return nickserv_delmask(user, user->handle_info, argv[1]);
1998}
1999
2000static NICKSERV_FUNC(cmd_odelmask)
2001{
2002 struct handle_info *hi;
2003 NICKSERV_MIN_PARMS(3);
2004 if (!(hi = get_victim_oper(user, argv[1])))
2005 return 0;
2006 return nickserv_delmask(user, hi, argv[2]);
2007}
2008
2009int
2010nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2011 unsigned int nn, add = 1, pos;
2012 unsigned long added, removed, flag;
2013
2014 for (added=removed=nn=0; str[nn]; nn++) {
2015 switch (str[nn]) {
2016 case '+': add = 1; break;
2017 case '-': add = 0; break;
2018 default:
2019 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2020 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2021 return 0;
2022 }
2023 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2024 /* cheesy avoidance of looking up the flag name.. */
2025 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2026 return 0;
2027 }
2028 flag = 1 << (pos - 1);
2029 if (add)
2030 added |= flag, removed &= ~flag;
2031 else
2032 removed |= flag, added &= ~flag;
2033 break;
2034 }
2035 }
2036 *padded = added;
2037 *premoved = removed;
2038 return 1;
2039}
2040
2041static int
2042nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2043{
2044 unsigned long before, after, added, removed;
2045 struct userNode *uNode;
2046
2047 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2048 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2049 return 0;
2050 hi->flags = (hi->flags | added) & ~removed;
2051 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2052
2053 /* Strip helping flag if they're only a support helper and not
2054 * currently in #support. */
2055 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2056 struct channelList *schannels;
2057 unsigned int ii;
2058 schannels = chanserv_support_channels();
2059 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2060 for (ii = 0; ii < schannels->used; ++ii)
2061 if (GetUserMode(schannels->list[ii], uNode))
2062 break;
2063 if (ii < schannels->used)
2064 break;
2065 }
2066 if (!uNode)
2067 HANDLE_CLEAR_FLAG(hi, HELPING);
2068 }
2069
2070 if (after && !before) {
2071 /* Add user to current helper list. */
2072 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2073 userList_append(&curr_helpers, uNode);
2074 } else if (!after && before) {
2075 /* Remove user from current helper list. */
2076 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2077 userList_remove(&curr_helpers, uNode);
2078 }
2079
2080 return 1;
2081}
2082
2083static void
2084set_list(struct userNode *user, struct handle_info *hi, int override)
2085{
2086 option_func_t *opt;
2087 unsigned int i;
2088 char *set_display[] = {
2089 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2090 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2091 };
2092
2093 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2094
2095 /* Do this so options are presented in a consistent order. */
2096 for (i = 0; i < ArrayLength(set_display); ++i)
2097 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2098 opt(user, hi, override, 0, NULL);
2099}
2100
2101static NICKSERV_FUNC(cmd_set)
2102{
2103 struct handle_info *hi;
2104 option_func_t *opt;
2105
2106 hi = user->handle_info;
2107 if (argc < 2) {
2108 set_list(user, hi, 0);
2109 return 1;
2110 }
2111 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2112 reply("NSMSG_INVALID_OPTION", argv[1]);
2113 return 0;
2114 }
2115 return opt(user, hi, 0, argc-1, argv+1);
2116}
2117
2118static NICKSERV_FUNC(cmd_oset)
2119{
2120 struct handle_info *hi;
2121 option_func_t *opt;
2122
2123 NICKSERV_MIN_PARMS(2);
2124
2125 if (!(hi = get_victim_oper(user, argv[1])))
2126 return 0;
2127
2128 if (argc < 3) {
2129 set_list(user, hi, 0);
2130 return 1;
2131 }
2132
2133 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2134 reply("NSMSG_INVALID_OPTION", argv[2]);
2135 return 0;
2136 }
2137
2138 return opt(user, hi, 1, argc-2, argv+2);
2139}
2140
2141static OPTION_FUNC(opt_info)
2142{
2143 const char *info;
2144 if (argc > 1) {
2145 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2146 free(hi->infoline);
2147 hi->infoline = NULL;
2148 } else {
2149 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2150 }
2151 }
2152
2153 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2154 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2155 return 1;
2156}
2157
2158static OPTION_FUNC(opt_width)
2159{
2160 if (argc > 1)
2161 hi->screen_width = strtoul(argv[1], NULL, 0);
2162
2163 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2164 hi->screen_width = MIN_LINE_SIZE;
2165 else if (hi->screen_width > MAX_LINE_SIZE)
2166 hi->screen_width = MAX_LINE_SIZE;
2167
2168 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2169 return 1;
2170}
2171
2172static OPTION_FUNC(opt_tablewidth)
2173{
2174 if (argc > 1)
2175 hi->table_width = strtoul(argv[1], NULL, 0);
2176
2177 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2178 hi->table_width = MIN_LINE_SIZE;
2179 else if (hi->screen_width > MAX_LINE_SIZE)
2180 hi->table_width = MAX_LINE_SIZE;
2181
2182 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2183 return 1;
2184}
2185
2186static OPTION_FUNC(opt_color)
2187{
2188 if (argc > 1) {
2189 if (enabled_string(argv[1]))
2190 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2191 else if (disabled_string(argv[1]))
2192 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2193 else {
2194 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2195 return 0;
2196 }
2197 }
2198
2199 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2200 return 1;
2201}
2202
2203static OPTION_FUNC(opt_privmsg)
2204{
2205 if (argc > 1) {
2206 if (enabled_string(argv[1]))
2207 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2208 else if (disabled_string(argv[1]))
2209 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2210 else {
2211 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2212 return 0;
2213 }
2214 }
2215
2216 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2217 return 1;
2218}
2219
2220static OPTION_FUNC(opt_style)
2221{
2222 char *style;
2223
2224 if (argc > 1) {
2225 if (!irccasecmp(argv[1], "Zoot"))
2226 hi->userlist_style = HI_STYLE_ZOOT;
2227 else if (!irccasecmp(argv[1], "def"))
2228 hi->userlist_style = HI_STYLE_DEF;
2229 }
2230
2231 switch (hi->userlist_style) {
2232 case HI_STYLE_DEF:
2233 style = "def";
2234 break;
2235 case HI_STYLE_ZOOT:
2236 default:
2237 style = "Zoot";
2238 }
2239
2240 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2241 return 1;
2242}
2243
2244static OPTION_FUNC(opt_announcements)
2245{
2246 const char *choice;
2247
2248 if (argc > 1) {
2249 if (enabled_string(argv[1]))
2250 hi->announcements = 'y';
2251 else if (disabled_string(argv[1]))
2252 hi->announcements = 'n';
2253 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2254 hi->announcements = '?';
2255 else {
2256 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2257 return 0;
2258 }
2259 }
2260
2261 switch (hi->announcements) {
2262 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2263 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2264 case '?': choice = "default"; break;
2265 default: choice = "unknown"; break;
2266 }
2267 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2268 return 1;
2269}
2270
2271static OPTION_FUNC(opt_password)
2272{
2273 if (!override) {
2274 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2275 return 0;
2276 }
2277
2278 if (argc > 1)
2279 cryptpass(argv[1], hi->passwd);
2280
2281 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2282 return 1;
2283}
2284
2285static OPTION_FUNC(opt_flags)
2286{
2287 char flags[33];
2288 unsigned int ii, flen;
2289
2290 if (!override) {
2291 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2292 return 0;
2293 }
2294
2295 if (argc > 1)
2296 nickserv_apply_flags(user, hi, argv[1]);
2297
2298 for (ii = flen = 0; handle_flags[ii]; ii++)
2299 if (hi->flags & (1 << ii))
2300 flags[flen++] = handle_flags[ii];
2301 flags[flen] = '\0';
2302 if (hi->flags)
2303 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2304 else
2305 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2306 return 1;
2307}
2308
2309static OPTION_FUNC(opt_email)
2310{
2311 if (argc > 1) {
2312 const char *str;
2313 if (!is_valid_email_addr(argv[1])) {
2314 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2315 return 0;
2316 }
2317 if ((str = sendmail_prohibited_address(argv[1]))) {
2318 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2319 return 0;
2320 }
2321 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2322 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2323 else if (!override)
2324 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2325 else {
2326 nickserv_set_email_addr(hi, argv[1]);
2327 if (hi->cookie)
2328 nickserv_eat_cookie(hi->cookie);
2329 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2330 }
2331 } else
2332 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2333 return 1;
2334}
2335
2336static OPTION_FUNC(opt_maxlogins)
2337{
2338 unsigned char maxlogins;
2339 if (argc > 1) {
2340 maxlogins = strtoul(argv[1], NULL, 0);
2341 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2342 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2343 return 0;
2344 }
2345 hi->maxlogins = maxlogins;
2346 }
2347 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2348 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2349 return 1;
2350}
2351
2352static OPTION_FUNC(opt_language)
2353{
2354 struct language *lang;
2355 if (argc > 1) {
2356 lang = language_find(argv[1]);
2357 if (irccasecmp(lang->name, argv[1]))
2358 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2359 hi->language = lang;
2360 }
2361 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2362 return 1;
2363}
2364
2365int
2366oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2367 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2368 return 0;
2369 if ((user->handle_info->opserv_level < target->opserv_level)
2370 || ((user->handle_info->opserv_level == target->opserv_level)
2371 && (user->handle_info->opserv_level < 1000))) {
2372 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2373 return 0;
2374 }
2375 if ((user->handle_info->opserv_level < new_level)
2376 || ((user->handle_info->opserv_level == new_level)
2377 && (user->handle_info->opserv_level < 1000))) {
2378 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2379 return 0;
2380 }
2381 if (user->handle_info == target) {
2382 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2383 return 0;
2384 }
2385 if (target->opserv_level == new_level)
2386 return 0;
2387 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2388 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2389 target->opserv_level = new_level;
2390 return 1;
2391}
2392
2393static OPTION_FUNC(opt_level)
2394{
2395 int res;
2396
2397 if (!override) {
2398 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2399 return 0;
2400 }
2401
2402 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2403 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2404 return res;
2405}
2406
2407static OPTION_FUNC(opt_epithet)
2408{
2409 if (!override) {
2410 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2411 return 0;
2412 }
2413
2414 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2415 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2416 if (hi->epithet)
2417 free(hi->epithet);
2418 if ((epithet[0] == '*') && !epithet[1])
2419 hi->epithet = NULL;
2420 else
2421 hi->epithet = strdup(epithet);
2422 }
2423
2424 if (hi->epithet)
2425 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2426 else
2427 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2428 return 1;
2429}
2430
2431static OPTION_FUNC(opt_title)
2432{
2433 const char *title;
2434
2435 if (!override) {
2436 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2437 return 0;
2438 }
2439
2440 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2441 title = argv[1];
2442 if (strchr(title, '.')) {
2443 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2444 return 0;
2445 }
2446 free(hi->fakehost);
2447 if (!strcmp(title, "*")) {
2448 hi->fakehost = NULL;
2449 } else {
2450 hi->fakehost = malloc(strlen(title)+2);
2451 hi->fakehost[0] = '.';
2452 strcpy(hi->fakehost+1, title);
2453 }
2454 apply_fakehost(hi);
2455 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2456 title = hi->fakehost + 1;
2457 else
2458 title = NULL;
2459 if (!title)
2460 title = user_find_message(user, "MSG_NONE");
2461 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2462 return 1;
2463}
2464
2465static OPTION_FUNC(opt_fakehost)
2466{
2467 const char *fake;
2468
2469 if (!override) {
2470 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2471 return 0;
2472 }
2473
2474 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2475 fake = argv[1];
2476 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2477 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID");
2478 return 0;
2479 }
2480 free(hi->fakehost);
2481 if (!strcmp(fake, "*"))
2482 hi->fakehost = NULL;
2483 else
2484 hi->fakehost = strdup(fake);
2485 fake = hi->fakehost;
2486 apply_fakehost(hi);
2487 } else
2488 fake = generate_fakehost(hi);
2489 if (!fake)
2490 fake = user_find_message(user, "MSG_NONE");
2491 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2492 return 1;
2493}
2494
2495static NICKSERV_FUNC(cmd_reclaim)
2496{
2497 struct handle_info *hi;
2498 struct nick_info *ni;
2499 struct userNode *victim;
2500
2501 NICKSERV_MIN_PARMS(2);
2502 hi = user->handle_info;
2503 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2504 if (!ni) {
2505 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2506 return 0;
2507 }
2508 if (ni->owner != user->handle_info) {
2509 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2510 return 0;
2511 }
2512 victim = GetUserH(ni->nick);
2513 if (!victim) {
2514 reply("MSG_NICK_UNKNOWN", ni->nick);
2515 return 0;
2516 }
2517 if (victim == user) {
2518 reply("NSMSG_NICK_USER_YOU");
2519 return 0;
2520 }
2521 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2522 switch (nickserv_conf.reclaim_action) {
2523 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2524 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2525 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2526 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2527 }
2528 return 1;
2529}
2530
2531static NICKSERV_FUNC(cmd_unregnick)
2532{
2533 const char *nick;
2534 struct handle_info *hi;
2535 struct nick_info *ni;
2536
2537 hi = user->handle_info;
2538 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2539 ni = dict_find(nickserv_nick_dict, nick, NULL);
2540 if (!ni) {
2541 reply("NSMSG_UNKNOWN_NICK", nick);
2542 return 0;
2543 }
2544 if (hi != ni->owner) {
2545 reply("NSMSG_NOT_YOUR_NICK", nick);
2546 return 0;
2547 }
2548 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2549 delete_nick(ni);
2550 return 1;
2551}
2552
2553static NICKSERV_FUNC(cmd_ounregnick)
2554{
2555 struct nick_info *ni;
2556
2557 NICKSERV_MIN_PARMS(2);
2558 if (!(ni = get_nick_info(argv[1]))) {
2559 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2560 return 0;
2561 }
2562 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2563 reply("MSG_USER_OUTRANKED", ni->nick);
2564 return 0;
2565 }
2566 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2567 delete_nick(ni);
2568 return 1;
2569}
2570
2571static NICKSERV_FUNC(cmd_unregister)
2572{
2573 struct handle_info *hi;
2574 char *passwd;
2575
2576 NICKSERV_MIN_PARMS(2);
2577 hi = user->handle_info;
2578 passwd = argv[1];
2579 argv[1] = "****";
2580 if (checkpass(passwd, hi->passwd)) {
2581 nickserv_unregister_handle(hi, user);
2582 return 1;
2583 } else {
2584 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2585 reply("NSMSG_PASSWORD_INVALID");
2586 return 0;
2587 }
2588}
2589
2590static NICKSERV_FUNC(cmd_ounregister)
2591{
2592 struct handle_info *hi;
2593
2594 NICKSERV_MIN_PARMS(2);
2595 if (!(hi = get_victim_oper(user, argv[1])))
2596 return 0;
2597 nickserv_unregister_handle(hi, user);
2598 return 0;
2599}
2600
2601static NICKSERV_FUNC(cmd_status)
2602{
2603 if (nickserv_conf.disable_nicks) {
2604 reply("NSMSG_GLOBAL_STATS_NONICK",
2605 dict_size(nickserv_handle_dict));
2606 } else {
2607 if (user->handle_info) {
2608 int cnt=0;
2609 struct nick_info *ni;
2610 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2611 reply("NSMSG_HANDLE_STATS", cnt);
2612 } else {
2613 reply("NSMSG_HANDLE_NONE");
2614 }
2615 reply("NSMSG_GLOBAL_STATS",
2616 dict_size(nickserv_handle_dict),
2617 dict_size(nickserv_nick_dict));
2618 }
2619 return 1;
2620}
2621
2622static NICKSERV_FUNC(cmd_ghost)
2623{
2624 struct userNode *target;
2625 char reason[MAXLEN];
2626
2627 NICKSERV_MIN_PARMS(2);
2628 if (!(target = GetUserH(argv[1]))) {
2629 reply("MSG_NICK_UNKNOWN", argv[1]);
2630 return 0;
2631 }
2632 if (target == user) {
2633 reply("NSMSG_CANNOT_GHOST_SELF");
2634 return 0;
2635 }
2636 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2637 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2638 return 0;
2639 }
2640 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2641 DelUser(target, nickserv, 1, reason);
2642 reply("NSMSG_GHOST_KILLED", argv[1]);
2643 return 1;
2644}
2645
2646static NICKSERV_FUNC(cmd_vacation)
2647{
2648 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2649 reply("NSMSG_ON_VACATION");
2650 return 1;
2651}
2652
2653static int
2654nickserv_saxdb_write(struct saxdb_context *ctx) {
2655 dict_iterator_t it;
2656 struct handle_info *hi;
2657 char flags[33];
2658
2659 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2660 hi = iter_data(it);
2661#ifdef WITH_PROTOCOL_BAHAMUT
2662 assert(hi->id);
2663#endif
2664 saxdb_start_record(ctx, iter_key(it), 0);
2665 if (hi->announcements != '?') {
2666 flags[0] = hi->announcements;
2667 flags[1] = 0;
2668 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2669 }
2670 if (hi->cookie) {
2671 struct handle_cookie *cookie = hi->cookie;
2672 char *type;
2673
2674 switch (cookie->type) {
2675 case ACTIVATION: type = KEY_ACTIVATION; break;
2676 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2677 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2678 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2679 default: type = NULL; break;
2680 }
2681 if (type) {
2682 saxdb_start_record(ctx, KEY_COOKIE, 0);
2683 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2684 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2685 if (cookie->data)
2686 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2687 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2688 saxdb_end_record(ctx);
2689 }
2690 }
2691 if (hi->email_addr)
2692 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2693 if (hi->epithet)
2694 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2695 if (hi->fakehost)
2696 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2697 if (hi->flags) {
2698 int ii, flen;
2699
2700 for (ii=flen=0; handle_flags[ii]; ++ii)
2701 if (hi->flags & (1 << ii))
2702 flags[flen++] = handle_flags[ii];
2703 flags[flen] = 0;
2704 saxdb_write_string(ctx, KEY_FLAGS, flags);
2705 }
2706#ifdef WITH_PROTOCOL_BAHAMUT
2707 saxdb_write_int(ctx, KEY_ID, hi->id);
2708#endif
2709 if (hi->infoline)
2710 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2711 if (hi->last_quit_host[0])
2712 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2713 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2714 if (hi->masks->used)
2715 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2716 if (hi->maxlogins)
2717 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2718 if (hi->nicks) {
2719 struct string_list *slist;
2720 struct nick_info *ni;
2721
2722 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2723 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2724 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2725 free(slist->list);
2726 free(slist);
2727 }
2728 if (hi->opserv_level)
2729 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2730 if (hi->language != lang_C)
2731 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2732 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2733 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2734 if (hi->screen_width)
2735 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2736 if (hi->table_width)
2737 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2738 flags[0] = hi->userlist_style;
2739 flags[1] = 0;
2740 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2741 saxdb_end_record(ctx);
2742 }
2743 return 0;
2744}
2745
2746static handle_merge_func_t *handle_merge_func_list;
2747static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2748
2749void
2750reg_handle_merge_func(handle_merge_func_t func)
2751{
2752 if (handle_merge_func_used == handle_merge_func_size) {
2753 if (handle_merge_func_size) {
2754 handle_merge_func_size <<= 1;
2755 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2756 } else {
2757 handle_merge_func_size = 8;
2758 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2759 }
2760 }
2761 handle_merge_func_list[handle_merge_func_used++] = func;
2762}
2763
2764static NICKSERV_FUNC(cmd_merge)
2765{
2766 struct handle_info *hi_from, *hi_to;
2767 struct userNode *last_user;
2768 struct userData *cList, *cListNext;
2769 unsigned int ii, jj, n;
2770 char buffer[MAXLEN];
2771
2772 NICKSERV_MIN_PARMS(3);
2773
2774 if (!(hi_from = get_victim_oper(user, argv[1])))
2775 return 0;
2776 if (!(hi_to = get_victim_oper(user, argv[2])))
2777 return 0;
2778 if (hi_to == hi_from) {
2779 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2780 return 0;
2781 }
2782
2783 for (n=0; n<handle_merge_func_used; n++)
2784 handle_merge_func_list[n](user, hi_to, hi_from);
2785
2786 /* Append "from" handle's nicks to "to" handle's nick list. */
2787 if (hi_to->nicks) {
2788 struct nick_info *last_ni;
2789 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2790 last_ni->next = hi_from->nicks;
2791 }
2792 while (hi_from->nicks) {
2793 hi_from->nicks->owner = hi_to;
2794 hi_from->nicks = hi_from->nicks->next;
2795 }
2796
2797 /* Merge the hostmasks. */
2798 for (ii=0; ii<hi_from->masks->used; ii++) {
2799 char *mask = hi_from->masks->list[ii];
2800 for (jj=0; jj<hi_to->masks->used; jj++)
2801 if (match_ircglobs(hi_to->masks->list[jj], mask))
2802 break;
2803 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2804 string_list_append(hi_to->masks, strdup(mask));
2805 }
2806
2807 /* Merge the lists of authed users. */
2808 if (hi_to->users) {
2809 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2810 last_user->next_authed = hi_from->users;
2811 } else {
2812 hi_to->users = hi_from->users;
2813 }
2814 /* Repoint the old "from" handle's users. */
2815 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2816 last_user->handle_info = hi_to;
2817 }
2818 hi_from->users = NULL;
2819
2820 /* Merge channel userlists. */
2821 for (cList=hi_from->channels; cList; cList=cListNext) {
2822 struct userData *cList2;
2823 cListNext = cList->u_next;
2824 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2825 if (cList->channel == cList2->channel)
2826 break;
2827 if (cList2 && (cList2->access >= cList->access)) {
2828 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);
2829 /* keep cList2 in hi_to; remove cList from hi_from */
2830 del_channel_user(cList, 1);
2831 } else {
2832 if (cList2) {
2833 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);
2834 /* remove the lower-ranking cList2 from hi_to */
2835 del_channel_user(cList2, 1);
2836 } else {
2837 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
2838 }
2839 /* cList needs to be moved from hi_from to hi_to */
2840 cList->handle = hi_to;
2841 /* Remove from linked list for hi_from */
2842 assert(!cList->u_prev);
2843 hi_from->channels = cList->u_next;
2844 if (cList->u_next)
2845 cList->u_next->u_prev = cList->u_prev;
2846 /* Add to linked list for hi_to */
2847 cList->u_prev = NULL;
2848 cList->u_next = hi_to->channels;
2849 if (hi_to->channels)
2850 hi_to->channels->u_prev = cList;
2851 hi_to->channels = cList;
2852 }
2853 }
2854
2855 /* Do they get an OpServ level promotion? */
2856 if (hi_from->opserv_level > hi_to->opserv_level)
2857 hi_to->opserv_level = hi_from->opserv_level;
2858
2859 /* What about last seen time? */
2860 if (hi_from->lastseen > hi_to->lastseen)
2861 hi_to->lastseen = hi_from->lastseen;
2862
2863 /* Notify of success. */
2864 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2865 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2866 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2867
2868 /* Unregister the "from" handle. */
2869 nickserv_unregister_handle(hi_from, NULL);
2870
2871 return 1;
2872}
2873
2874struct nickserv_discrim {
2875 unsigned int limit, min_level, max_level;
2876 unsigned long flags_on, flags_off;
2877 time_t min_registered, max_registered;
2878 time_t lastseen;
2879 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2880 const char *nickmask;
2881 const char *hostmask;
2882 const char *handlemask;
2883 const char *emailmask;
2884};
2885
2886typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2887
2888struct discrim_apply_info {
2889 struct nickserv_discrim *discrim;
2890 discrim_search_func func;
2891 struct userNode *source;
2892 unsigned int matched;
2893};
2894
2895static struct nickserv_discrim *
2896nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2897{
2898 unsigned int i;
2899 struct nickserv_discrim *discrim;
2900
2901 discrim = malloc(sizeof(*discrim));
2902 memset(discrim, 0, sizeof(*discrim));
2903 discrim->min_level = 0;
2904 discrim->max_level = ~0;
2905 discrim->limit = 50;
2906 discrim->min_registered = 0;
2907 discrim->max_registered = INT_MAX;
2908 discrim->lastseen = now;
2909
2910 for (i=0; i<argc; i++) {
2911 if (i == argc - 1) {
2912 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2913 goto fail;
2914 }
2915 if (!irccasecmp(argv[i], "limit")) {
2916 discrim->limit = strtoul(argv[++i], NULL, 0);
2917 } else if (!irccasecmp(argv[i], "flags")) {
2918 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2919 } else if (!irccasecmp(argv[i], "registered")) {
2920 const char *cmp = argv[++i];
2921 if (cmp[0] == '<') {
2922 if (cmp[1] == '=') {
2923 discrim->min_registered = now - ParseInterval(cmp+2);
2924 } else {
2925 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2926 }
2927 } else if (cmp[0] == '=') {
2928 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2929 } else if (cmp[0] == '>') {
2930 if (cmp[1] == '=') {
2931 discrim->max_registered = now - ParseInterval(cmp+2);
2932 } else {
2933 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2934 }
2935 } else {
2936 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2937 }
2938 } else if (!irccasecmp(argv[i], "seen")) {
2939 discrim->lastseen = now - ParseInterval(argv[++i]);
2940 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2941 discrim->nickmask = argv[++i];
2942 } else if (!irccasecmp(argv[i], "hostmask")) {
2943 i++;
2944 if (!irccasecmp(argv[i], "exact")) {
2945 if (i == argc - 1) {
2946 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2947 goto fail;
2948 }
2949 discrim->hostmask_type = EXACT;
2950 } else if (!irccasecmp(argv[i], "subset")) {
2951 if (i == argc - 1) {
2952 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2953 goto fail;
2954 }
2955 discrim->hostmask_type = SUBSET;
2956 } else if (!irccasecmp(argv[i], "superset")) {
2957 if (i == argc - 1) {
2958 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2959 goto fail;
2960 }
2961 discrim->hostmask_type = SUPERSET;
2962 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2963 if (i == argc - 1) {
2964 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2965 goto fail;
2966 }
2967 discrim->hostmask_type = LASTQUIT;
2968 } else {
2969 i--;
2970 discrim->hostmask_type = SUPERSET;
2971 }
2972 discrim->hostmask = argv[++i];
2973 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2974 if (!irccasecmp(argv[++i], "*")) {
2975 discrim->handlemask = 0;
2976 } else {
2977 discrim->handlemask = argv[i];
2978 }
2979 } else if (!irccasecmp(argv[i], "email")) {
2980 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2981 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2982 goto fail;
2983 } else if (!irccasecmp(argv[++i], "*")) {
2984 discrim->emailmask = 0;
2985 } else {
2986 discrim->emailmask = argv[i];
2987 }
2988 } else if (!irccasecmp(argv[i], "access")) {
2989 const char *cmp = argv[++i];
2990 if (cmp[0] == '<') {
2991 if (discrim->min_level == 0) discrim->min_level = 1;
2992 if (cmp[1] == '=') {
2993 discrim->max_level = strtoul(cmp+2, NULL, 0);
2994 } else {
2995 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
2996 }
2997 } else if (cmp[0] == '=') {
2998 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
2999 } else if (cmp[0] == '>') {
3000 if (cmp[1] == '=') {
3001 discrim->min_level = strtoul(cmp+2, NULL, 0);
3002 } else {
3003 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3004 }
3005 } else {
3006 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3007 }
3008 } else {
3009 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3010 goto fail;
3011 }
3012 }
3013 return discrim;
3014 fail:
3015 free(discrim);
3016 return NULL;
3017}
3018
3019static int
3020nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3021{
3022 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3023 || (discrim->flags_off & hi->flags)
3024 || (discrim->min_registered > hi->registered)
3025 || (discrim->max_registered < hi->registered)
3026 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3027 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3028 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3029 || (discrim->min_level > hi->opserv_level)
3030 || (discrim->max_level < hi->opserv_level)) {
3031 return 0;
3032 }
3033 if (discrim->hostmask) {
3034 unsigned int i;
3035 for (i=0; i<hi->masks->used; i++) {
3036 const char *mask = hi->masks->list[i];
3037 if ((discrim->hostmask_type == SUBSET)
3038 && (match_ircglobs(discrim->hostmask, mask))) break;
3039 else if ((discrim->hostmask_type == EXACT)
3040 && !irccasecmp(discrim->hostmask, mask)) break;
3041 else if ((discrim->hostmask_type == SUPERSET)
3042 && (match_ircglobs(mask, discrim->hostmask))) break;
3043 else if ((discrim->hostmask_type == LASTQUIT)
3044 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3045 }
3046 if (i==hi->masks->used) return 0;
3047 }
3048 if (discrim->nickmask) {
3049 struct nick_info *nick = hi->nicks;
3050 while (nick) {
3051 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3052 nick = nick->next;
3053 }
3054 if (!nick) return 0;
3055 }
3056 return 1;
3057}
3058
3059static unsigned int
3060nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3061{
3062 dict_iterator_t it, next;
3063 unsigned int matched;
3064
3065 for (it = dict_first(nickserv_handle_dict), matched = 0;
3066 it && (matched < discrim->limit);
3067 it = next) {
3068 next = iter_next(it);
3069 if (nickserv_discrim_match(discrim, iter_data(it))) {
3070 dsf(source, iter_data(it));
3071 matched++;
3072 }
3073 }
3074 return matched;
3075}
3076
3077static void
3078search_print_func(struct userNode *source, struct handle_info *match)
3079{
3080 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3081}
3082
3083static void
3084search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3085{
3086}
3087
3088static void
3089search_unregister_func (struct userNode *source, struct handle_info *match)
3090{
3091 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3092 nickserv_unregister_handle(match, source);
3093}
3094
3095static int
3096nickserv_sort_accounts_by_access(const void *a, const void *b)
3097{
3098 const struct handle_info *hi_a = *(const struct handle_info**)a;
3099 const struct handle_info *hi_b = *(const struct handle_info**)b;
3100 if (hi_a->opserv_level != hi_b->opserv_level)
3101 return hi_b->opserv_level - hi_a->opserv_level;
3102 return irccasecmp(hi_a->handle, hi_b->handle);
3103}
3104
3105void
3106nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3107{
3108 struct handle_info_list hil;
3109 struct helpfile_table tbl;
3110 unsigned int ii;
3111 dict_iterator_t it;
3112 const char **ary;
3113
3114 memset(&hil, 0, sizeof(hil));
3115 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3116 struct handle_info *hi = iter_data(it);
3117 if (hi->opserv_level)
3118 handle_info_list_append(&hil, hi);
3119 }
3120 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3121 tbl.length = hil.used + 1;
3122 tbl.width = 2;
3123 tbl.flags = TABLE_NO_FREE;
3124 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3125 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3126 ary[0] = "Account";
3127 ary[1] = "Level";
3128 for (ii = 0; ii < hil.used; ) {
3129 ary = malloc(tbl.width * sizeof(ary[0]));
3130 ary[0] = hil.list[ii]->handle;
3131 ary[1] = strtab(hil.list[ii]->opserv_level);
3132 tbl.contents[++ii] = ary;
3133 }
3134 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3135 reply("MSG_MATCH_COUNT", hil.used);
3136 for (ii = 0; ii < hil.used; ii++)
3137 free(tbl.contents[ii]);
3138 free(tbl.contents);
3139 free(hil.list);
3140}
3141
3142static NICKSERV_FUNC(cmd_search)
3143{
3144 struct nickserv_discrim *discrim;
3145 discrim_search_func action;
3146 struct svccmd *subcmd;
3147 unsigned int matches;
3148 char buf[MAXLEN];
3149
3150 NICKSERV_MIN_PARMS(3);
3151 sprintf(buf, "search %s", argv[1]);
3152 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3153 if (!irccasecmp(argv[1], "print"))
3154 action = search_print_func;
3155 else if (!irccasecmp(argv[1], "count"))
3156 action = search_count_func;
3157 else if (!irccasecmp(argv[1], "unregister"))
3158 action = search_unregister_func;
3159 else {
3160 reply("NSMSG_INVALID_ACTION", argv[1]);
3161 return 0;
3162 }
3163
3164 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3165 return 0;
3166
3167 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3168 if (!discrim)
3169 return 0;
3170
3171 if (action == search_print_func)
3172 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3173 else if (action == search_count_func)
3174 discrim->limit = INT_MAX;
3175
3176 matches = nickserv_discrim_search(discrim, action, user);
3177
3178 if (matches)
3179 reply("MSG_MATCH_COUNT", matches);
3180 else
3181 reply("MSG_NO_MATCHES");
3182
3183 free(discrim);
3184 return 0;
3185}
3186
3187static MODCMD_FUNC(cmd_checkpass)
3188{
3189 struct handle_info *hi;
3190
3191 NICKSERV_MIN_PARMS(3);
3192 if (!(hi = get_handle_info(argv[1]))) {
3193 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3194 return 0;
3195 }
3196 if (checkpass(argv[2], hi->passwd))
3197 reply("CHECKPASS_YES");
3198 else
3199 reply("CHECKPASS_NO");
3200 argv[2] = "****";
3201 return 1;
3202}
3203
3204static void
3205nickserv_db_read_handle(const char *handle, dict_t obj)
3206{
3207 const char *str;
3208 struct string_list *masks, *slist;
3209 struct handle_info *hi;
3210 struct userNode *authed_users;
3211 unsigned long int id;
3212 unsigned int ii;
3213 dict_t subdb;
3214
3215 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3216 id = str ? strtoul(str, NULL, 0) : 0;
3217 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3218 if (!str) {
3219 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3220 return;
3221 }
3222 if ((hi = get_handle_info(handle))) {
3223 authed_users = hi->users;
3224 hi->users = NULL;
3225 dict_remove(nickserv_handle_dict, hi->handle);
3226 } else {
3227 authed_users = NULL;
3228 }
3229 hi = register_handle(handle, str, id);
3230 if (authed_users) {
3231 hi->users = authed_users;
3232 while (authed_users) {
3233 authed_users->handle_info = hi;
3234 authed_users = authed_users->next_authed;
3235 }
3236 }
3237 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3238 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3239 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3240 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3241 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3242 hi->language = language_find(str ? str : "C");
3243 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3244 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3245 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3246 if (str)
3247 hi->infoline = strdup(str);
3248 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3249 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3250 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3251 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3252 /* We want to read the nicks even if disable_nicks is set. This is so
3253 * that we don't lose the nick data entirely. */
3254 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3255 if (slist) {
3256 for (ii=0; ii<slist->used; ii++)
3257 register_nick(slist->list[ii], hi);
3258 }
3259 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3260 if (str) {
3261 for (ii=0; str[ii]; ii++)
3262 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3263 }
3264 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3265 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3266 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3267 hi->announcements = str ? str[0] : '?';
3268 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3269 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3270 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3271 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3272 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3273 if (!str)
3274 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3275 if (str)
3276 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3277 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3278 if (str)
3279 nickserv_set_email_addr(hi, str);
3280 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3281 if (str)
3282 hi->epithet = strdup(str);
3283 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3284 if (str)
3285 hi->fakehost = strdup(str);
3286 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3287 if (subdb) {
3288 const char *data, *type, *expires, *cookie_str;
3289 struct handle_cookie *cookie;
3290
3291 cookie = calloc(1, sizeof(*cookie));
3292 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3293 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3294 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3295 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3296 if (!type || !expires || !cookie_str) {
3297 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3298 goto cookie_out;
3299 }
3300 if (!irccasecmp(type, KEY_ACTIVATION))
3301 cookie->type = ACTIVATION;
3302 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3303 cookie->type = PASSWORD_CHANGE;
3304 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3305 cookie->type = EMAIL_CHANGE;
3306 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3307 cookie->type = ALLOWAUTH;
3308 else {
3309 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3310 goto cookie_out;
3311 }
3312 cookie->expires = strtoul(expires, NULL, 0);
3313 if (cookie->expires < now)
3314 goto cookie_out;
3315 if (data)
3316 cookie->data = strdup(data);
3317 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3318 cookie->hi = hi;
3319 cookie_out:
3320 if (cookie->hi)
3321 nickserv_bake_cookie(cookie);
3322 else
3323 nickserv_free_cookie(cookie);
3324 }
3325}
3326
3327static int
3328nickserv_saxdb_read(dict_t db) {
3329 dict_iterator_t it;
3330 struct record_data *rd;
3331
3332 for (it=dict_first(db); it; it=iter_next(it)) {
3333 rd = iter_data(it);
3334 nickserv_db_read_handle(iter_key(it), rd->d.object);
3335 }
3336 return 0;
3337}
3338
3339static NICKSERV_FUNC(cmd_mergedb)
3340{
3341 struct timeval start, stop;
3342 dict_t db;
3343
3344 NICKSERV_MIN_PARMS(2);
3345 gettimeofday(&start, NULL);
3346 if (!(db = parse_database(argv[1]))) {
3347 reply("NSMSG_DB_UNREADABLE", argv[1]);
3348 return 0;
3349 }
3350 nickserv_saxdb_read(db);
3351 free_database(db);
3352 gettimeofday(&stop, NULL);
3353 stop.tv_sec -= start.tv_sec;
3354 stop.tv_usec -= start.tv_usec;
3355 if (stop.tv_usec < 0) {
3356 stop.tv_sec -= 1;
3357 stop.tv_usec += 1000000;
3358 }
3359 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3360 return 1;
3361}
3362
3363static void
3364expire_handles(UNUSED_ARG(void *data))
3365{
3366 dict_iterator_t it, next;
3367 time_t expiry;
3368 struct handle_info *hi;
3369
3370 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3371 next = iter_next(it);
3372 hi = iter_data(it);
3373 if ((hi->opserv_level > 0)
3374 || hi->users
3375 || HANDLE_FLAGGED(hi, FROZEN)
3376 || HANDLE_FLAGGED(hi, NODELETE)) {
3377 continue;
3378 }
3379 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3380 if ((now - hi->lastseen) > expiry) {
3381 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3382 nickserv_unregister_handle(hi, NULL);
3383 }
3384 }
3385
3386 if (nickserv_conf.handle_expire_frequency)
3387 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3388}
3389
3390static void
3391nickserv_load_dict(const char *fname)
3392{
3393 FILE *file;
3394 char line[128];
3395 if (!(file = fopen(fname, "r"))) {
3396 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3397 return;
3398 }
3399 while (!feof(file)) {
3400 fgets(line, sizeof(line), file);
3401 if (!line[0])
3402 continue;
3403 if (line[strlen(line)-1] == '\n')
3404 line[strlen(line)-1] = 0;
3405 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3406 }
3407 fclose(file);
3408 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3409}
3410
3411static enum reclaim_action
3412reclaim_action_from_string(const char *str) {
3413 if (!str)
3414 return RECLAIM_NONE;
3415 else if (!irccasecmp(str, "warn"))
3416 return RECLAIM_WARN;
3417 else if (!irccasecmp(str, "svsnick"))
3418 return RECLAIM_SVSNICK;
3419 else if (!irccasecmp(str, "kill"))
3420 return RECLAIM_KILL;
3421 else
3422 return RECLAIM_NONE;
3423}
3424
3425static void
3426nickserv_conf_read(void)
3427{
3428 dict_t conf_node, child;
3429 const char *str;
3430 dict_iterator_t it;
3431
3432 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3433 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3434 return;
3435 }
3436 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3437 if (!str)
3438 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3439 if (nickserv_conf.valid_handle_regex_set)
3440 regfree(&nickserv_conf.valid_handle_regex);
3441 if (str) {
3442 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3443 nickserv_conf.valid_handle_regex_set = !err;
3444 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3445 } else {
3446 nickserv_conf.valid_handle_regex_set = 0;
3447 }
3448 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3449 if (nickserv_conf.valid_nick_regex_set)
3450 regfree(&nickserv_conf.valid_nick_regex);
3451 if (str) {
3452 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3453 nickserv_conf.valid_nick_regex_set = !err;
3454 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3455 } else {
3456 nickserv_conf.valid_nick_regex_set = 0;
3457 }
3458 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3459 if (!str)
3460 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3461 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3462 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3463 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3464 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3465 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3466 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3467 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3468 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3469 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3470 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3471 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3472 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3473 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3474 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3475 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3476 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3477 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3478 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3479 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3480 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3481 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3482 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3483 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3484 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3485 if (!str)
3486 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3487 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3488 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3489 if (!str)
3490 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3491 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3492 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3493 if (!str)
3494 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3495 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3496 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3497 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3498 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3499 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3500 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3501 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3502 if (!nickserv_conf.disable_nicks) {
3503 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3504 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3505 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3506 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3507 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3508 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3509 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3510 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3511 }
3512 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3513 for (it=dict_first(child); it; it=iter_next(it)) {
3514 const char *key = iter_key(it), *value;
3515 unsigned char flag;
3516 int pos;
3517
3518 if (!strncasecmp(key, "uc_", 3))
3519 flag = toupper(key[3]);
3520 else if (!strncasecmp(key, "lc_", 3))
3521 flag = tolower(key[3]);
3522 else
3523 flag = key[0];
3524
3525 if ((pos = handle_inverse_flags[flag])) {
3526 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3527 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3528 }
3529 }
3530 if (nickserv_conf.weak_password_dict)
3531 dict_delete(nickserv_conf.weak_password_dict);
3532 nickserv_conf.weak_password_dict = dict_new();
3533 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3534 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3535 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3536 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3537 if (str)
3538 nickserv_load_dict(str);
3539 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3540 if (nickserv && str)
3541 NickChange(nickserv, str, 0);
3542 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3543 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3544 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3545 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3546 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3547 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3548 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3549 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3550 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3551 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3552 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3553 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3554 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3555 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3556 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3557 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3558 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3559 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3560 str = conf_get_data("server/network", RECDB_QSTRING);
3561 nickserv_conf.network_name = str ? str : "some IRC network";
3562 if (!nickserv_conf.auth_policer_params) {
3563 nickserv_conf.auth_policer_params = policer_params_new();
3564 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3565 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3566 }
3567 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3568 for (it=dict_first(child); it; it=iter_next(it))
3569 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3570}
3571
3572static void
3573nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3574 const char *msg;
3575 char newnick[NICKLEN+1];
3576
3577 assert(user);
3578 assert(ni);
3579 switch (action) {
3580 case RECLAIM_NONE:
3581 /* do nothing */
3582 break;
3583 case RECLAIM_WARN:
3584 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3585 break;
3586 case RECLAIM_SVSNICK:
3587 do {
3588 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3589 } while (GetUserH(newnick));
3590 irc_svsnick(nickserv, user, newnick);
3591 break;
3592 case RECLAIM_KILL:
3593 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3594 irc_kill(nickserv, user, msg);
3595 break;
3596 }
3597}
3598
3599static void
3600nickserv_reclaim_p(void *data) {
3601 struct userNode *user = data;
3602 struct nick_info *ni = get_nick_info(user->nick);
3603 if (ni)
3604 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3605}
3606
3607static int
3608check_user_nick(struct userNode *user) {
3609 struct nick_info *ni;
3610 user->modes &= ~FLAGS_REGNICK;
3611 if (!(ni = get_nick_info(user->nick)))
3612 return 0;
3613 if (user->handle_info == ni->owner) {
3614 user->modes |= FLAGS_REGNICK;
3615 irc_regnick(user);
3616 return 0;
3617 }
3618 if (nickserv_conf.warn_nick_owned)
3619 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3620 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3621 return 0;
3622 if (nickserv_conf.auto_reclaim_delay)
3623 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3624 else
3625 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3626 return 0;
3627}
3628
3629int
3630handle_new_user(struct userNode *user)
3631{
3632 return check_user_nick(user);
3633}
3634
3635void
3636handle_account(struct userNode *user, const char *stamp)
3637{
3638 struct handle_info *hi;
3639
3640#ifdef WITH_PROTOCOL_P10
3641 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3642#else
3643 hi = dict_find(nickserv_id_dict, stamp, NULL);
3644#endif
3645
3646 if (hi) {
3647 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3648 return;
3649 }
3650 set_user_handle_info(user, hi, 0);
3651 } else {
3652 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3653 }
3654}
3655
3656void
3657handle_nick_change(struct userNode *user, const char *old_nick)
3658{
3659 struct handle_info *hi;
3660
3661 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3662 dict_remove(nickserv_allow_auth_dict, old_nick);
3663 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3664 }
3665 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3666 check_user_nick(user);
3667}
3668
3669void
3670nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3671{
3672 dict_remove(nickserv_allow_auth_dict, user->nick);
3673 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3674 set_user_handle_info(user, NULL, 0);
3675}
3676
3677static struct modcmd *
3678nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3679{
3680 if (min_level > 0) {
3681 char buf[16];
3682 sprintf(buf, "%u", min_level);
3683 if (must_be_qualified) {
3684 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3685 } else {
3686 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3687 }
3688 } else if (min_level == 0) {
3689 if (must_be_qualified) {
3690 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3691 } else {
3692 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3693 }
3694 } else {
3695 if (must_be_qualified) {
3696 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3697 } else {
3698 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3699 }
3700 }
3701}
3702
3703static void
3704nickserv_db_cleanup(void)
3705{
3706 unreg_del_user_func(nickserv_remove_user);
3707 userList_clean(&curr_helpers);
3708 policer_params_delete(nickserv_conf.auth_policer_params);
3709 dict_delete(nickserv_handle_dict);
3710 dict_delete(nickserv_nick_dict);
3711 dict_delete(nickserv_opt_dict);
3712 dict_delete(nickserv_allow_auth_dict);
3713 dict_delete(nickserv_email_dict);
3714 dict_delete(nickserv_id_dict);
3715 dict_delete(nickserv_conf.weak_password_dict);
3716 free(auth_func_list);
3717 free(unreg_func_list);
3718 free(rf_list);
3719 free(allowauth_func_list);
3720 free(handle_merge_func_list);
3721 free(failpw_func_list);
3722 if (nickserv_conf.valid_handle_regex_set)
3723 regfree(&nickserv_conf.valid_handle_regex);
3724 if (nickserv_conf.valid_nick_regex_set)
3725 regfree(&nickserv_conf.valid_nick_regex);
3726}
3727
3728void
3729init_nickserv(const char *nick)
3730{
3731 unsigned int i;
3732 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3733 reg_new_user_func(handle_new_user);
3734 reg_nick_change_func(handle_nick_change);
3735 reg_del_user_func(nickserv_remove_user);
3736 reg_account_func(handle_account);
3737
3738 /* set up handle_inverse_flags */
3739 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3740 for (i=0; handle_flags[i]; i++) {
3741 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3742 flag_access_levels[i] = 0;
3743 }
3744
3745 conf_register_reload(nickserv_conf_read);
3746 nickserv_opt_dict = dict_new();
3747 nickserv_email_dict = dict_new();
3748 dict_set_free_keys(nickserv_email_dict, free);
3749 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3750
3751 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4048352e 3752/* Removed qualified_host as default requirement for AUTH, REGISTER, PASS, etc. nets
3753 * can enable it per command using modcmd. (its a shitty default IMO, and now in 1.3
3754 * a big pain to disable since its nolonger in the config file. ) -Rubin
3755 */
3756 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+loghostmask", NULL);
d76ed9a9 3757 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4048352e 3758 nickserv_define_func("REGISTER", cmd_register, -1, 0, 0);
d76ed9a9 3759 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4048352e 3760 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 0);
d76ed9a9 3761 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3762 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3763 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3764 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3765 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4048352e 3766 nickserv_define_func("PASS", cmd_pass, -1, 1, 0);
d76ed9a9 3767 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3768 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3769 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3770 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3771 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3772 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3773 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3774 if (!nickserv_conf.disable_nicks) {
3775 /* nick management commands */
3776 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3777 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3778 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3779 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3780 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3781 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3782 }
3783 if (nickserv_conf.email_enabled) {
3784 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4048352e 3785 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 0);
3786 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 0);
d76ed9a9 3787 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3788 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3789 }
3790 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3791 /* miscellaneous commands */
3792 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3793 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3794 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3795 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3796 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3797 /* other options */
3798 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3799 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3800 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3801 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3802 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3803 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3804 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3805 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3806 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3807 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3808 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3809 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3810 if (nickserv_conf.titlehost_suffix) {
3811 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
3812 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
3813 }
3814 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3815 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3816 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3817
3818 nickserv_handle_dict = dict_new();
3819 dict_set_free_keys(nickserv_handle_dict, free);
3820 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3821
3822 nickserv_id_dict = dict_new();
3823 dict_set_free_keys(nickserv_id_dict, free);
3824
3825 nickserv_nick_dict = dict_new();
3826 dict_set_free_data(nickserv_nick_dict, free_nick_info);
3827
3828 nickserv_allow_auth_dict = dict_new();
3829
3830 userList_init(&curr_helpers);
3831
3832 if (nick) {
3833 nickserv = AddService(nick, "Nick Services", NULL);
3834 nickserv_service = service_register(nickserv);
3835 }
3836 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3837 reg_exit_func(nickserv_db_cleanup);
3838 if(nickserv_conf.handle_expire_frequency)
3839 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3840 message_register_table(msgtab);
3841}