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