]> jfr.im git - irc/evilnet/x3.git/blob - src/nickserv.c
helpfile improvements
[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 process_adduser_pending(user);
1324
1325 return 1;
1326 }
1327
1328 static NICKSERV_FUNC(cmd_oregister)
1329 {
1330 char *mask;
1331 struct userNode *settee;
1332 struct handle_info *hi;
1333
1334 NICKSERV_MIN_PARMS(4);
1335
1336 if (!is_valid_handle(argv[1])) {
1337 reply("NSMSG_BAD_HANDLE", argv[1]);
1338 return 0;
1339 }
1340
1341 if (strchr(argv[3], '@')) {
1342 mask = canonicalize_hostmask(strdup(argv[3]));
1343 if (argc > 4) {
1344 settee = GetUserH(argv[4]);
1345 if (!settee) {
1346 reply("MSG_NICK_UNKNOWN", argv[4]);
1347 free(mask);
1348 return 0;
1349 }
1350 } else {
1351 settee = NULL;
1352 }
1353 } else if ((settee = GetUserH(argv[3]))) {
1354 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1355 } else {
1356 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1357 return 0;
1358 }
1359 if (settee && settee->handle_info) {
1360 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1361 free(mask);
1362 return 0;
1363 }
1364 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1365 free(mask);
1366 return 0;
1367 }
1368 string_list_append(hi->masks, mask);
1369 return 1;
1370 }
1371
1372 static NICKSERV_FUNC(cmd_handleinfo)
1373 {
1374 char buff[400];
1375 unsigned int i, pos=0, herelen;
1376 struct userNode *target, *next_un;
1377 struct handle_info *hi;
1378 const char *nsmsg_none;
1379
1380 if (argc < 2) {
1381 if (!(hi = user->handle_info)) {
1382 reply("NSMSG_MUST_AUTH");
1383 return 0;
1384 }
1385 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1386 return 0;
1387 }
1388
1389 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1390 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1391 reply("MSG_BAR");
1392 #ifdef WITH_PROTOCOL_BAHAMUT
1393 reply("NSMSG_HANDLEINFO_ID", hi->id);
1394 #endif
1395 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1396
1397 if (!hi->users) {
1398 intervalString(buff, now - hi->lastseen, user->handle_info);
1399 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1400 } else {
1401 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1402 }
1403
1404 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1405 if (HANDLE_FLAGGED(hi, FROZEN))
1406 reply("NSMSG_HANDLEINFO_VACATION");
1407
1408 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1409 struct do_not_register *dnr;
1410 if ((dnr = chanserv_is_dnr(NULL, hi)))
1411 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1412 if (!oper_outranks(user, hi))
1413 return 1;
1414 } else if (hi != user->handle_info) {
1415 reply("NSMSG_HANDLEINFO_END");
1416 return 1;
1417 }
1418
1419 if (nickserv_conf.email_enabled)
1420 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1421
1422 if (hi->cookie) {
1423 const char *type;
1424 switch (hi->cookie->type) {
1425 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1426 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1427 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1428 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1429 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1430 }
1431 reply(type);
1432 }
1433
1434 if (hi->flags) {
1435 unsigned long flen = 1;
1436 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1437 flags[0] = '+';
1438 for (i=0, flen=1; handle_flags[i]; i++)
1439 if (hi->flags & 1 << i)
1440 flags[flen++] = handle_flags[i];
1441 flags[flen] = 0;
1442 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1443 } else {
1444 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1445 }
1446
1447 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1448 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1449 || (hi->opserv_level > 0)) {
1450 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1451 }
1452
1453 if (hi->fakehost)
1454 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1455
1456 if (hi->last_quit_host[0])
1457 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1458 else
1459 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1460
1461 if (nickserv_conf.disable_nicks) {
1462 /* nicks disabled; don't show anything about registered nicks */
1463 } else if (hi->nicks) {
1464 struct nick_info *ni, *next_ni;
1465 for (ni = hi->nicks; ni; ni = next_ni) {
1466 herelen = strlen(ni->nick);
1467 if (pos + herelen + 1 > ArrayLength(buff)) {
1468 next_ni = ni;
1469 goto print_nicks_buff;
1470 } else {
1471 next_ni = ni->next;
1472 }
1473 memcpy(buff+pos, ni->nick, herelen);
1474 pos += herelen; buff[pos++] = ' ';
1475 if (!next_ni) {
1476 print_nicks_buff:
1477 buff[pos-1] = 0;
1478 reply("NSMSG_HANDLEINFO_NICKS", buff);
1479 pos = 0;
1480 }
1481 }
1482 } else {
1483 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1484 }
1485
1486 if (hi->masks->used) {
1487 for (i=0; i < hi->masks->used; i++) {
1488 herelen = strlen(hi->masks->list[i]);
1489 if (pos + herelen + 1 > ArrayLength(buff)) {
1490 i--;
1491 goto print_mask_buff;
1492 }
1493 memcpy(buff+pos, hi->masks->list[i], herelen);
1494 pos += herelen; buff[pos++] = ' ';
1495 if (i+1 == hi->masks->used) {
1496 print_mask_buff:
1497 buff[pos-1] = 0;
1498 reply("NSMSG_HANDLEINFO_MASKS", buff);
1499 pos = 0;
1500 }
1501 }
1502 } else {
1503 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1504 }
1505
1506 if (hi->channels) {
1507 struct userData *channel, *next;
1508 char *name;
1509
1510 for (channel = hi->channels; channel; channel = next) {
1511 next = channel->u_next;
1512 name = channel->channel->channel->name;
1513 herelen = strlen(name);
1514 if (pos + herelen + 7 > ArrayLength(buff)) {
1515 next = channel;
1516 goto print_chans_buff;
1517 }
1518 if (IsUserSuspended(channel))
1519 buff[pos++] = '-';
1520 pos += sprintf(buff+pos, "%s:%s ", user_level_name_from_level(channel->access), name);
1521 if (next == NULL) {
1522 print_chans_buff:
1523 buff[pos-1] = 0;
1524 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1525 pos = 0;
1526 }
1527 }
1528 } else {
1529 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1530 }
1531
1532 for (target = hi->users; target; target = next_un) {
1533 herelen = strlen(target->nick);
1534 if (pos + herelen + 1 > ArrayLength(buff)) {
1535 next_un = target;
1536 goto print_cnick_buff;
1537 } else {
1538 next_un = target->next_authed;
1539 }
1540 memcpy(buff+pos, target->nick, herelen);
1541 pos += herelen; buff[pos++] = ' ';
1542 if (!next_un) {
1543 print_cnick_buff:
1544 buff[pos-1] = 0;
1545 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1546 pos = 0;
1547 }
1548 }
1549
1550 reply("NSMSG_HANDLEINFO_END");
1551 return 1;
1552 }
1553
1554 static NICKSERV_FUNC(cmd_userinfo)
1555 {
1556 struct userNode *target;
1557
1558 NICKSERV_MIN_PARMS(2);
1559 if (!(target = GetUserH(argv[1]))) {
1560 reply("MSG_NICK_UNKNOWN", argv[1]);
1561 return 0;
1562 }
1563 if (target->handle_info)
1564 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1565 else
1566 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1567 return 1;
1568 }
1569
1570 static NICKSERV_FUNC(cmd_nickinfo)
1571 {
1572 struct nick_info *ni;
1573
1574 NICKSERV_MIN_PARMS(2);
1575 if (!(ni = get_nick_info(argv[1]))) {
1576 reply("MSG_NICK_UNKNOWN", argv[1]);
1577 return 0;
1578 }
1579 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1580 return 1;
1581 }
1582
1583 static NICKSERV_FUNC(cmd_rename_handle)
1584 {
1585 struct handle_info *hi;
1586 char msgbuf[MAXLEN], *old_handle;
1587 unsigned int nn;
1588
1589 NICKSERV_MIN_PARMS(3);
1590 if (!(hi = get_victim_oper(user, argv[1])))
1591 return 0;
1592 if (!is_valid_handle(argv[2])) {
1593 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1594 return 0;
1595 }
1596 if (get_handle_info(argv[2])) {
1597 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1598 return 0;
1599 }
1600 if(strlen(argv[2]) > 15)
1601 {
1602 reply("NMSG_HANDLE_TOLONG", argv[2], 15);
1603 return 0;
1604 }
1605
1606 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1607 hi->handle = strdup(argv[2]);
1608 dict_insert(nickserv_handle_dict, hi->handle, hi);
1609 for (nn=0; nn<rf_list_used; nn++)
1610 rf_list[nn](hi, old_handle);
1611 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1612 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1613 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1614 free(old_handle);
1615 return 1;
1616 }
1617
1618 static failpw_func_t *failpw_func_list;
1619 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1620
1621 void
1622 reg_failpw_func(failpw_func_t func)
1623 {
1624 if (failpw_func_used == failpw_func_size) {
1625 if (failpw_func_size) {
1626 failpw_func_size <<= 1;
1627 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1628 } else {
1629 failpw_func_size = 8;
1630 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1631 }
1632 }
1633 failpw_func_list[failpw_func_used++] = func;
1634 }
1635
1636 /*
1637 * Return hi if the handle/pass pair matches, NULL if it doesnt.
1638 *
1639 * called by nefariouses enhanced AC login-on-connect code
1640 *
1641 */
1642 struct handle_info *loc_auth(char *handle, char *password)
1643 {
1644 int pw_arg, used, maxlogins;
1645 unsigned int ii;
1646 int wildmask = 0;
1647 struct handle_info *hi;
1648 struct userNode *other;
1649
1650 hi = dict_find(nickserv_handle_dict, handle, NULL);
1651 pw_arg = 2;
1652 if (!hi) {
1653 return NULL;
1654 }
1655
1656 /* We don't know the users hostname, or anything because they
1657 * havn't registered yet. So we can only allow LOC if your
1658 * account has *@* as a hostmask.
1659 */
1660 for (ii=0; ii<hi->masks->used; ii++)
1661 {
1662 if (!strcmp(hi->masks->list[ii], "*@*"))
1663 {
1664 wildmask++;
1665 break;
1666 }
1667 }
1668 if(wildmask < 1)
1669 return NULL;
1670
1671 /* Responses from here on look up the language used by the handle they asked about. */
1672 if (!checkpass(password, hi->passwd)) {
1673 return NULL;
1674 }
1675 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1676 return NULL;
1677 }
1678 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1679 for (used = 0, other = hi->users; other; other = other->next_authed) {
1680 if (++used >= maxlogins) {
1681 return NULL;
1682 }
1683 }
1684 return hi;
1685 }
1686
1687 static NICKSERV_FUNC(cmd_auth)
1688 {
1689 int pw_arg, used, maxlogins;
1690 struct handle_info *hi;
1691 const char *passwd;
1692 struct userNode *other;
1693
1694 if (user->handle_info) {
1695 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1696 return 0;
1697 }
1698 if (IsStamped(user)) {
1699 /* Unauthenticated users might still have been stamped
1700 previously and could therefore have a hidden host;
1701 do not allow them to authenticate. */
1702 reply("NSMSG_STAMPED_AUTH");
1703 return 0;
1704 }
1705 if (argc == 3) {
1706 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1707 pw_arg = 2;
1708 } else if (argc == 2) {
1709 if (nickserv_conf.disable_nicks) {
1710 if (!(hi = get_handle_info(user->nick))) {
1711 reply("NSMSG_HANDLE_NOT_FOUND");
1712 return 0;
1713 }
1714 } else {
1715 /* try to look up their handle from their nick */
1716 struct nick_info *ni;
1717 ni = get_nick_info(user->nick);
1718 if (!ni) {
1719 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1720 return 0;
1721 }
1722 hi = ni->owner;
1723 }
1724 pw_arg = 1;
1725 } else {
1726 reply("MSG_MISSING_PARAMS", argv[0]);
1727 svccmd_send_help(user, nickserv, cmd);
1728 return 0;
1729 }
1730 if (!hi) {
1731 reply("NSMSG_HANDLE_NOT_FOUND");
1732 return 0;
1733 }
1734 /* Responses from here on look up the language used by the handle they asked about. */
1735 passwd = argv[pw_arg];
1736 if (!valid_user_for(user, hi)) {
1737 if (hi->email_addr && nickserv_conf.email_enabled)
1738 send_message_type(4, user, cmd->parent->bot,
1739 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1740 hi->handle);
1741 else
1742 send_message_type(4, user, cmd->parent->bot,
1743 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1744 hi->handle);
1745 argv[pw_arg] = "BADMASK";
1746 return 1;
1747 }
1748 if (!checkpass(passwd, hi->passwd)) {
1749 unsigned int n;
1750 send_message_type(4, user, cmd->parent->bot,
1751 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1752 argv[pw_arg] = "BADPASS";
1753 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1754 if (nickserv_conf.autogag_enabled) {
1755 if (!user->auth_policer.params) {
1756 user->auth_policer.last_req = now;
1757 user->auth_policer.params = nickserv_conf.auth_policer_params;
1758 }
1759 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1760 char *hostmask;
1761 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1762 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1763 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1764 free(hostmask);
1765 argv[pw_arg] = "GAGGED";
1766 }
1767 }
1768 return 1;
1769 }
1770 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1771 send_message_type(4, user, cmd->parent->bot,
1772 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1773 argv[pw_arg] = "SUSPENDED";
1774 return 1;
1775 }
1776 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1777 for (used = 0, other = hi->users; other; other = other->next_authed) {
1778 if (++used >= maxlogins) {
1779 send_message_type(4, user, cmd->parent->bot,
1780 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1781 maxlogins);
1782 argv[pw_arg] = "MAXLOGINS";
1783 return 1;
1784 }
1785 }
1786
1787 set_user_handle_info(user, hi, 1);
1788 if (nickserv_conf.email_required && !hi->email_addr)
1789 reply("NSMSG_PLEASE_SET_EMAIL");
1790 if (!is_secure_password(hi->handle, passwd, NULL))
1791 reply("NSMSG_WEAK_PASSWORD");
1792 if (hi->passwd[0] != '$')
1793 cryptpass(passwd, hi->passwd);
1794 reply("NSMSG_AUTH_SUCCESS");
1795
1796 process_adduser_pending(user);
1797 argv[pw_arg] = "****";
1798 return 1;
1799 }
1800
1801 static allowauth_func_t *allowauth_func_list;
1802 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1803
1804 void
1805 reg_allowauth_func(allowauth_func_t func)
1806 {
1807 if (allowauth_func_used == allowauth_func_size) {
1808 if (allowauth_func_size) {
1809 allowauth_func_size <<= 1;
1810 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1811 } else {
1812 allowauth_func_size = 8;
1813 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1814 }
1815 }
1816 allowauth_func_list[allowauth_func_used++] = func;
1817 }
1818
1819 static NICKSERV_FUNC(cmd_allowauth)
1820 {
1821 struct userNode *target;
1822 struct handle_info *hi;
1823 unsigned int n;
1824
1825 NICKSERV_MIN_PARMS(2);
1826 if (!(target = GetUserH(argv[1]))) {
1827 reply("MSG_NICK_UNKNOWN", argv[1]);
1828 return 0;
1829 }
1830 if (target->handle_info) {
1831 reply("NSMSG_USER_PREV_AUTH", target->nick);
1832 return 0;
1833 }
1834 if (IsStamped(target)) {
1835 /* Unauthenticated users might still have been stamped
1836 previously and could therefore have a hidden host;
1837 do not allow them to authenticate to an account. */
1838 reply("NSMSG_USER_PREV_STAMP", target->nick);
1839 return 0;
1840 }
1841 if (argc == 2)
1842 hi = NULL;
1843 else if (!(hi = get_handle_info(argv[2]))) {
1844 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1845 return 0;
1846 }
1847 if (hi) {
1848 if (hi->opserv_level > user->handle_info->opserv_level) {
1849 reply("MSG_USER_OUTRANKED", hi->handle);
1850 return 0;
1851 }
1852 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1853 || (hi->opserv_level > 0))
1854 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1855 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1856 return 0;
1857 }
1858 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1859 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1860 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1861 if (nickserv_conf.email_enabled)
1862 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1863 } else {
1864 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1865 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1866 else
1867 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1868 }
1869 for (n=0; n<allowauth_func_used; n++)
1870 allowauth_func_list[n](user, target, hi);
1871 return 1;
1872 }
1873
1874 static NICKSERV_FUNC(cmd_authcookie)
1875 {
1876 struct handle_info *hi;
1877
1878 NICKSERV_MIN_PARMS(2);
1879 if (user->handle_info) {
1880 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1881 return 0;
1882 }
1883 if (IsStamped(user)) {
1884 /* Unauthenticated users might still have been stamped
1885 previously and could therefore have a hidden host;
1886 do not allow them to authenticate to an account. */
1887 reply("NSMSG_STAMPED_AUTHCOOKIE");
1888 return 0;
1889 }
1890 if (!(hi = get_handle_info(argv[1]))) {
1891 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1892 return 0;
1893 }
1894 if (!hi->email_addr) {
1895 reply("MSG_SET_EMAIL_ADDR");
1896 return 0;
1897 }
1898 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL, 0);
1899 return 1;
1900 }
1901
1902 static NICKSERV_FUNC(cmd_delcookie)
1903 {
1904 struct handle_info *hi;
1905
1906 hi = user->handle_info;
1907 if (!hi->cookie) {
1908 reply("NSMSG_NO_COOKIE");
1909 return 0;
1910 }
1911 switch (hi->cookie->type) {
1912 case ACTIVATION:
1913 case EMAIL_CHANGE:
1914 reply("NSMSG_MUST_TIME_OUT");
1915 break;
1916 default:
1917 nickserv_eat_cookie(hi->cookie);
1918 reply("NSMSG_ATE_COOKIE");
1919 break;
1920 }
1921 return 1;
1922 }
1923
1924 static NICKSERV_FUNC(cmd_odelcookie)
1925 {
1926 struct handle_info *hi;
1927
1928 NICKSERV_MIN_PARMS(2);
1929
1930 if (!(hi = get_victim_oper(user, argv[1])))
1931 return 0;
1932
1933 if (!hi->cookie) {
1934 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
1935 return 0;
1936 }
1937
1938 nickserv_eat_cookie(hi->cookie);
1939 reply("NSMSG_ATE_FOREIGN_COOKIE", hi->handle);
1940
1941 return 1;
1942 }
1943
1944 static NICKSERV_FUNC(cmd_resetpass)
1945 {
1946 struct handle_info *hi;
1947 char crypted[MD5_CRYPT_LENGTH];
1948 int weblink;
1949
1950 NICKSERV_MIN_PARMS(3);
1951 if(argc >= 4 && !strcmp(argv[3], "WEBLINK"))
1952 weblink = 1;
1953 else
1954 weblink = 0;
1955 if (user->handle_info) {
1956 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1957 return 0;
1958 }
1959 if (IsStamped(user)) {
1960 /* Unauthenticated users might still have been stamped
1961 previously and could therefore have a hidden host;
1962 do not allow them to activate an account. */
1963 reply("NSMSG_STAMPED_RESETPASS");
1964 return 0;
1965 }
1966 if (!(hi = get_handle_info(argv[1]))) {
1967 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1968 return 0;
1969 }
1970 if (!hi->email_addr) {
1971 reply("MSG_SET_EMAIL_ADDR");
1972 return 0;
1973 }
1974 cryptpass(argv[2], crypted);
1975 argv[2] = "****";
1976 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted, weblink);
1977 return 1;
1978 }
1979
1980 static NICKSERV_FUNC(cmd_cookie)
1981 {
1982 struct handle_info *hi;
1983 const char *cookie;
1984
1985 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1986 cookie = argv[1];
1987 } else {
1988 NICKSERV_MIN_PARMS(3);
1989 if (!(hi = get_handle_info(argv[1]))) {
1990 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1991 return 0;
1992 }
1993 cookie = argv[2];
1994 }
1995
1996 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1997 reply("NSMSG_HANDLE_SUSPENDED");
1998 return 0;
1999 }
2000
2001 if (!hi->cookie) {
2002 reply("NSMSG_NO_COOKIE");
2003 return 0;
2004 }
2005
2006 /* Check validity of operation before comparing cookie to
2007 * prohibit guessing by authed users. */
2008 if (user->handle_info
2009 && (hi->cookie->type != EMAIL_CHANGE)
2010 && (hi->cookie->type != PASSWORD_CHANGE)) {
2011 reply("NSMSG_CANNOT_COOKIE");
2012 return 0;
2013 }
2014
2015 if (strcmp(cookie, hi->cookie->cookie)) {
2016 reply("NSMSG_BAD_COOKIE");
2017 return 0;
2018 }
2019
2020 switch (hi->cookie->type) {
2021 case ACTIVATION:
2022 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2023 set_user_handle_info(user, hi, 1);
2024 reply("NSMSG_HANDLE_ACTIVATED");
2025 if (nickserv_conf.sync_log)
2026 SyncLog("ACCOUNTACC %s", hi->handle);
2027 break;
2028 case PASSWORD_CHANGE:
2029 set_user_handle_info(user, hi, 1);
2030 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2031 reply("NSMSG_PASSWORD_CHANGED");
2032 if (nickserv_conf.sync_log)
2033 SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
2034 break;
2035 case EMAIL_CHANGE:
2036 if (!hi->email_addr && nickserv_conf.sync_log) {
2037 /*
2038 * This should only happen if an OREGISTER was sent. Require
2039 * email must be enabled! - SiRVulcaN
2040 */
2041 SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, hi->cookie->data, user->info);
2042 }
2043 nickserv_set_email_addr(hi, hi->cookie->data);
2044 reply("NSMSG_EMAIL_CHANGED");
2045 if (nickserv_conf.sync_log)
2046 SyncLog("EMAILCHANGE %s %s", hi->handle, hi->cookie->data);
2047 break;
2048 case ALLOWAUTH:
2049 set_user_handle_info(user, hi, 1);
2050 reply("NSMSG_AUTH_SUCCESS");
2051 break;
2052 default:
2053 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2054 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2055 break;
2056 }
2057
2058 nickserv_eat_cookie(hi->cookie);
2059
2060 return 1;
2061 }
2062
2063 static NICKSERV_FUNC(cmd_oregnick) {
2064 const char *nick;
2065 struct handle_info *target;
2066 struct nick_info *ni;
2067
2068 NICKSERV_MIN_PARMS(3);
2069 if (!(target = modcmd_get_handle_info(user, argv[1])))
2070 return 0;
2071 nick = argv[2];
2072 if (!is_registerable_nick(nick)) {
2073 reply("NSMSG_BAD_NICK", nick);
2074 return 0;
2075 }
2076 ni = dict_find(nickserv_nick_dict, nick, NULL);
2077 if (ni) {
2078 reply("NSMSG_NICK_EXISTS", nick);
2079 return 0;
2080 }
2081 register_nick(nick, target);
2082 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2083 return 1;
2084 }
2085
2086 static NICKSERV_FUNC(cmd_regnick) {
2087 unsigned n;
2088 struct nick_info *ni;
2089
2090 if (!is_registerable_nick(user->nick)) {
2091 reply("NSMSG_BAD_NICK", user->nick);
2092 return 0;
2093 }
2094 /* count their nicks, see if it's too many */
2095 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2096 if (n >= nickserv_conf.nicks_per_handle) {
2097 reply("NSMSG_TOO_MANY_NICKS");
2098 return 0;
2099 }
2100 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2101 if (ni) {
2102 reply("NSMSG_NICK_EXISTS", user->nick);
2103 return 0;
2104 }
2105 register_nick(user->nick, user->handle_info);
2106 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2107 return 1;
2108 }
2109
2110 static NICKSERV_FUNC(cmd_pass)
2111 {
2112 struct handle_info *hi;
2113 const char *old_pass, *new_pass;
2114
2115 NICKSERV_MIN_PARMS(3);
2116 hi = user->handle_info;
2117 old_pass = argv[1];
2118 new_pass = argv[2];
2119 argv[2] = "****";
2120 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2121 if (!checkpass(old_pass, hi->passwd)) {
2122 argv[1] = "BADPASS";
2123 reply("NSMSG_PASSWORD_INVALID");
2124 return 0;
2125 }
2126 cryptpass(new_pass, hi->passwd);
2127 if (nickserv_conf.sync_log)
2128 SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd);
2129 argv[1] = "****";
2130 reply("NSMSG_PASS_SUCCESS");
2131 return 1;
2132 }
2133
2134 static int
2135 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2136 {
2137 unsigned int i;
2138 char *new_mask = canonicalize_hostmask(strdup(mask));
2139 for (i=0; i<hi->masks->used; i++) {
2140 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2141 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2142 free(new_mask);
2143 return 0;
2144 }
2145 }
2146 string_list_append(hi->masks, new_mask);
2147 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2148 return 1;
2149 }
2150
2151 static NICKSERV_FUNC(cmd_addmask)
2152 {
2153 if (argc < 2) {
2154 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2155 int res = nickserv_addmask(user, user->handle_info, mask);
2156 free(mask);
2157 return res;
2158 } else {
2159 if (!is_gline(argv[1])) {
2160 reply("NSMSG_MASK_INVALID", argv[1]);
2161 return 0;
2162 }
2163 return nickserv_addmask(user, user->handle_info, argv[1]);
2164 }
2165 }
2166
2167 static NICKSERV_FUNC(cmd_oaddmask)
2168 {
2169 struct handle_info *hi;
2170
2171 NICKSERV_MIN_PARMS(3);
2172 if (!(hi = get_victim_oper(user, argv[1])))
2173 return 0;
2174 return nickserv_addmask(user, hi, argv[2]);
2175 }
2176
2177 static int
2178 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
2179 {
2180 unsigned int i;
2181 for (i=0; i<hi->masks->used; i++) {
2182 if (!strcmp(del_mask, hi->masks->list[i])) {
2183 char *old_mask = hi->masks->list[i];
2184 if (hi->masks->used == 1) {
2185 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2186 return 0;
2187 }
2188 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2189 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2190 free(old_mask);
2191 return 1;
2192 }
2193 }
2194 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2195 return 0;
2196 }
2197
2198 static NICKSERV_FUNC(cmd_delmask)
2199 {
2200 NICKSERV_MIN_PARMS(2);
2201 return nickserv_delmask(user, user->handle_info, argv[1]);
2202 }
2203
2204 static NICKSERV_FUNC(cmd_odelmask)
2205 {
2206 struct handle_info *hi;
2207 NICKSERV_MIN_PARMS(3);
2208 if (!(hi = get_victim_oper(user, argv[1])))
2209 return 0;
2210 return nickserv_delmask(user, hi, argv[2]);
2211 }
2212
2213 int
2214 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2215 unsigned int nn, add = 1, pos;
2216 unsigned long added, removed, flag;
2217
2218 for (added=removed=nn=0; str[nn]; nn++) {
2219 switch (str[nn]) {
2220 case '+': add = 1; break;
2221 case '-': add = 0; break;
2222 default:
2223 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2224 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2225 return 0;
2226 }
2227 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2228 /* cheesy avoidance of looking up the flag name.. */
2229 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2230 return 0;
2231 }
2232 flag = 1 << (pos - 1);
2233 if (add)
2234 added |= flag, removed &= ~flag;
2235 else
2236 removed |= flag, added &= ~flag;
2237 break;
2238 }
2239 }
2240 *padded = added;
2241 *premoved = removed;
2242 return 1;
2243 }
2244
2245 static int
2246 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2247 {
2248 unsigned long before, after, added, removed;
2249 struct userNode *uNode;
2250
2251 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2252 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2253 return 0;
2254 hi->flags = (hi->flags | added) & ~removed;
2255 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2256
2257 /* Strip helping flag if they're only a support helper and not
2258 * currently in #support. */
2259 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2260 struct channelList *schannels;
2261 unsigned int ii;
2262 schannels = chanserv_support_channels();
2263 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2264 for (ii = 0; ii < schannels->used; ++ii)
2265 if (GetUserMode(schannels->list[ii], uNode))
2266 break;
2267 if (ii < schannels->used)
2268 break;
2269 }
2270 if (!uNode)
2271 HANDLE_CLEAR_FLAG(hi, HELPING);
2272 }
2273
2274 if (after && !before) {
2275 /* Add user to current helper list. */
2276 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2277 userList_append(&curr_helpers, uNode);
2278 } else if (!after && before) {
2279 /* Remove user from current helper list. */
2280 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2281 userList_remove(&curr_helpers, uNode);
2282 }
2283
2284 return 1;
2285 }
2286
2287 static void
2288 set_list(struct userNode *user, struct handle_info *hi, int override)
2289 {
2290 option_func_t *opt;
2291 unsigned int i;
2292 char *set_display[] = {
2293 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", /* "STYLE", */
2294 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE",
2295 "FAKEHOST", "TITLE", "EPITHET"
2296 };
2297
2298 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2299 send_message(user, nickserv, "NSMSG_SETTING_LIST_HEADER");
2300
2301 /* Do this so options are presented in a consistent order. */
2302 for (i = 0; i < ArrayLength(set_display); ++i)
2303 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2304 opt(user, hi, override, 0, NULL);
2305 send_message(user, nickserv, "NSMSG_SETTING_LIST_END");
2306 }
2307
2308 static NICKSERV_FUNC(cmd_set)
2309 {
2310 struct handle_info *hi;
2311 option_func_t *opt;
2312
2313 hi = user->handle_info;
2314 if (argc < 2) {
2315 set_list(user, hi, 0);
2316 return 1;
2317 }
2318 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2319 reply("NSMSG_INVALID_OPTION", argv[1]);
2320 return 0;
2321 }
2322 return opt(user, hi, 0, argc-1, argv+1);
2323 }
2324
2325 static NICKSERV_FUNC(cmd_oset)
2326 {
2327 struct handle_info *hi;
2328 option_func_t *opt;
2329
2330 NICKSERV_MIN_PARMS(2);
2331
2332 if (!(hi = get_victim_oper(user, argv[1])))
2333 return 0;
2334
2335 if (argc < 3) {
2336 set_list(user, hi, 0);
2337 return 1;
2338 }
2339
2340 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2341 reply("NSMSG_INVALID_OPTION", argv[2]);
2342 return 0;
2343 }
2344
2345 return opt(user, hi, 1, argc-2, argv+2);
2346 }
2347
2348 static OPTION_FUNC(opt_info)
2349 {
2350 const char *info;
2351 if (argc > 1) {
2352 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2353 free(hi->infoline);
2354 hi->infoline = NULL;
2355 } else {
2356 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2357 }
2358 }
2359
2360 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2361 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2362 return 1;
2363 }
2364
2365 static OPTION_FUNC(opt_width)
2366 {
2367 if (argc > 1)
2368 hi->screen_width = strtoul(argv[1], NULL, 0);
2369
2370 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2371 hi->screen_width = MIN_LINE_SIZE;
2372 else if (hi->screen_width > MAX_LINE_SIZE)
2373 hi->screen_width = MAX_LINE_SIZE;
2374
2375 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2376 return 1;
2377 }
2378
2379 static OPTION_FUNC(opt_tablewidth)
2380 {
2381 if (argc > 1)
2382 hi->table_width = strtoul(argv[1], NULL, 0);
2383
2384 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2385 hi->table_width = MIN_LINE_SIZE;
2386 else if (hi->screen_width > MAX_LINE_SIZE)
2387 hi->table_width = MAX_LINE_SIZE;
2388
2389 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2390 return 1;
2391 }
2392
2393 static OPTION_FUNC(opt_color)
2394 {
2395 if (argc > 1) {
2396 if (enabled_string(argv[1]))
2397 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2398 else if (disabled_string(argv[1]))
2399 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2400 else {
2401 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2402 return 0;
2403 }
2404 }
2405
2406 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2407 return 1;
2408 }
2409
2410 static OPTION_FUNC(opt_privmsg)
2411 {
2412 if (argc > 1) {
2413 if (enabled_string(argv[1]))
2414 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2415 else if (disabled_string(argv[1]))
2416 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2417 else {
2418 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2419 return 0;
2420 }
2421 }
2422
2423 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2424 return 1;
2425 }
2426
2427 /*
2428 static OPTION_FUNC(opt_style)
2429 {
2430 char *style;
2431
2432 if (argc > 1) {
2433 if (!irccasecmp(argv[1], "Zoot"))
2434 hi->userlist_style = HI_STYLE_ZOOT;
2435 else if (!irccasecmp(argv[1], "def"))
2436 hi->userlist_style = HI_STYLE_DEF;
2437 }
2438
2439 switch (hi->userlist_style) {
2440 case HI_STYLE_DEF:
2441 style = "def";
2442 break;
2443 case HI_STYLE_ZOOT:
2444 default:
2445 style = "Zoot";
2446 }
2447
2448 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2449 return 1;
2450 }
2451 */
2452
2453 static OPTION_FUNC(opt_announcements)
2454 {
2455 const char *choice;
2456
2457 if (argc > 1) {
2458 if (enabled_string(argv[1]))
2459 hi->announcements = 'y';
2460 else if (disabled_string(argv[1]))
2461 hi->announcements = 'n';
2462 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2463 hi->announcements = '?';
2464 else {
2465 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2466 return 0;
2467 }
2468 }
2469
2470 switch (hi->announcements) {
2471 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2472 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2473 case '?': choice = "default"; break;
2474 default: choice = "unknown"; break;
2475 }
2476 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2477 return 1;
2478 }
2479
2480 static OPTION_FUNC(opt_password)
2481 {
2482 if (!override) {
2483 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2484 return 0;
2485 }
2486
2487 if (argc > 1)
2488 cryptpass(argv[1], hi->passwd);
2489
2490 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2491 return 1;
2492 }
2493
2494 static OPTION_FUNC(opt_flags)
2495 {
2496 char flags[33];
2497 unsigned int ii, flen;
2498
2499 if (!override) {
2500 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2501 return 0;
2502 }
2503
2504 if (argc > 1)
2505 nickserv_apply_flags(user, hi, argv[1]);
2506
2507 for (ii = flen = 0; handle_flags[ii]; ii++)
2508 if (hi->flags & (1 << ii))
2509 flags[flen++] = handle_flags[ii];
2510 flags[flen] = '\0';
2511 if (hi->flags)
2512 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2513 else
2514 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2515 return 1;
2516 }
2517
2518 static OPTION_FUNC(opt_email)
2519 {
2520 if (argc > 1) {
2521 const char *str;
2522 if (!is_valid_email_addr(argv[1])) {
2523 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2524 return 0;
2525 }
2526 if ((str = sendmail_prohibited_address(argv[1]))) {
2527 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2528 return 0;
2529 }
2530 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2531 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2532 else if (!override)
2533 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1], 0);
2534 else {
2535 nickserv_set_email_addr(hi, argv[1]);
2536 if (hi->cookie)
2537 nickserv_eat_cookie(hi->cookie);
2538 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2539 }
2540 } else
2541 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2542 return 1;
2543 }
2544
2545 static OPTION_FUNC(opt_maxlogins)
2546 {
2547 unsigned char maxlogins;
2548 if (argc > 1) {
2549 maxlogins = strtoul(argv[1], NULL, 0);
2550 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2551 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2552 return 0;
2553 }
2554 hi->maxlogins = maxlogins;
2555 }
2556 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2557 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2558 return 1;
2559 }
2560
2561 static OPTION_FUNC(opt_language)
2562 {
2563 struct language *lang;
2564 if (argc > 1) {
2565 lang = language_find(argv[1]);
2566 if (irccasecmp(lang->name, argv[1]))
2567 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2568 hi->language = lang;
2569 }
2570 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2571 return 1;
2572 }
2573
2574 int
2575 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2576 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2577 return 0;
2578 if ((user->handle_info->opserv_level < target->opserv_level)
2579 || ((user->handle_info->opserv_level == target->opserv_level)
2580 && (user->handle_info->opserv_level < 1000))) {
2581 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2582 return 0;
2583 }
2584 if ((user->handle_info->opserv_level < new_level)
2585 || ((user->handle_info->opserv_level == new_level)
2586 && (user->handle_info->opserv_level < 1000))) {
2587 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2588 return 0;
2589 }
2590 if (user->handle_info == target) {
2591 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2592 return 0;
2593 }
2594 if (target->opserv_level == new_level)
2595 return 0;
2596 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2597 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2598 target->opserv_level = new_level;
2599 return 1;
2600 }
2601
2602 static OPTION_FUNC(opt_level)
2603 {
2604 int res;
2605
2606 if (!override) {
2607 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2608 return 0;
2609 }
2610
2611 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2612 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2613 return res;
2614 }
2615
2616 static OPTION_FUNC(opt_epithet)
2617 {
2618 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2619 char *epithet;
2620 if (!override) {
2621 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2622 return 0;
2623 }
2624
2625 epithet = unsplit_string(argv+1, argc-1, NULL);
2626
2627 if (hi->epithet)
2628 free(hi->epithet);
2629 if ((epithet[0] == '*') && !epithet[1])
2630 hi->epithet = NULL;
2631 else
2632 hi->epithet = strdup(epithet);
2633 }
2634
2635 if (hi->epithet)
2636 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2637 else
2638 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2639 return 1;
2640 }
2641
2642 static OPTION_FUNC(opt_title)
2643 {
2644 const char *title;
2645
2646 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2647 if (!override) {
2648 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2649 return 0;
2650 }
2651
2652 title = argv[1];
2653 if (strchr(title, '.')) {
2654 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2655 return 0;
2656 }
2657 if ((strlen(user->handle_info->handle) + strlen(title) +
2658 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2659 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2660 return 0;
2661 }
2662
2663 free(hi->fakehost);
2664 if (!strcmp(title, "*")) {
2665 hi->fakehost = NULL;
2666 } else {
2667 hi->fakehost = malloc(strlen(title)+2);
2668 hi->fakehost[0] = '.';
2669 strcpy(hi->fakehost+1, title);
2670 }
2671 apply_fakehost(hi);
2672 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2673 title = hi->fakehost + 1;
2674 else
2675 title = NULL;
2676 if (!title)
2677 title = user_find_message(user, "MSG_NONE");
2678 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2679 return 1;
2680 }
2681
2682 static OPTION_FUNC(opt_fakehost)
2683 {
2684 const char *fake;
2685
2686 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2687 if (!override) {
2688 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2689 return 0;
2690 }
2691
2692 fake = argv[1];
2693 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2694 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2695 return 0;
2696 }
2697 free(hi->fakehost);
2698 if (!strcmp(fake, "*"))
2699 hi->fakehost = NULL;
2700 else
2701 hi->fakehost = strdup(fake);
2702 fake = hi->fakehost;
2703 apply_fakehost(hi);
2704 } else
2705 fake = generate_fakehost(hi);
2706 if (!fake)
2707 fake = user_find_message(user, "MSG_NONE");
2708 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2709 return 1;
2710 }
2711
2712 static NICKSERV_FUNC(cmd_reclaim)
2713 {
2714 struct handle_info *hi;
2715 struct nick_info *ni;
2716 struct userNode *victim;
2717
2718 NICKSERV_MIN_PARMS(2);
2719 hi = user->handle_info;
2720 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2721 if (!ni) {
2722 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2723 return 0;
2724 }
2725 if (ni->owner != user->handle_info) {
2726 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2727 return 0;
2728 }
2729 victim = GetUserH(ni->nick);
2730 if (!victim) {
2731 reply("MSG_NICK_UNKNOWN", ni->nick);
2732 return 0;
2733 }
2734 if (victim == user) {
2735 reply("NSMSG_NICK_USER_YOU");
2736 return 0;
2737 }
2738 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2739 switch (nickserv_conf.reclaim_action) {
2740 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2741 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2742 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2743 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2744 }
2745 return 1;
2746 }
2747
2748 static NICKSERV_FUNC(cmd_unregnick)
2749 {
2750 const char *nick;
2751 struct handle_info *hi;
2752 struct nick_info *ni;
2753
2754 hi = user->handle_info;
2755 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2756 ni = dict_find(nickserv_nick_dict, nick, NULL);
2757 if (!ni) {
2758 reply("NSMSG_UNKNOWN_NICK", nick);
2759 return 0;
2760 }
2761 if (hi != ni->owner) {
2762 reply("NSMSG_NOT_YOUR_NICK", nick);
2763 return 0;
2764 }
2765 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2766 delete_nick(ni);
2767 return 1;
2768 }
2769
2770 static NICKSERV_FUNC(cmd_ounregnick)
2771 {
2772 struct nick_info *ni;
2773
2774 NICKSERV_MIN_PARMS(2);
2775 if (!(ni = get_nick_info(argv[1]))) {
2776 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2777 return 0;
2778 }
2779 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2780 reply("MSG_USER_OUTRANKED", ni->nick);
2781 return 0;
2782 }
2783 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2784 delete_nick(ni);
2785 return 1;
2786 }
2787
2788 static NICKSERV_FUNC(cmd_unregister)
2789 {
2790 struct handle_info *hi;
2791 char *passwd;
2792
2793 NICKSERV_MIN_PARMS(2);
2794 hi = user->handle_info;
2795 passwd = argv[1];
2796 argv[1] = "****";
2797 if (checkpass(passwd, hi->passwd)) {
2798 nickserv_unregister_handle(hi, user);
2799 return 1;
2800 } else {
2801 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2802 reply("NSMSG_PASSWORD_INVALID");
2803 return 0;
2804 }
2805 }
2806
2807 static NICKSERV_FUNC(cmd_ounregister)
2808 {
2809 struct handle_info *hi;
2810
2811 NICKSERV_MIN_PARMS(2);
2812 if (!(hi = get_victim_oper(user, argv[1])))
2813 return 0;
2814 nickserv_unregister_handle(hi, user);
2815 return 1;
2816 }
2817
2818 static NICKSERV_FUNC(cmd_status)
2819 {
2820 if (nickserv_conf.disable_nicks) {
2821 reply("NSMSG_GLOBAL_STATS_NONICK",
2822 dict_size(nickserv_handle_dict));
2823 } else {
2824 if (user->handle_info) {
2825 int cnt=0;
2826 struct nick_info *ni;
2827 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2828 reply("NSMSG_HANDLE_STATS", cnt);
2829 } else {
2830 reply("NSMSG_HANDLE_NONE");
2831 }
2832 reply("NSMSG_GLOBAL_STATS",
2833 dict_size(nickserv_handle_dict),
2834 dict_size(nickserv_nick_dict));
2835 }
2836 return 1;
2837 }
2838
2839 static NICKSERV_FUNC(cmd_ghost)
2840 {
2841 struct userNode *target;
2842 char reason[MAXLEN];
2843
2844 NICKSERV_MIN_PARMS(2);
2845 if (!(target = GetUserH(argv[1]))) {
2846 reply("MSG_NICK_UNKNOWN", argv[1]);
2847 return 0;
2848 }
2849 if (target == user) {
2850 reply("NSMSG_CANNOT_GHOST_SELF");
2851 return 0;
2852 }
2853 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2854 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2855 return 0;
2856 }
2857 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2858 DelUser(target, nickserv, 1, reason);
2859 reply("NSMSG_GHOST_KILLED", argv[1]);
2860 return 1;
2861 }
2862
2863 static NICKSERV_FUNC(cmd_vacation)
2864 {
2865 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2866 reply("NSMSG_ON_VACATION");
2867 return 1;
2868 }
2869
2870 static int
2871 nickserv_saxdb_write(struct saxdb_context *ctx) {
2872 dict_iterator_t it;
2873 struct handle_info *hi;
2874 char flags[33];
2875
2876 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2877 hi = iter_data(it);
2878 #ifdef WITH_PROTOCOL_BAHAMUT
2879 assert(hi->id);
2880 #endif
2881 saxdb_start_record(ctx, iter_key(it), 0);
2882 if (hi->announcements != '?') {
2883 flags[0] = hi->announcements;
2884 flags[1] = 0;
2885 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2886 }
2887 if (hi->cookie) {
2888 struct handle_cookie *cookie = hi->cookie;
2889 char *type;
2890
2891 switch (cookie->type) {
2892 case ACTIVATION: type = KEY_ACTIVATION; break;
2893 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2894 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2895 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2896 default: type = NULL; break;
2897 }
2898 if (type) {
2899 saxdb_start_record(ctx, KEY_COOKIE, 0);
2900 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2901 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2902 if (cookie->data)
2903 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2904 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2905 saxdb_end_record(ctx);
2906 }
2907 }
2908 if (hi->email_addr)
2909 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2910 if (hi->epithet)
2911 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2912 if (hi->fakehost)
2913 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2914 if (hi->flags) {
2915 int ii, flen;
2916
2917 for (ii=flen=0; handle_flags[ii]; ++ii)
2918 if (hi->flags & (1 << ii))
2919 flags[flen++] = handle_flags[ii];
2920 flags[flen] = 0;
2921 saxdb_write_string(ctx, KEY_FLAGS, flags);
2922 }
2923 #ifdef WITH_PROTOCOL_BAHAMUT
2924 saxdb_write_int(ctx, KEY_ID, hi->id);
2925 #endif
2926 if (hi->infoline)
2927 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2928 if (hi->last_quit_host[0])
2929 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2930 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2931 if (hi->masks->used)
2932 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2933 if (hi->maxlogins)
2934 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2935 if (hi->nicks) {
2936 struct string_list *slist;
2937 struct nick_info *ni;
2938
2939 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2940 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2941 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2942 free(slist->list);
2943 free(slist);
2944 }
2945 if (hi->opserv_level)
2946 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2947 if (hi->language != lang_C)
2948 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2949 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2950 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2951 if (hi->screen_width)
2952 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2953 if (hi->table_width)
2954 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2955 flags[0] = hi->userlist_style;
2956 flags[1] = 0;
2957 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2958 saxdb_end_record(ctx);
2959 }
2960 return 0;
2961 }
2962
2963 static handle_merge_func_t *handle_merge_func_list;
2964 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2965
2966 void
2967 reg_handle_merge_func(handle_merge_func_t func)
2968 {
2969 if (handle_merge_func_used == handle_merge_func_size) {
2970 if (handle_merge_func_size) {
2971 handle_merge_func_size <<= 1;
2972 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2973 } else {
2974 handle_merge_func_size = 8;
2975 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2976 }
2977 }
2978 handle_merge_func_list[handle_merge_func_used++] = func;
2979 }
2980
2981 static NICKSERV_FUNC(cmd_merge)
2982 {
2983 struct handle_info *hi_from, *hi_to;
2984 struct userNode *last_user;
2985 struct userData *cList, *cListNext;
2986 unsigned int ii, jj, n;
2987 char buffer[MAXLEN];
2988
2989 NICKSERV_MIN_PARMS(3);
2990
2991 if (!(hi_from = get_victim_oper(user, argv[1])))
2992 return 0;
2993 if (!(hi_to = get_victim_oper(user, argv[2])))
2994 return 0;
2995 if (hi_to == hi_from) {
2996 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2997 return 0;
2998 }
2999
3000 for (n=0; n<handle_merge_func_used; n++)
3001 handle_merge_func_list[n](user, hi_to, hi_from);
3002
3003 /* Append "from" handle's nicks to "to" handle's nick list. */
3004 if (hi_to->nicks) {
3005 struct nick_info *last_ni;
3006 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3007 last_ni->next = hi_from->nicks;
3008 }
3009 while (hi_from->nicks) {
3010 hi_from->nicks->owner = hi_to;
3011 hi_from->nicks = hi_from->nicks->next;
3012 }
3013
3014 /* Merge the hostmasks. */
3015 for (ii=0; ii<hi_from->masks->used; ii++) {
3016 char *mask = hi_from->masks->list[ii];
3017 for (jj=0; jj<hi_to->masks->used; jj++)
3018 if (match_ircglobs(hi_to->masks->list[jj], mask))
3019 break;
3020 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3021 string_list_append(hi_to->masks, strdup(mask));
3022 }
3023
3024 /* Merge the lists of authed users. */
3025 if (hi_to->users) {
3026 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3027 last_user->next_authed = hi_from->users;
3028 } else {
3029 hi_to->users = hi_from->users;
3030 }
3031 /* Repoint the old "from" handle's users. */
3032 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3033 last_user->handle_info = hi_to;
3034 }
3035 hi_from->users = NULL;
3036
3037 /* Merge channel userlists. */
3038 for (cList=hi_from->channels; cList; cList=cListNext) {
3039 struct userData *cList2;
3040 cListNext = cList->u_next;
3041 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3042 if (cList->channel == cList2->channel)
3043 break;
3044 if (cList2 && (cList2->access >= cList->access)) {
3045 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);
3046 /* keep cList2 in hi_to; remove cList from hi_from */
3047 del_channel_user(cList, 1);
3048 } else {
3049 if (cList2) {
3050 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);
3051 /* remove the lower-ranking cList2 from hi_to */
3052 del_channel_user(cList2, 1);
3053 } else {
3054 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3055 }
3056 /* cList needs to be moved from hi_from to hi_to */
3057 cList->handle = hi_to;
3058 /* Remove from linked list for hi_from */
3059 assert(!cList->u_prev);
3060 hi_from->channels = cList->u_next;
3061 if (cList->u_next)
3062 cList->u_next->u_prev = cList->u_prev;
3063 /* Add to linked list for hi_to */
3064 cList->u_prev = NULL;
3065 cList->u_next = hi_to->channels;
3066 if (hi_to->channels)
3067 hi_to->channels->u_prev = cList;
3068 hi_to->channels = cList;
3069 }
3070 }
3071
3072 /* Do they get an OpServ level promotion? */
3073 if (hi_from->opserv_level > hi_to->opserv_level)
3074 hi_to->opserv_level = hi_from->opserv_level;
3075
3076 /* What about last seen time? */
3077 if (hi_from->lastseen > hi_to->lastseen)
3078 hi_to->lastseen = hi_from->lastseen;
3079
3080 /* Does a fakehost carry over? (This intentionally doesn't set it
3081 * for users previously attached to hi_to. They'll just have to
3082 * reconnect.)
3083 */
3084 if (hi_from->fakehost && !hi_to->fakehost)
3085 hi_to->fakehost = strdup(hi_from->fakehost);
3086
3087 /* Notify of success. */
3088 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3089 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3090 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3091
3092 /* Unregister the "from" handle. */
3093 nickserv_unregister_handle(hi_from, NULL);
3094
3095 return 1;
3096 }
3097
3098 struct nickserv_discrim {
3099 unsigned int limit, min_level, max_level;
3100 unsigned long flags_on, flags_off;
3101 time_t min_registered, max_registered;
3102 time_t lastseen;
3103 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3104 const char *nickmask;
3105 const char *hostmask;
3106 const char *handlemask;
3107 const char *emailmask;
3108 };
3109
3110 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3111
3112 struct discrim_apply_info {
3113 struct nickserv_discrim *discrim;
3114 discrim_search_func func;
3115 struct userNode *source;
3116 unsigned int matched;
3117 };
3118
3119 static struct nickserv_discrim *
3120 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3121 {
3122 unsigned int i;
3123 struct nickserv_discrim *discrim;
3124
3125 discrim = malloc(sizeof(*discrim));
3126 memset(discrim, 0, sizeof(*discrim));
3127 discrim->min_level = 0;
3128 discrim->max_level = ~0;
3129 discrim->limit = 50;
3130 discrim->min_registered = 0;
3131 discrim->max_registered = INT_MAX;
3132 discrim->lastseen = now;
3133
3134 for (i=0; i<argc; i++) {
3135 if (i == argc - 1) {
3136 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3137 goto fail;
3138 }
3139 if (!irccasecmp(argv[i], "limit")) {
3140 discrim->limit = strtoul(argv[++i], NULL, 0);
3141 } else if (!irccasecmp(argv[i], "flags")) {
3142 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3143 } else if (!irccasecmp(argv[i], "registered")) {
3144 const char *cmp = argv[++i];
3145 if (cmp[0] == '<') {
3146 if (cmp[1] == '=') {
3147 discrim->min_registered = now - ParseInterval(cmp+2);
3148 } else {
3149 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3150 }
3151 } else if (cmp[0] == '=') {
3152 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3153 } else if (cmp[0] == '>') {
3154 if (cmp[1] == '=') {
3155 discrim->max_registered = now - ParseInterval(cmp+2);
3156 } else {
3157 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3158 }
3159 } else {
3160 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3161 }
3162 } else if (!irccasecmp(argv[i], "seen")) {
3163 discrim->lastseen = now - ParseInterval(argv[++i]);
3164 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3165 discrim->nickmask = argv[++i];
3166 } else if (!irccasecmp(argv[i], "hostmask")) {
3167 i++;
3168 if (!irccasecmp(argv[i], "exact")) {
3169 if (i == argc - 1) {
3170 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3171 goto fail;
3172 }
3173 discrim->hostmask_type = EXACT;
3174 } else if (!irccasecmp(argv[i], "subset")) {
3175 if (i == argc - 1) {
3176 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3177 goto fail;
3178 }
3179 discrim->hostmask_type = SUBSET;
3180 } else if (!irccasecmp(argv[i], "superset")) {
3181 if (i == argc - 1) {
3182 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3183 goto fail;
3184 }
3185 discrim->hostmask_type = SUPERSET;
3186 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3187 if (i == argc - 1) {
3188 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3189 goto fail;
3190 }
3191 discrim->hostmask_type = LASTQUIT;
3192 } else {
3193 i--;
3194 discrim->hostmask_type = SUPERSET;
3195 }
3196 discrim->hostmask = argv[++i];
3197 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3198 if (!irccasecmp(argv[++i], "*")) {
3199 discrim->handlemask = 0;
3200 } else {
3201 discrim->handlemask = argv[i];
3202 }
3203 } else if (!irccasecmp(argv[i], "email")) {
3204 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3205 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3206 goto fail;
3207 } else if (!irccasecmp(argv[++i], "*")) {
3208 discrim->emailmask = 0;
3209 } else {
3210 discrim->emailmask = argv[i];
3211 }
3212 } else if (!irccasecmp(argv[i], "access")) {
3213 const char *cmp = argv[++i];
3214 if (cmp[0] == '<') {
3215 if (discrim->min_level == 0) discrim->min_level = 1;
3216 if (cmp[1] == '=') {
3217 discrim->max_level = strtoul(cmp+2, NULL, 0);
3218 } else {
3219 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3220 }
3221 } else if (cmp[0] == '=') {
3222 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3223 } else if (cmp[0] == '>') {
3224 if (cmp[1] == '=') {
3225 discrim->min_level = strtoul(cmp+2, NULL, 0);
3226 } else {
3227 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3228 }
3229 } else {
3230 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3231 }
3232 } else {
3233 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3234 goto fail;
3235 }
3236 }
3237 return discrim;
3238 fail:
3239 free(discrim);
3240 return NULL;
3241 }
3242
3243 static int
3244 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3245 {
3246 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3247 || (discrim->flags_off & hi->flags)
3248 || (discrim->min_registered > hi->registered)
3249 || (discrim->max_registered < hi->registered)
3250 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3251 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3252 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3253 || (discrim->min_level > hi->opserv_level)
3254 || (discrim->max_level < hi->opserv_level)) {
3255 return 0;
3256 }
3257 if (discrim->hostmask) {
3258 unsigned int i;
3259 for (i=0; i<hi->masks->used; i++) {
3260 const char *mask = hi->masks->list[i];
3261 if ((discrim->hostmask_type == SUBSET)
3262 && (match_ircglobs(discrim->hostmask, mask))) break;
3263 else if ((discrim->hostmask_type == EXACT)
3264 && !irccasecmp(discrim->hostmask, mask)) break;
3265 else if ((discrim->hostmask_type == SUPERSET)
3266 && (match_ircglobs(mask, discrim->hostmask))) break;
3267 else if ((discrim->hostmask_type == LASTQUIT)
3268 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3269 }
3270 if (i==hi->masks->used) return 0;
3271 }
3272 if (discrim->nickmask) {
3273 struct nick_info *nick = hi->nicks;
3274 while (nick) {
3275 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3276 nick = nick->next;
3277 }
3278 if (!nick) return 0;
3279 }
3280 return 1;
3281 }
3282
3283 static unsigned int
3284 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3285 {
3286 dict_iterator_t it, next;
3287 unsigned int matched;
3288
3289 for (it = dict_first(nickserv_handle_dict), matched = 0;
3290 it && (matched < discrim->limit);
3291 it = next) {
3292 next = iter_next(it);
3293 if (nickserv_discrim_match(discrim, iter_data(it))) {
3294 dsf(source, iter_data(it));
3295 matched++;
3296 }
3297 }
3298 return matched;
3299 }
3300
3301 static void
3302 search_print_func(struct userNode *source, struct handle_info *match)
3303 {
3304 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3305 }
3306
3307 static void
3308 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3309 {
3310 }
3311
3312 static void
3313 search_unregister_func (struct userNode *source, struct handle_info *match)
3314 {
3315 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3316 nickserv_unregister_handle(match, source);
3317 }
3318
3319 static int
3320 nickserv_sort_accounts_by_access(const void *a, const void *b)
3321 {
3322 const struct handle_info *hi_a = *(const struct handle_info**)a;
3323 const struct handle_info *hi_b = *(const struct handle_info**)b;
3324 if (hi_a->opserv_level != hi_b->opserv_level)
3325 return hi_b->opserv_level - hi_a->opserv_level;
3326 return irccasecmp(hi_a->handle, hi_b->handle);
3327 }
3328
3329 void
3330 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3331 {
3332 struct handle_info_list hil;
3333 struct helpfile_table tbl;
3334 unsigned int ii;
3335 dict_iterator_t it;
3336 const char **ary;
3337
3338 memset(&hil, 0, sizeof(hil));
3339 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3340 struct handle_info *hi = iter_data(it);
3341 if (hi->opserv_level)
3342 handle_info_list_append(&hil, hi);
3343 }
3344 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3345 tbl.length = hil.used + 1;
3346 tbl.width = 2;
3347 tbl.flags = TABLE_NO_FREE;
3348 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3349 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3350 ary[0] = "Account";
3351 ary[1] = "Level";
3352 for (ii = 0; ii < hil.used; ) {
3353 ary = malloc(tbl.width * sizeof(ary[0]));
3354 ary[0] = hil.list[ii]->handle;
3355 ary[1] = strtab(hil.list[ii]->opserv_level);
3356 tbl.contents[++ii] = ary;
3357 }
3358 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3359 reply("MSG_MATCH_COUNT", hil.used);
3360 for (ii = 0; ii < hil.used; ii++)
3361 free(tbl.contents[ii]);
3362 free(tbl.contents);
3363 free(hil.list);
3364 }
3365
3366 static NICKSERV_FUNC(cmd_search)
3367 {
3368 struct nickserv_discrim *discrim;
3369 discrim_search_func action;
3370 struct svccmd *subcmd;
3371 unsigned int matches;
3372 char buf[MAXLEN];
3373
3374 NICKSERV_MIN_PARMS(3);
3375 sprintf(buf, "search %s", argv[1]);
3376 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3377 if (!irccasecmp(argv[1], "print"))
3378 action = search_print_func;
3379 else if (!irccasecmp(argv[1], "count"))
3380 action = search_count_func;
3381 else if (!irccasecmp(argv[1], "unregister"))
3382 action = search_unregister_func;
3383 else {
3384 reply("NSMSG_INVALID_ACTION", argv[1]);
3385 return 0;
3386 }
3387
3388 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3389 return 0;
3390
3391 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3392 if (!discrim)
3393 return 0;
3394
3395 if (action == search_print_func)
3396 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3397 else if (action == search_count_func)
3398 discrim->limit = INT_MAX;
3399
3400 matches = nickserv_discrim_search(discrim, action, user);
3401
3402 if (matches)
3403 reply("MSG_MATCH_COUNT", matches);
3404 else
3405 reply("MSG_NO_MATCHES");
3406
3407 free(discrim);
3408 return 0;
3409 }
3410
3411 static MODCMD_FUNC(cmd_checkpass)
3412 {
3413 struct handle_info *hi;
3414
3415 NICKSERV_MIN_PARMS(3);
3416 if (!(hi = get_handle_info(argv[1]))) {
3417 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3418 return 0;
3419 }
3420 if (checkpass(argv[2], hi->passwd))
3421 reply("CHECKPASS_YES");
3422 else
3423 reply("CHECKPASS_NO");
3424 argv[2] = "****";
3425 return 1;
3426 }
3427
3428 static void
3429 nickserv_db_read_handle(const char *handle, dict_t obj)
3430 {
3431 const char *str;
3432 struct string_list *masks, *slist;
3433 struct handle_info *hi;
3434 struct userNode *authed_users;
3435 unsigned long int id;
3436 unsigned int ii;
3437 dict_t subdb;
3438
3439 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3440 id = str ? strtoul(str, NULL, 0) : 0;
3441 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3442 if (!str) {
3443 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3444 return;
3445 }
3446 if ((hi = get_handle_info(handle))) {
3447 authed_users = hi->users;
3448 hi->users = NULL;
3449 dict_remove(nickserv_handle_dict, hi->handle);
3450 } else {
3451 authed_users = NULL;
3452 }
3453 hi = register_handle(handle, str, id);
3454 if (authed_users) {
3455 hi->users = authed_users;
3456 while (authed_users) {
3457 authed_users->handle_info = hi;
3458 authed_users = authed_users->next_authed;
3459 }
3460 }
3461 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3462 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3463 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3464 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3465 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3466 hi->language = language_find(str ? str : "C");
3467 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3468 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3469 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3470 if (str)
3471 hi->infoline = strdup(str);
3472 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3473 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3474 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3475 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3476 /* We want to read the nicks even if disable_nicks is set. This is so
3477 * that we don't lose the nick data entirely. */
3478 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3479 if (slist) {
3480 for (ii=0; ii<slist->used; ii++)
3481 register_nick(slist->list[ii], hi);
3482 }
3483 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3484 if (str) {
3485 for (ii=0; str[ii]; ii++)
3486 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3487 }
3488 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3489 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3490 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3491 hi->announcements = str ? str[0] : '?';
3492 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3493 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3494 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3495 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3496 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3497 if (!str)
3498 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3499 if (str)
3500 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3501 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3502 if (str)
3503 nickserv_set_email_addr(hi, str);
3504 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3505 if (str)
3506 hi->epithet = strdup(str);
3507 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3508 if (str)
3509 hi->fakehost = strdup(str);
3510 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3511 if (subdb) {
3512 const char *data, *type, *expires, *cookie_str;
3513 struct handle_cookie *cookie;
3514
3515 cookie = calloc(1, sizeof(*cookie));
3516 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3517 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3518 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3519 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3520 if (!type || !expires || !cookie_str) {
3521 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3522 goto cookie_out;
3523 }
3524 if (!irccasecmp(type, KEY_ACTIVATION))
3525 cookie->type = ACTIVATION;
3526 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3527 cookie->type = PASSWORD_CHANGE;
3528 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3529 cookie->type = EMAIL_CHANGE;
3530 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3531 cookie->type = ALLOWAUTH;
3532 else {
3533 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3534 goto cookie_out;
3535 }
3536 cookie->expires = strtoul(expires, NULL, 0);
3537 if (cookie->expires < now)
3538 goto cookie_out;
3539 if (data)
3540 cookie->data = strdup(data);
3541 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3542 cookie->hi = hi;
3543 cookie_out:
3544 if (cookie->hi)
3545 nickserv_bake_cookie(cookie);
3546 else
3547 nickserv_free_cookie(cookie);
3548 }
3549 }
3550
3551 static int
3552 nickserv_saxdb_read(dict_t db) {
3553 dict_iterator_t it;
3554 struct record_data *rd;
3555
3556 for (it=dict_first(db); it; it=iter_next(it)) {
3557 rd = iter_data(it);
3558 nickserv_db_read_handle(iter_key(it), rd->d.object);
3559 }
3560 return 0;
3561 }
3562
3563 static NICKSERV_FUNC(cmd_mergedb)
3564 {
3565 struct timeval start, stop;
3566 dict_t db;
3567
3568 NICKSERV_MIN_PARMS(2);
3569 gettimeofday(&start, NULL);
3570 if (!(db = parse_database(argv[1]))) {
3571 reply("NSMSG_DB_UNREADABLE", argv[1]);
3572 return 0;
3573 }
3574 nickserv_saxdb_read(db);
3575 free_database(db);
3576 gettimeofday(&stop, NULL);
3577 stop.tv_sec -= start.tv_sec;
3578 stop.tv_usec -= start.tv_usec;
3579 if (stop.tv_usec < 0) {
3580 stop.tv_sec -= 1;
3581 stop.tv_usec += 1000000;
3582 }
3583 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3584 return 1;
3585 }
3586
3587 static void
3588 expire_handles(UNUSED_ARG(void *data))
3589 {
3590 dict_iterator_t it, next;
3591 time_t expiry;
3592 struct handle_info *hi;
3593
3594 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3595 next = iter_next(it);
3596 hi = iter_data(it);
3597 if ((hi->opserv_level > 0)
3598 || hi->users
3599 || HANDLE_FLAGGED(hi, FROZEN)
3600 || HANDLE_FLAGGED(hi, NODELETE)) {
3601 continue;
3602 }
3603 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3604 if ((now - hi->lastseen) > expiry) {
3605 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3606 nickserv_unregister_handle(hi, NULL);
3607 }
3608 }
3609
3610 if (nickserv_conf.handle_expire_frequency)
3611 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3612 }
3613
3614 static void
3615 nickserv_load_dict(const char *fname)
3616 {
3617 FILE *file;
3618 char line[128];
3619 if (!(file = fopen(fname, "r"))) {
3620 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3621 return;
3622 }
3623 while (!feof(file)) {
3624 fgets(line, sizeof(line), file);
3625 if (!line[0])
3626 continue;
3627 if (line[strlen(line)-1] == '\n')
3628 line[strlen(line)-1] = 0;
3629 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3630 }
3631 fclose(file);
3632 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3633 }
3634
3635 static enum reclaim_action
3636 reclaim_action_from_string(const char *str) {
3637 if (!str)
3638 return RECLAIM_NONE;
3639 else if (!irccasecmp(str, "warn"))
3640 return RECLAIM_WARN;
3641 else if (!irccasecmp(str, "svsnick"))
3642 return RECLAIM_SVSNICK;
3643 else if (!irccasecmp(str, "kill"))
3644 return RECLAIM_KILL;
3645 else
3646 return RECLAIM_NONE;
3647 }
3648
3649 static void
3650 nickserv_conf_read(void)
3651 {
3652 dict_t conf_node, child;
3653 const char *str;
3654 dict_iterator_t it;
3655
3656 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3657 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3658 return;
3659 }
3660 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3661 if (!str)
3662 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3663 if (nickserv_conf.valid_handle_regex_set)
3664 regfree(&nickserv_conf.valid_handle_regex);
3665 if (str) {
3666 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3667 nickserv_conf.valid_handle_regex_set = !err;
3668 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3669 } else {
3670 nickserv_conf.valid_handle_regex_set = 0;
3671 }
3672 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3673 if (nickserv_conf.valid_nick_regex_set)
3674 regfree(&nickserv_conf.valid_nick_regex);
3675 if (str) {
3676 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3677 nickserv_conf.valid_nick_regex_set = !err;
3678 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3679 } else {
3680 nickserv_conf.valid_nick_regex_set = 0;
3681 }
3682 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3683 if (!str)
3684 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3685 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3686 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3687 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3688 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3689 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3690 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3691 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3692 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3693 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3694 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3695 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3696 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3697 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3698 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3699 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3700 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3701 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3702 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3703 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3704 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3705 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3706 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3707 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3708 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3709 if (!str)
3710 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3711 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3712 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3713 if (!str)
3714 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3715 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3716 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3717 if (!str)
3718 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3719 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3720 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3721 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3722 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3723 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3724 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3725 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3726 if (!nickserv_conf.disable_nicks) {
3727 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3728 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3729 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3730 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3731 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3732 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3733 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3734 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3735 }
3736 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3737 for (it=dict_first(child); it; it=iter_next(it)) {
3738 const char *key = iter_key(it), *value;
3739 unsigned char flag;
3740 int pos;
3741
3742 if (!strncasecmp(key, "uc_", 3))
3743 flag = toupper(key[3]);
3744 else if (!strncasecmp(key, "lc_", 3))
3745 flag = tolower(key[3]);
3746 else
3747 flag = key[0];
3748
3749 if ((pos = handle_inverse_flags[flag])) {
3750 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3751 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3752 }
3753 }
3754 if (nickserv_conf.weak_password_dict)
3755 dict_delete(nickserv_conf.weak_password_dict);
3756 nickserv_conf.weak_password_dict = dict_new();
3757 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3758 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3759 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3760 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3761 if (str)
3762 nickserv_load_dict(str);
3763 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3764 if (nickserv && str)
3765 NickChange(nickserv, str, 0);
3766 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3767 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3768 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3769 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3770 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3771 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3772 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3773 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3774 str = database_get_data(conf_node, KEY_SYNC_LOG, RECDB_QSTRING);
3775 nickserv_conf.sync_log = str ? enabled_string(str) : 0;
3776 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3777 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3778 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3779 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3780 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3781 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3782 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3783 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3784 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3785 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3786 str = conf_get_data("server/network", RECDB_QSTRING);
3787 nickserv_conf.network_name = str ? str : "some IRC network";
3788 if (!nickserv_conf.auth_policer_params) {
3789 nickserv_conf.auth_policer_params = policer_params_new();
3790 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3791 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3792 }
3793 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3794 for (it=dict_first(child); it; it=iter_next(it))
3795 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3796 }
3797
3798 static void
3799 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3800 const char *msg;
3801 char newnick[NICKLEN+1];
3802
3803 assert(user);
3804 assert(ni);
3805 switch (action) {
3806 case RECLAIM_NONE:
3807 /* do nothing */
3808 break;
3809 case RECLAIM_WARN:
3810 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3811 break;
3812 case RECLAIM_SVSNICK:
3813 do {
3814 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3815 } while (GetUserH(newnick));
3816 irc_svsnick(nickserv, user, newnick);
3817 break;
3818 case RECLAIM_KILL:
3819 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3820 irc_kill(nickserv, user, msg);
3821 break;
3822 }
3823 }
3824
3825 static void
3826 nickserv_reclaim_p(void *data) {
3827 struct userNode *user = data;
3828 struct nick_info *ni = get_nick_info(user->nick);
3829 if (ni)
3830 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3831 }
3832
3833 static int
3834 check_user_nick(struct userNode *user) {
3835 struct nick_info *ni;
3836 user->modes &= ~FLAGS_REGNICK;
3837 if (!(ni = get_nick_info(user->nick)))
3838 return 0;
3839 if (user->handle_info == ni->owner) {
3840 user->modes |= FLAGS_REGNICK;
3841 irc_regnick(user);
3842 return 0;
3843 }
3844 if (nickserv_conf.warn_nick_owned)
3845 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3846 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3847 return 0;
3848 if (nickserv_conf.auto_reclaim_delay)
3849 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3850 else
3851 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3852 return 0;
3853 }
3854
3855 int
3856 handle_new_user(struct userNode *user)
3857 {
3858 return check_user_nick(user);
3859 }
3860
3861 void
3862 handle_account(struct userNode *user, const char *stamp)
3863 {
3864 struct handle_info *hi;
3865 char *colon;
3866
3867 #ifdef WITH_PROTOCOL_P10
3868 time_t timestamp = 0;
3869
3870 colon = strchr(stamp, ':');
3871 if(colon && colon[1])
3872 {
3873 *colon = 0;
3874 timestamp = atoi(colon+1);
3875 }
3876 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3877 if(hi && timestamp && hi->registered != timestamp)
3878 {
3879 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);
3880 return;
3881 }
3882 #else
3883 hi = dict_find(nickserv_id_dict, stamp, NULL);
3884 log_module(MAIN_LOG, LOG_WARNING, "Using non-P10 code in accounts, not tested at all!");
3885 #endif
3886
3887 if (hi) {
3888 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3889 return;
3890 }
3891 set_user_handle_info(user, hi, 0);
3892 } else {
3893 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3894 }
3895 }
3896
3897 void
3898 handle_nick_change(struct userNode *user, const char *old_nick)
3899 {
3900 struct handle_info *hi;
3901
3902 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3903 dict_remove(nickserv_allow_auth_dict, old_nick);
3904 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3905 }
3906 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3907 check_user_nick(user);
3908 }
3909
3910 void
3911 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3912 {
3913 dict_remove(nickserv_allow_auth_dict, user->nick);
3914 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3915 set_user_handle_info(user, NULL, 0);
3916 }
3917
3918 static struct modcmd *
3919 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3920 {
3921 if (min_level > 0) {
3922 char buf[16];
3923 sprintf(buf, "%u", min_level);
3924 if (must_be_qualified) {
3925 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3926 } else {
3927 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3928 }
3929 } else if (min_level == 0) {
3930 if (must_be_qualified) {
3931 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3932 } else {
3933 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3934 }
3935 } else {
3936 if (must_be_qualified) {
3937 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3938 } else {
3939 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3940 }
3941 }
3942 }
3943
3944 static void
3945 nickserv_db_cleanup(void)
3946 {
3947 unreg_del_user_func(nickserv_remove_user);
3948 userList_clean(&curr_helpers);
3949 policer_params_delete(nickserv_conf.auth_policer_params);
3950 dict_delete(nickserv_handle_dict);
3951 dict_delete(nickserv_nick_dict);
3952 dict_delete(nickserv_opt_dict);
3953 dict_delete(nickserv_allow_auth_dict);
3954 dict_delete(nickserv_email_dict);
3955 dict_delete(nickserv_id_dict);
3956 dict_delete(nickserv_conf.weak_password_dict);
3957 free(auth_func_list);
3958 free(unreg_func_list);
3959 free(rf_list);
3960 free(allowauth_func_list);
3961 free(handle_merge_func_list);
3962 free(failpw_func_list);
3963 if (nickserv_conf.valid_handle_regex_set)
3964 regfree(&nickserv_conf.valid_handle_regex);
3965 if (nickserv_conf.valid_nick_regex_set)
3966 regfree(&nickserv_conf.valid_nick_regex);
3967 }
3968
3969 void
3970 init_nickserv(const char *nick)
3971 {
3972 unsigned int i;
3973 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3974 reg_new_user_func(handle_new_user);
3975 reg_nick_change_func(handle_nick_change);
3976 reg_del_user_func(nickserv_remove_user);
3977 reg_account_func(handle_account);
3978
3979 /* set up handle_inverse_flags */
3980 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3981 for (i=0; handle_flags[i]; i++) {
3982 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3983 flag_access_levels[i] = 0;
3984 }
3985
3986 conf_register_reload(nickserv_conf_read);
3987 nickserv_opt_dict = dict_new();
3988 nickserv_email_dict = dict_new();
3989 dict_set_free_keys(nickserv_email_dict, free);
3990 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3991
3992 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3993 /* Removed qualified_host as default requirement for AUTH, REGISTER, PASS, etc. nets
3994 * can enable it per command using modcmd. (its a shitty default IMO, and now in 1.3
3995 * a big pain to disable since its nolonger in the config file. ) -Rubin
3996 */
3997 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+loghostmask", NULL);
3998 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3999 nickserv_define_func("REGISTER", cmd_register, -1, 0, 0);
4000 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4001 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 0);
4002 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4003 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4004 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4005 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4006 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4007 nickserv_define_func("PASS", cmd_pass, -1, 1, 0);
4008 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4009 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4010 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4011 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4012 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4013 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4014 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
4015 if (!nickserv_conf.disable_nicks) {
4016 /* nick management commands */
4017 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4018 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4019 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4020 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4021 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4022 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4023 }
4024 if (nickserv_conf.email_enabled) {
4025 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4026 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 0);
4027 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 0);
4028 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4029 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4030 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4031 }
4032 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4033 /* miscellaneous commands */
4034 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4035 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4036 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4037 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4038 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4039 /* other options */
4040 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4041 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4042 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4043 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4044 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4045 /* dict_insert(nickserv_opt_dict, "STYLE", opt_style); */
4046 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4047 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4048 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4049 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4050 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4051 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4052 if (nickserv_conf.titlehost_suffix) {
4053 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4054 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4055 }
4056 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
4057 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4058 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4059
4060 nickserv_handle_dict = dict_new();
4061 dict_set_free_keys(nickserv_handle_dict, free);
4062 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4063
4064 nickserv_id_dict = dict_new();
4065 dict_set_free_keys(nickserv_id_dict, free);
4066
4067 nickserv_nick_dict = dict_new();
4068 dict_set_free_data(nickserv_nick_dict, free);
4069
4070 nickserv_allow_auth_dict = dict_new();
4071
4072 userList_init(&curr_helpers);
4073
4074 if (nick) {
4075 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4076 nickserv = AddService(nick, modes ? modes : NULL, "Nick Services", NULL);
4077 nickserv_service = service_register(nickserv);
4078 }
4079 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4080 reg_exit_func(nickserv_db_cleanup);
4081 if(nickserv_conf.handle_expire_frequency)
4082 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4083 message_register_table(msgtab);
4084 }