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