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