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