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