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