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