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