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