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