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