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