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