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