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