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