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