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