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