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