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