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