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