]> 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);
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 1 if the handle/pass pair matches, 0 if it doesnt.
1533 *
1534 * called by nefariouses enhanced AC login-on-connect code
1535 *
1536 */
1537 int loc_auth(struct userNode *user, char *handle, char *password)
1538 {
1539 int pw_arg, used, maxlogins;
1540 struct handle_info *hi;
1541 /*
1542 struct userNode *other;
1543 */
1544
1545 hi = dict_find(nickserv_handle_dict, handle, NULL);
1546 pw_arg = 2;
1547 if (!hi) {
1548 return 0;
1549 }
1550 /* Responses from here on look up the language used by the handle they asked about. */
1551 if (!checkpass(password, hi->passwd)) {
1552 return 0;
1553 }
1554 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1555 return 0;
1556 }
1557 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1558 /* Do we want to deny if they already have more logins? I dont see why but
1559 * someone else might? -Rubin
1560 for (used = 0, other = hi->users; other; other = other->next_authed) {
1561 if (++used >= maxlogins) {
1562 send_message_type(4, user, cmd->parent->bot,
1563 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1564 maxlogins);
1565 argv[pw_arg] = "MAXLOGINS";
1566 return 1;
1567 }
1568 }
1569 */
1570 return 1;
1571 }
1572
1573 static NICKSERV_FUNC(cmd_auth)
1574 {
1575 int pw_arg, used, maxlogins;
1576 struct handle_info *hi;
1577 const char *passwd;
1578 struct userNode *other;
1579
1580 if (user->handle_info) {
1581 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1582 return 0;
1583 }
1584 if (IsStamped(user)) {
1585 /* Unauthenticated users might still have been stamped
1586 previously and could therefore have a hidden host;
1587 do not allow them to authenticate. */
1588 reply("NSMSG_STAMPED_AUTH");
1589 return 0;
1590 }
1591 if (argc == 3) {
1592 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1593 pw_arg = 2;
1594 } else if (argc == 2) {
1595 if (nickserv_conf.disable_nicks) {
1596 if (!(hi = get_handle_info(user->nick))) {
1597 reply("NSMSG_HANDLE_NOT_FOUND");
1598 return 0;
1599 }
1600 } else {
1601 /* try to look up their handle from their nick */
1602 struct nick_info *ni;
1603 ni = get_nick_info(user->nick);
1604 if (!ni) {
1605 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1606 return 0;
1607 }
1608 hi = ni->owner;
1609 }
1610 pw_arg = 1;
1611 } else {
1612 reply("MSG_MISSING_PARAMS", argv[0]);
1613 svccmd_send_help(user, nickserv, cmd);
1614 return 0;
1615 }
1616 if (!hi) {
1617 reply("NSMSG_HANDLE_NOT_FOUND");
1618 return 0;
1619 }
1620 /* Responses from here on look up the language used by the handle they asked about. */
1621 passwd = argv[pw_arg];
1622 if (!valid_user_for(user, hi)) {
1623 if (hi->email_addr && nickserv_conf.email_enabled)
1624 send_message_type(4, user, cmd->parent->bot,
1625 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1626 hi->handle);
1627 else
1628 send_message_type(4, user, cmd->parent->bot,
1629 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1630 hi->handle);
1631 argv[pw_arg] = "BADMASK";
1632 return 1;
1633 }
1634 if (!checkpass(passwd, hi->passwd)) {
1635 unsigned int n;
1636 send_message_type(4, user, cmd->parent->bot,
1637 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1638 argv[pw_arg] = "BADPASS";
1639 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1640 if (nickserv_conf.autogag_enabled) {
1641 if (!user->auth_policer.params) {
1642 user->auth_policer.last_req = now;
1643 user->auth_policer.params = nickserv_conf.auth_policer_params;
1644 }
1645 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1646 char *hostmask;
1647 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1648 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1649 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1650 free(hostmask);
1651 argv[pw_arg] = "GAGGED";
1652 }
1653 }
1654 return 1;
1655 }
1656 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1657 send_message_type(4, user, cmd->parent->bot,
1658 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1659 argv[pw_arg] = "SUSPENDED";
1660 return 1;
1661 }
1662 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1663 for (used = 0, other = hi->users; other; other = other->next_authed) {
1664 if (++used >= maxlogins) {
1665 send_message_type(4, user, cmd->parent->bot,
1666 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1667 maxlogins);
1668 argv[pw_arg] = "MAXLOGINS";
1669 return 1;
1670 }
1671 }
1672
1673 set_user_handle_info(user, hi, 1);
1674 if (nickserv_conf.email_required && !hi->email_addr)
1675 reply("NSMSG_PLEASE_SET_EMAIL");
1676 if (!is_secure_password(hi->handle, passwd, NULL))
1677 reply("NSMSG_WEAK_PASSWORD");
1678 if (hi->passwd[0] != '$')
1679 cryptpass(passwd, hi->passwd);
1680 reply("NSMSG_AUTH_SUCCESS");
1681 argv[pw_arg] = "****";
1682 return 1;
1683 }
1684
1685 static allowauth_func_t *allowauth_func_list;
1686 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1687
1688 void
1689 reg_allowauth_func(allowauth_func_t func)
1690 {
1691 if (allowauth_func_used == allowauth_func_size) {
1692 if (allowauth_func_size) {
1693 allowauth_func_size <<= 1;
1694 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1695 } else {
1696 allowauth_func_size = 8;
1697 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1698 }
1699 }
1700 allowauth_func_list[allowauth_func_used++] = func;
1701 }
1702
1703 static NICKSERV_FUNC(cmd_allowauth)
1704 {
1705 struct userNode *target;
1706 struct handle_info *hi;
1707 unsigned int n;
1708
1709 NICKSERV_MIN_PARMS(2);
1710 if (!(target = GetUserH(argv[1]))) {
1711 reply("MSG_NICK_UNKNOWN", argv[1]);
1712 return 0;
1713 }
1714 if (target->handle_info) {
1715 reply("NSMSG_USER_PREV_AUTH", target->nick);
1716 return 0;
1717 }
1718 if (IsStamped(target)) {
1719 /* Unauthenticated users might still have been stamped
1720 previously and could therefore have a hidden host;
1721 do not allow them to authenticate to an account. */
1722 reply("NSMSG_USER_PREV_STAMP", target->nick);
1723 return 0;
1724 }
1725 if (argc == 2)
1726 hi = NULL;
1727 else if (!(hi = get_handle_info(argv[2]))) {
1728 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1729 return 0;
1730 }
1731 if (hi) {
1732 if (hi->opserv_level > user->handle_info->opserv_level) {
1733 reply("MSG_USER_OUTRANKED", hi->handle);
1734 return 0;
1735 }
1736 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1737 || (hi->opserv_level > 0))
1738 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1739 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1740 return 0;
1741 }
1742 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1743 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1744 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1745 if (nickserv_conf.email_enabled)
1746 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1747 } else {
1748 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1749 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1750 else
1751 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1752 }
1753 for (n=0; n<allowauth_func_used; n++)
1754 allowauth_func_list[n](user, target, hi);
1755 return 1;
1756 }
1757
1758 static NICKSERV_FUNC(cmd_authcookie)
1759 {
1760 struct handle_info *hi;
1761
1762 NICKSERV_MIN_PARMS(2);
1763 if (user->handle_info) {
1764 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1765 return 0;
1766 }
1767 if (IsStamped(user)) {
1768 /* Unauthenticated users might still have been stamped
1769 previously and could therefore have a hidden host;
1770 do not allow them to authenticate to an account. */
1771 reply("NSMSG_STAMPED_AUTHCOOKIE");
1772 return 0;
1773 }
1774 if (!(hi = get_handle_info(argv[1]))) {
1775 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1776 return 0;
1777 }
1778 if (!hi->email_addr) {
1779 reply("MSG_SET_EMAIL_ADDR");
1780 return 0;
1781 }
1782 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1783 return 1;
1784 }
1785
1786 static NICKSERV_FUNC(cmd_delcookie)
1787 {
1788 struct handle_info *hi;
1789
1790 hi = user->handle_info;
1791 if (!hi->cookie) {
1792 reply("NSMSG_NO_COOKIE");
1793 return 0;
1794 }
1795 switch (hi->cookie->type) {
1796 case ACTIVATION:
1797 case EMAIL_CHANGE:
1798 reply("NSMSG_MUST_TIME_OUT");
1799 break;
1800 default:
1801 nickserv_eat_cookie(hi->cookie);
1802 reply("NSMSG_ATE_COOKIE");
1803 break;
1804 }
1805 return 1;
1806 }
1807
1808 static NICKSERV_FUNC(cmd_resetpass)
1809 {
1810 struct handle_info *hi;
1811 char crypted[MD5_CRYPT_LENGTH];
1812
1813 NICKSERV_MIN_PARMS(3);
1814 if (user->handle_info) {
1815 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1816 return 0;
1817 }
1818 if (IsStamped(user)) {
1819 /* Unauthenticated users might still have been stamped
1820 previously and could therefore have a hidden host;
1821 do not allow them to activate an account. */
1822 reply("NSMSG_STAMPED_RESETPASS");
1823 return 0;
1824 }
1825 if (!(hi = get_handle_info(argv[1]))) {
1826 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1827 return 0;
1828 }
1829 if (!hi->email_addr) {
1830 reply("MSG_SET_EMAIL_ADDR");
1831 return 0;
1832 }
1833 cryptpass(argv[2], crypted);
1834 argv[2] = "****";
1835 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1836 return 1;
1837 }
1838
1839 static NICKSERV_FUNC(cmd_cookie)
1840 {
1841 struct handle_info *hi;
1842 const char *cookie;
1843
1844 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1845 cookie = argv[1];
1846 } else {
1847 NICKSERV_MIN_PARMS(3);
1848 if (!(hi = get_handle_info(argv[1]))) {
1849 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1850 return 0;
1851 }
1852 cookie = argv[2];
1853 }
1854
1855 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1856 reply("NSMSG_HANDLE_SUSPENDED");
1857 return 0;
1858 }
1859
1860 if (!hi->cookie) {
1861 reply("NSMSG_NO_COOKIE");
1862 return 0;
1863 }
1864
1865 /* Check validity of operation before comparing cookie to
1866 * prohibit guessing by authed users. */
1867 if (user->handle_info
1868 && (hi->cookie->type != EMAIL_CHANGE)
1869 && (hi->cookie->type != PASSWORD_CHANGE)) {
1870 reply("NSMSG_CANNOT_COOKIE");
1871 return 0;
1872 }
1873
1874 if (strcmp(cookie, hi->cookie->cookie)) {
1875 reply("NSMSG_BAD_COOKIE");
1876 return 0;
1877 }
1878
1879 switch (hi->cookie->type) {
1880 case ACTIVATION:
1881 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1882 set_user_handle_info(user, hi, 1);
1883 reply("NSMSG_HANDLE_ACTIVATED");
1884 break;
1885 case PASSWORD_CHANGE:
1886 set_user_handle_info(user, hi, 1);
1887 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1888 reply("NSMSG_PASSWORD_CHANGED");
1889 break;
1890 case EMAIL_CHANGE:
1891 nickserv_set_email_addr(hi, hi->cookie->data);
1892 reply("NSMSG_EMAIL_CHANGED");
1893 break;
1894 case ALLOWAUTH:
1895 set_user_handle_info(user, hi, 1);
1896 reply("NSMSG_AUTH_SUCCESS");
1897 break;
1898 default:
1899 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1900 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1901 break;
1902 }
1903
1904 nickserv_eat_cookie(hi->cookie);
1905
1906 return 1;
1907 }
1908
1909 static NICKSERV_FUNC(cmd_oregnick) {
1910 const char *nick;
1911 struct handle_info *target;
1912 struct nick_info *ni;
1913
1914 NICKSERV_MIN_PARMS(3);
1915 if (!(target = modcmd_get_handle_info(user, argv[1])))
1916 return 0;
1917 nick = argv[2];
1918 if (!is_registerable_nick(nick)) {
1919 reply("NSMSG_BAD_NICK", nick);
1920 return 0;
1921 }
1922 ni = dict_find(nickserv_nick_dict, nick, NULL);
1923 if (ni) {
1924 reply("NSMSG_NICK_EXISTS", nick);
1925 return 0;
1926 }
1927 register_nick(nick, target);
1928 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1929 return 1;
1930 }
1931
1932 static NICKSERV_FUNC(cmd_regnick) {
1933 unsigned n;
1934 struct nick_info *ni;
1935
1936 if (!is_registerable_nick(user->nick)) {
1937 reply("NSMSG_BAD_NICK", user->nick);
1938 return 0;
1939 }
1940 /* count their nicks, see if it's too many */
1941 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1942 if (n >= nickserv_conf.nicks_per_handle) {
1943 reply("NSMSG_TOO_MANY_NICKS");
1944 return 0;
1945 }
1946 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1947 if (ni) {
1948 reply("NSMSG_NICK_EXISTS", user->nick);
1949 return 0;
1950 }
1951 register_nick(user->nick, user->handle_info);
1952 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1953 return 1;
1954 }
1955
1956 static NICKSERV_FUNC(cmd_pass)
1957 {
1958 struct handle_info *hi;
1959 const char *old_pass, *new_pass;
1960
1961 NICKSERV_MIN_PARMS(3);
1962 hi = user->handle_info;
1963 old_pass = argv[1];
1964 new_pass = argv[2];
1965 argv[2] = "****";
1966 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1967 if (!checkpass(old_pass, hi->passwd)) {
1968 argv[1] = "BADPASS";
1969 reply("NSMSG_PASSWORD_INVALID");
1970 return 0;
1971 }
1972 cryptpass(new_pass, hi->passwd);
1973 argv[1] = "****";
1974 reply("NSMSG_PASS_SUCCESS");
1975 return 1;
1976 }
1977
1978 static int
1979 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1980 {
1981 unsigned int i;
1982 char *new_mask = canonicalize_hostmask(strdup(mask));
1983 for (i=0; i<hi->masks->used; i++) {
1984 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1985 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1986 free(new_mask);
1987 return 0;
1988 }
1989 }
1990 string_list_append(hi->masks, new_mask);
1991 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
1992 return 1;
1993 }
1994
1995 static NICKSERV_FUNC(cmd_addmask)
1996 {
1997 if (argc < 2) {
1998 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1999 int res = nickserv_addmask(user, user->handle_info, mask);
2000 free(mask);
2001 return res;
2002 } else {
2003 if (!is_gline(argv[1])) {
2004 reply("NSMSG_MASK_INVALID", argv[1]);
2005 return 0;
2006 }
2007 return nickserv_addmask(user, user->handle_info, argv[1]);
2008 }
2009 }
2010
2011 static NICKSERV_FUNC(cmd_oaddmask)
2012 {
2013 struct handle_info *hi;
2014
2015 NICKSERV_MIN_PARMS(3);
2016 if (!(hi = get_victim_oper(user, argv[1])))
2017 return 0;
2018 return nickserv_addmask(user, hi, argv[2]);
2019 }
2020
2021 static int
2022 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
2023 {
2024 unsigned int i;
2025 for (i=0; i<hi->masks->used; i++) {
2026 if (!strcmp(del_mask, hi->masks->list[i])) {
2027 char *old_mask = hi->masks->list[i];
2028 if (hi->masks->used == 1) {
2029 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2030 return 0;
2031 }
2032 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2033 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2034 free(old_mask);
2035 return 1;
2036 }
2037 }
2038 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2039 return 0;
2040 }
2041
2042 static NICKSERV_FUNC(cmd_delmask)
2043 {
2044 NICKSERV_MIN_PARMS(2);
2045 return nickserv_delmask(user, user->handle_info, argv[1]);
2046 }
2047
2048 static NICKSERV_FUNC(cmd_odelmask)
2049 {
2050 struct handle_info *hi;
2051 NICKSERV_MIN_PARMS(3);
2052 if (!(hi = get_victim_oper(user, argv[1])))
2053 return 0;
2054 return nickserv_delmask(user, hi, argv[2]);
2055 }
2056
2057 int
2058 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2059 unsigned int nn, add = 1, pos;
2060 unsigned long added, removed, flag;
2061
2062 for (added=removed=nn=0; str[nn]; nn++) {
2063 switch (str[nn]) {
2064 case '+': add = 1; break;
2065 case '-': add = 0; break;
2066 default:
2067 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2068 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2069 return 0;
2070 }
2071 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2072 /* cheesy avoidance of looking up the flag name.. */
2073 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2074 return 0;
2075 }
2076 flag = 1 << (pos - 1);
2077 if (add)
2078 added |= flag, removed &= ~flag;
2079 else
2080 removed |= flag, added &= ~flag;
2081 break;
2082 }
2083 }
2084 *padded = added;
2085 *premoved = removed;
2086 return 1;
2087 }
2088
2089 static int
2090 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2091 {
2092 unsigned long before, after, added, removed;
2093 struct userNode *uNode;
2094
2095 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2096 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2097 return 0;
2098 hi->flags = (hi->flags | added) & ~removed;
2099 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2100
2101 /* Strip helping flag if they're only a support helper and not
2102 * currently in #support. */
2103 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2104 struct channelList *schannels;
2105 unsigned int ii;
2106 schannels = chanserv_support_channels();
2107 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2108 for (ii = 0; ii < schannels->used; ++ii)
2109 if (GetUserMode(schannels->list[ii], uNode))
2110 break;
2111 if (ii < schannels->used)
2112 break;
2113 }
2114 if (!uNode)
2115 HANDLE_CLEAR_FLAG(hi, HELPING);
2116 }
2117
2118 if (after && !before) {
2119 /* Add user to current helper list. */
2120 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2121 userList_append(&curr_helpers, uNode);
2122 } else if (!after && before) {
2123 /* Remove user from current helper list. */
2124 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2125 userList_remove(&curr_helpers, uNode);
2126 }
2127
2128 return 1;
2129 }
2130
2131 static void
2132 set_list(struct userNode *user, struct handle_info *hi, int override)
2133 {
2134 option_func_t *opt;
2135 unsigned int i;
2136 char *set_display[] = {
2137 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2138 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2139 };
2140
2141 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2142
2143 /* Do this so options are presented in a consistent order. */
2144 for (i = 0; i < ArrayLength(set_display); ++i)
2145 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2146 opt(user, hi, override, 0, NULL);
2147 }
2148
2149 static NICKSERV_FUNC(cmd_set)
2150 {
2151 struct handle_info *hi;
2152 option_func_t *opt;
2153
2154 hi = user->handle_info;
2155 if (argc < 2) {
2156 set_list(user, hi, 0);
2157 return 1;
2158 }
2159 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2160 reply("NSMSG_INVALID_OPTION", argv[1]);
2161 return 0;
2162 }
2163 return opt(user, hi, 0, argc-1, argv+1);
2164 }
2165
2166 static NICKSERV_FUNC(cmd_oset)
2167 {
2168 struct handle_info *hi;
2169 option_func_t *opt;
2170
2171 NICKSERV_MIN_PARMS(2);
2172
2173 if (!(hi = get_victim_oper(user, argv[1])))
2174 return 0;
2175
2176 if (argc < 3) {
2177 set_list(user, hi, 0);
2178 return 1;
2179 }
2180
2181 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2182 reply("NSMSG_INVALID_OPTION", argv[2]);
2183 return 0;
2184 }
2185
2186 return opt(user, hi, 1, argc-2, argv+2);
2187 }
2188
2189 static OPTION_FUNC(opt_info)
2190 {
2191 const char *info;
2192 if (argc > 1) {
2193 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2194 free(hi->infoline);
2195 hi->infoline = NULL;
2196 } else {
2197 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2198 }
2199 }
2200
2201 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2202 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2203 return 1;
2204 }
2205
2206 static OPTION_FUNC(opt_width)
2207 {
2208 if (argc > 1)
2209 hi->screen_width = strtoul(argv[1], NULL, 0);
2210
2211 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2212 hi->screen_width = MIN_LINE_SIZE;
2213 else if (hi->screen_width > MAX_LINE_SIZE)
2214 hi->screen_width = MAX_LINE_SIZE;
2215
2216 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2217 return 1;
2218 }
2219
2220 static OPTION_FUNC(opt_tablewidth)
2221 {
2222 if (argc > 1)
2223 hi->table_width = strtoul(argv[1], NULL, 0);
2224
2225 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2226 hi->table_width = MIN_LINE_SIZE;
2227 else if (hi->screen_width > MAX_LINE_SIZE)
2228 hi->table_width = MAX_LINE_SIZE;
2229
2230 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2231 return 1;
2232 }
2233
2234 static OPTION_FUNC(opt_color)
2235 {
2236 if (argc > 1) {
2237 if (enabled_string(argv[1]))
2238 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2239 else if (disabled_string(argv[1]))
2240 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2241 else {
2242 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2243 return 0;
2244 }
2245 }
2246
2247 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2248 return 1;
2249 }
2250
2251 static OPTION_FUNC(opt_privmsg)
2252 {
2253 if (argc > 1) {
2254 if (enabled_string(argv[1]))
2255 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2256 else if (disabled_string(argv[1]))
2257 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2258 else {
2259 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2260 return 0;
2261 }
2262 }
2263
2264 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2265 return 1;
2266 }
2267
2268 static OPTION_FUNC(opt_style)
2269 {
2270 char *style;
2271
2272 if (argc > 1) {
2273 if (!irccasecmp(argv[1], "Zoot"))
2274 hi->userlist_style = HI_STYLE_ZOOT;
2275 else if (!irccasecmp(argv[1], "def"))
2276 hi->userlist_style = HI_STYLE_DEF;
2277 }
2278
2279 switch (hi->userlist_style) {
2280 case HI_STYLE_DEF:
2281 style = "def";
2282 break;
2283 case HI_STYLE_ZOOT:
2284 default:
2285 style = "Zoot";
2286 }
2287
2288 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2289 return 1;
2290 }
2291
2292 static OPTION_FUNC(opt_announcements)
2293 {
2294 const char *choice;
2295
2296 if (argc > 1) {
2297 if (enabled_string(argv[1]))
2298 hi->announcements = 'y';
2299 else if (disabled_string(argv[1]))
2300 hi->announcements = 'n';
2301 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2302 hi->announcements = '?';
2303 else {
2304 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2305 return 0;
2306 }
2307 }
2308
2309 switch (hi->announcements) {
2310 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2311 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2312 case '?': choice = "default"; break;
2313 default: choice = "unknown"; break;
2314 }
2315 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2316 return 1;
2317 }
2318
2319 static OPTION_FUNC(opt_password)
2320 {
2321 if (!override) {
2322 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2323 return 0;
2324 }
2325
2326 if (argc > 1)
2327 cryptpass(argv[1], hi->passwd);
2328
2329 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2330 return 1;
2331 }
2332
2333 static OPTION_FUNC(opt_flags)
2334 {
2335 char flags[33];
2336 unsigned int ii, flen;
2337
2338 if (!override) {
2339 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2340 return 0;
2341 }
2342
2343 if (argc > 1)
2344 nickserv_apply_flags(user, hi, argv[1]);
2345
2346 for (ii = flen = 0; handle_flags[ii]; ii++)
2347 if (hi->flags & (1 << ii))
2348 flags[flen++] = handle_flags[ii];
2349 flags[flen] = '\0';
2350 if (hi->flags)
2351 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2352 else
2353 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2354 return 1;
2355 }
2356
2357 static OPTION_FUNC(opt_email)
2358 {
2359 if (argc > 1) {
2360 const char *str;
2361 if (!is_valid_email_addr(argv[1])) {
2362 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2363 return 0;
2364 }
2365 if ((str = sendmail_prohibited_address(argv[1]))) {
2366 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2367 return 0;
2368 }
2369 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2370 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2371 else if (!override)
2372 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2373 else {
2374 nickserv_set_email_addr(hi, argv[1]);
2375 if (hi->cookie)
2376 nickserv_eat_cookie(hi->cookie);
2377 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2378 }
2379 } else
2380 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2381 return 1;
2382 }
2383
2384 static OPTION_FUNC(opt_maxlogins)
2385 {
2386 unsigned char maxlogins;
2387 if (argc > 1) {
2388 maxlogins = strtoul(argv[1], NULL, 0);
2389 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2390 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2391 return 0;
2392 }
2393 hi->maxlogins = maxlogins;
2394 }
2395 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2396 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2397 return 1;
2398 }
2399
2400 static OPTION_FUNC(opt_language)
2401 {
2402 struct language *lang;
2403 if (argc > 1) {
2404 lang = language_find(argv[1]);
2405 if (irccasecmp(lang->name, argv[1]))
2406 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2407 hi->language = lang;
2408 }
2409 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2410 return 1;
2411 }
2412
2413 int
2414 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2415 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2416 return 0;
2417 if ((user->handle_info->opserv_level < target->opserv_level)
2418 || ((user->handle_info->opserv_level == target->opserv_level)
2419 && (user->handle_info->opserv_level < 1000))) {
2420 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2421 return 0;
2422 }
2423 if ((user->handle_info->opserv_level < new_level)
2424 || ((user->handle_info->opserv_level == new_level)
2425 && (user->handle_info->opserv_level < 1000))) {
2426 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2427 return 0;
2428 }
2429 if (user->handle_info == target) {
2430 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2431 return 0;
2432 }
2433 if (target->opserv_level == new_level)
2434 return 0;
2435 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2436 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2437 target->opserv_level = new_level;
2438 return 1;
2439 }
2440
2441 static OPTION_FUNC(opt_level)
2442 {
2443 int res;
2444
2445 if (!override) {
2446 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2447 return 0;
2448 }
2449
2450 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2451 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2452 return res;
2453 }
2454
2455 static OPTION_FUNC(opt_epithet)
2456 {
2457 if (!override) {
2458 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2459 return 0;
2460 }
2461
2462 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2463 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2464 if (hi->epithet)
2465 free(hi->epithet);
2466 if ((epithet[0] == '*') && !epithet[1])
2467 hi->epithet = NULL;
2468 else
2469 hi->epithet = strdup(epithet);
2470 }
2471
2472 if (hi->epithet)
2473 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2474 else
2475 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2476 return 1;
2477 }
2478
2479 static OPTION_FUNC(opt_title)
2480 {
2481 const char *title;
2482
2483 if (!override) {
2484 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2485 return 0;
2486 }
2487
2488 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2489 title = argv[1];
2490 if (strchr(title, '.')) {
2491 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2492 return 0;
2493 }
2494 if ((strlen(user->handle_info->handle) + strlen(title) +
2495 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2496 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2497 return 0;
2498 }
2499
2500 free(hi->fakehost);
2501 if (!strcmp(title, "*")) {
2502 hi->fakehost = NULL;
2503 } else {
2504 hi->fakehost = malloc(strlen(title)+2);
2505 hi->fakehost[0] = '.';
2506 strcpy(hi->fakehost+1, title);
2507 }
2508 apply_fakehost(hi);
2509 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2510 title = hi->fakehost + 1;
2511 else
2512 title = NULL;
2513 if (!title)
2514 title = user_find_message(user, "MSG_NONE");
2515 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2516 return 1;
2517 }
2518
2519 static OPTION_FUNC(opt_fakehost)
2520 {
2521 const char *fake;
2522
2523 if (!override) {
2524 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2525 return 0;
2526 }
2527
2528 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2529 fake = argv[1];
2530 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2531 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2532 return 0;
2533 }
2534 free(hi->fakehost);
2535 if (!strcmp(fake, "*"))
2536 hi->fakehost = NULL;
2537 else
2538 hi->fakehost = strdup(fake);
2539 fake = hi->fakehost;
2540 apply_fakehost(hi);
2541 } else
2542 fake = generate_fakehost(hi);
2543 if (!fake)
2544 fake = user_find_message(user, "MSG_NONE");
2545 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2546 return 1;
2547 }
2548
2549 static NICKSERV_FUNC(cmd_reclaim)
2550 {
2551 struct handle_info *hi;
2552 struct nick_info *ni;
2553 struct userNode *victim;
2554
2555 NICKSERV_MIN_PARMS(2);
2556 hi = user->handle_info;
2557 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2558 if (!ni) {
2559 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2560 return 0;
2561 }
2562 if (ni->owner != user->handle_info) {
2563 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2564 return 0;
2565 }
2566 victim = GetUserH(ni->nick);
2567 if (!victim) {
2568 reply("MSG_NICK_UNKNOWN", ni->nick);
2569 return 0;
2570 }
2571 if (victim == user) {
2572 reply("NSMSG_NICK_USER_YOU");
2573 return 0;
2574 }
2575 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2576 switch (nickserv_conf.reclaim_action) {
2577 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2578 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2579 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2580 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2581 }
2582 return 1;
2583 }
2584
2585 static NICKSERV_FUNC(cmd_unregnick)
2586 {
2587 const char *nick;
2588 struct handle_info *hi;
2589 struct nick_info *ni;
2590
2591 hi = user->handle_info;
2592 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2593 ni = dict_find(nickserv_nick_dict, nick, NULL);
2594 if (!ni) {
2595 reply("NSMSG_UNKNOWN_NICK", nick);
2596 return 0;
2597 }
2598 if (hi != ni->owner) {
2599 reply("NSMSG_NOT_YOUR_NICK", nick);
2600 return 0;
2601 }
2602 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2603 delete_nick(ni);
2604 return 1;
2605 }
2606
2607 static NICKSERV_FUNC(cmd_ounregnick)
2608 {
2609 struct nick_info *ni;
2610
2611 NICKSERV_MIN_PARMS(2);
2612 if (!(ni = get_nick_info(argv[1]))) {
2613 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2614 return 0;
2615 }
2616 if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2617 reply("MSG_USER_OUTRANKED", ni->nick);
2618 return 0;
2619 }
2620 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2621 delete_nick(ni);
2622 return 1;
2623 }
2624
2625 static NICKSERV_FUNC(cmd_unregister)
2626 {
2627 struct handle_info *hi;
2628 char *passwd;
2629
2630 NICKSERV_MIN_PARMS(2);
2631 hi = user->handle_info;
2632 passwd = argv[1];
2633 argv[1] = "****";
2634 if (checkpass(passwd, hi->passwd)) {
2635 nickserv_unregister_handle(hi, user);
2636 return 1;
2637 } else {
2638 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2639 reply("NSMSG_PASSWORD_INVALID");
2640 return 0;
2641 }
2642 }
2643
2644 static NICKSERV_FUNC(cmd_ounregister)
2645 {
2646 struct handle_info *hi;
2647
2648 NICKSERV_MIN_PARMS(2);
2649 if (!(hi = get_victim_oper(user, argv[1])))
2650 return 0;
2651 nickserv_unregister_handle(hi, user);
2652 return 1;
2653 }
2654
2655 static NICKSERV_FUNC(cmd_status)
2656 {
2657 if (nickserv_conf.disable_nicks) {
2658 reply("NSMSG_GLOBAL_STATS_NONICK",
2659 dict_size(nickserv_handle_dict));
2660 } else {
2661 if (user->handle_info) {
2662 int cnt=0;
2663 struct nick_info *ni;
2664 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2665 reply("NSMSG_HANDLE_STATS", cnt);
2666 } else {
2667 reply("NSMSG_HANDLE_NONE");
2668 }
2669 reply("NSMSG_GLOBAL_STATS",
2670 dict_size(nickserv_handle_dict),
2671 dict_size(nickserv_nick_dict));
2672 }
2673 return 1;
2674 }
2675
2676 static NICKSERV_FUNC(cmd_ghost)
2677 {
2678 struct userNode *target;
2679 char reason[MAXLEN];
2680
2681 NICKSERV_MIN_PARMS(2);
2682 if (!(target = GetUserH(argv[1]))) {
2683 reply("MSG_NICK_UNKNOWN", argv[1]);
2684 return 0;
2685 }
2686 if (target == user) {
2687 reply("NSMSG_CANNOT_GHOST_SELF");
2688 return 0;
2689 }
2690 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2691 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2692 return 0;
2693 }
2694 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2695 DelUser(target, nickserv, 1, reason);
2696 reply("NSMSG_GHOST_KILLED", argv[1]);
2697 return 1;
2698 }
2699
2700 static NICKSERV_FUNC(cmd_vacation)
2701 {
2702 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2703 reply("NSMSG_ON_VACATION");
2704 return 1;
2705 }
2706
2707 static int
2708 nickserv_saxdb_write(struct saxdb_context *ctx) {
2709 dict_iterator_t it;
2710 struct handle_info *hi;
2711 char flags[33];
2712
2713 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2714 hi = iter_data(it);
2715 #ifdef WITH_PROTOCOL_BAHAMUT
2716 assert(hi->id);
2717 #endif
2718 saxdb_start_record(ctx, iter_key(it), 0);
2719 if (hi->announcements != '?') {
2720 flags[0] = hi->announcements;
2721 flags[1] = 0;
2722 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2723 }
2724 if (hi->cookie) {
2725 struct handle_cookie *cookie = hi->cookie;
2726 char *type;
2727
2728 switch (cookie->type) {
2729 case ACTIVATION: type = KEY_ACTIVATION; break;
2730 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2731 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2732 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2733 default: type = NULL; break;
2734 }
2735 if (type) {
2736 saxdb_start_record(ctx, KEY_COOKIE, 0);
2737 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2738 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2739 if (cookie->data)
2740 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2741 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2742 saxdb_end_record(ctx);
2743 }
2744 }
2745 if (hi->email_addr)
2746 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2747 if (hi->epithet)
2748 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2749 if (hi->fakehost)
2750 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2751 if (hi->flags) {
2752 int ii, flen;
2753
2754 for (ii=flen=0; handle_flags[ii]; ++ii)
2755 if (hi->flags & (1 << ii))
2756 flags[flen++] = handle_flags[ii];
2757 flags[flen] = 0;
2758 saxdb_write_string(ctx, KEY_FLAGS, flags);
2759 }
2760 #ifdef WITH_PROTOCOL_BAHAMUT
2761 saxdb_write_int(ctx, KEY_ID, hi->id);
2762 #endif
2763 if (hi->infoline)
2764 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2765 if (hi->last_quit_host[0])
2766 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2767 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2768 if (hi->masks->used)
2769 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2770 if (hi->maxlogins)
2771 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2772 if (hi->nicks) {
2773 struct string_list *slist;
2774 struct nick_info *ni;
2775
2776 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2777 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2778 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2779 free(slist->list);
2780 free(slist);
2781 }
2782 if (hi->opserv_level)
2783 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2784 if (hi->language != lang_C)
2785 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2786 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2787 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2788 if (hi->screen_width)
2789 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2790 if (hi->table_width)
2791 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2792 flags[0] = hi->userlist_style;
2793 flags[1] = 0;
2794 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2795 saxdb_end_record(ctx);
2796 }
2797 return 0;
2798 }
2799
2800 static handle_merge_func_t *handle_merge_func_list;
2801 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2802
2803 void
2804 reg_handle_merge_func(handle_merge_func_t func)
2805 {
2806 if (handle_merge_func_used == handle_merge_func_size) {
2807 if (handle_merge_func_size) {
2808 handle_merge_func_size <<= 1;
2809 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2810 } else {
2811 handle_merge_func_size = 8;
2812 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2813 }
2814 }
2815 handle_merge_func_list[handle_merge_func_used++] = func;
2816 }
2817
2818 static NICKSERV_FUNC(cmd_merge)
2819 {
2820 struct handle_info *hi_from, *hi_to;
2821 struct userNode *last_user;
2822 struct userData *cList, *cListNext;
2823 unsigned int ii, jj, n;
2824 char buffer[MAXLEN];
2825
2826 NICKSERV_MIN_PARMS(3);
2827
2828 if (!(hi_from = get_victim_oper(user, argv[1])))
2829 return 0;
2830 if (!(hi_to = get_victim_oper(user, argv[2])))
2831 return 0;
2832 if (hi_to == hi_from) {
2833 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2834 return 0;
2835 }
2836
2837 for (n=0; n<handle_merge_func_used; n++)
2838 handle_merge_func_list[n](user, hi_to, hi_from);
2839
2840 /* Append "from" handle's nicks to "to" handle's nick list. */
2841 if (hi_to->nicks) {
2842 struct nick_info *last_ni;
2843 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2844 last_ni->next = hi_from->nicks;
2845 }
2846 while (hi_from->nicks) {
2847 hi_from->nicks->owner = hi_to;
2848 hi_from->nicks = hi_from->nicks->next;
2849 }
2850
2851 /* Merge the hostmasks. */
2852 for (ii=0; ii<hi_from->masks->used; ii++) {
2853 char *mask = hi_from->masks->list[ii];
2854 for (jj=0; jj<hi_to->masks->used; jj++)
2855 if (match_ircglobs(hi_to->masks->list[jj], mask))
2856 break;
2857 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2858 string_list_append(hi_to->masks, strdup(mask));
2859 }
2860
2861 /* Merge the lists of authed users. */
2862 if (hi_to->users) {
2863 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2864 last_user->next_authed = hi_from->users;
2865 } else {
2866 hi_to->users = hi_from->users;
2867 }
2868 /* Repoint the old "from" handle's users. */
2869 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2870 last_user->handle_info = hi_to;
2871 }
2872 hi_from->users = NULL;
2873
2874 /* Merge channel userlists. */
2875 for (cList=hi_from->channels; cList; cList=cListNext) {
2876 struct userData *cList2;
2877 cListNext = cList->u_next;
2878 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2879 if (cList->channel == cList2->channel)
2880 break;
2881 if (cList2 && (cList2->access >= cList->access)) {
2882 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);
2883 /* keep cList2 in hi_to; remove cList from hi_from */
2884 del_channel_user(cList, 1);
2885 } else {
2886 if (cList2) {
2887 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);
2888 /* remove the lower-ranking cList2 from hi_to */
2889 del_channel_user(cList2, 1);
2890 } else {
2891 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
2892 }
2893 /* cList needs to be moved from hi_from to hi_to */
2894 cList->handle = hi_to;
2895 /* Remove from linked list for hi_from */
2896 assert(!cList->u_prev);
2897 hi_from->channels = cList->u_next;
2898 if (cList->u_next)
2899 cList->u_next->u_prev = cList->u_prev;
2900 /* Add to linked list for hi_to */
2901 cList->u_prev = NULL;
2902 cList->u_next = hi_to->channels;
2903 if (hi_to->channels)
2904 hi_to->channels->u_prev = cList;
2905 hi_to->channels = cList;
2906 }
2907 }
2908
2909 /* Do they get an OpServ level promotion? */
2910 if (hi_from->opserv_level > hi_to->opserv_level)
2911 hi_to->opserv_level = hi_from->opserv_level;
2912
2913 /* What about last seen time? */
2914 if (hi_from->lastseen > hi_to->lastseen)
2915 hi_to->lastseen = hi_from->lastseen;
2916
2917 /* Notify of success. */
2918 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2919 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2920 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2921
2922 /* Unregister the "from" handle. */
2923 nickserv_unregister_handle(hi_from, NULL);
2924
2925 return 1;
2926 }
2927
2928 struct nickserv_discrim {
2929 unsigned int limit, min_level, max_level;
2930 unsigned long flags_on, flags_off;
2931 time_t min_registered, max_registered;
2932 time_t lastseen;
2933 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2934 const char *nickmask;
2935 const char *hostmask;
2936 const char *handlemask;
2937 const char *emailmask;
2938 };
2939
2940 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2941
2942 struct discrim_apply_info {
2943 struct nickserv_discrim *discrim;
2944 discrim_search_func func;
2945 struct userNode *source;
2946 unsigned int matched;
2947 };
2948
2949 static struct nickserv_discrim *
2950 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2951 {
2952 unsigned int i;
2953 struct nickserv_discrim *discrim;
2954
2955 discrim = malloc(sizeof(*discrim));
2956 memset(discrim, 0, sizeof(*discrim));
2957 discrim->min_level = 0;
2958 discrim->max_level = ~0;
2959 discrim->limit = 50;
2960 discrim->min_registered = 0;
2961 discrim->max_registered = INT_MAX;
2962 discrim->lastseen = now;
2963
2964 for (i=0; i<argc; i++) {
2965 if (i == argc - 1) {
2966 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2967 goto fail;
2968 }
2969 if (!irccasecmp(argv[i], "limit")) {
2970 discrim->limit = strtoul(argv[++i], NULL, 0);
2971 } else if (!irccasecmp(argv[i], "flags")) {
2972 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2973 } else if (!irccasecmp(argv[i], "registered")) {
2974 const char *cmp = argv[++i];
2975 if (cmp[0] == '<') {
2976 if (cmp[1] == '=') {
2977 discrim->min_registered = now - ParseInterval(cmp+2);
2978 } else {
2979 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2980 }
2981 } else if (cmp[0] == '=') {
2982 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2983 } else if (cmp[0] == '>') {
2984 if (cmp[1] == '=') {
2985 discrim->max_registered = now - ParseInterval(cmp+2);
2986 } else {
2987 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2988 }
2989 } else {
2990 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2991 }
2992 } else if (!irccasecmp(argv[i], "seen")) {
2993 discrim->lastseen = now - ParseInterval(argv[++i]);
2994 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2995 discrim->nickmask = argv[++i];
2996 } else if (!irccasecmp(argv[i], "hostmask")) {
2997 i++;
2998 if (!irccasecmp(argv[i], "exact")) {
2999 if (i == argc - 1) {
3000 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3001 goto fail;
3002 }
3003 discrim->hostmask_type = EXACT;
3004 } else if (!irccasecmp(argv[i], "subset")) {
3005 if (i == argc - 1) {
3006 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3007 goto fail;
3008 }
3009 discrim->hostmask_type = SUBSET;
3010 } else if (!irccasecmp(argv[i], "superset")) {
3011 if (i == argc - 1) {
3012 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3013 goto fail;
3014 }
3015 discrim->hostmask_type = SUPERSET;
3016 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3017 if (i == argc - 1) {
3018 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3019 goto fail;
3020 }
3021 discrim->hostmask_type = LASTQUIT;
3022 } else {
3023 i--;
3024 discrim->hostmask_type = SUPERSET;
3025 }
3026 discrim->hostmask = argv[++i];
3027 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3028 if (!irccasecmp(argv[++i], "*")) {
3029 discrim->handlemask = 0;
3030 } else {
3031 discrim->handlemask = argv[i];
3032 }
3033 } else if (!irccasecmp(argv[i], "email")) {
3034 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3035 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3036 goto fail;
3037 } else if (!irccasecmp(argv[++i], "*")) {
3038 discrim->emailmask = 0;
3039 } else {
3040 discrim->emailmask = argv[i];
3041 }
3042 } else if (!irccasecmp(argv[i], "access")) {
3043 const char *cmp = argv[++i];
3044 if (cmp[0] == '<') {
3045 if (discrim->min_level == 0) discrim->min_level = 1;
3046 if (cmp[1] == '=') {
3047 discrim->max_level = strtoul(cmp+2, NULL, 0);
3048 } else {
3049 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3050 }
3051 } else if (cmp[0] == '=') {
3052 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3053 } else if (cmp[0] == '>') {
3054 if (cmp[1] == '=') {
3055 discrim->min_level = strtoul(cmp+2, NULL, 0);
3056 } else {
3057 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3058 }
3059 } else {
3060 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3061 }
3062 } else {
3063 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3064 goto fail;
3065 }
3066 }
3067 return discrim;
3068 fail:
3069 free(discrim);
3070 return NULL;
3071 }
3072
3073 static int
3074 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3075 {
3076 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3077 || (discrim->flags_off & hi->flags)
3078 || (discrim->min_registered > hi->registered)
3079 || (discrim->max_registered < hi->registered)
3080 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3081 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3082 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3083 || (discrim->min_level > hi->opserv_level)
3084 || (discrim->max_level < hi->opserv_level)) {
3085 return 0;
3086 }
3087 if (discrim->hostmask) {
3088 unsigned int i;
3089 for (i=0; i<hi->masks->used; i++) {
3090 const char *mask = hi->masks->list[i];
3091 if ((discrim->hostmask_type == SUBSET)
3092 && (match_ircglobs(discrim->hostmask, mask))) break;
3093 else if ((discrim->hostmask_type == EXACT)
3094 && !irccasecmp(discrim->hostmask, mask)) break;
3095 else if ((discrim->hostmask_type == SUPERSET)
3096 && (match_ircglobs(mask, discrim->hostmask))) break;
3097 else if ((discrim->hostmask_type == LASTQUIT)
3098 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3099 }
3100 if (i==hi->masks->used) return 0;
3101 }
3102 if (discrim->nickmask) {
3103 struct nick_info *nick = hi->nicks;
3104 while (nick) {
3105 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3106 nick = nick->next;
3107 }
3108 if (!nick) return 0;
3109 }
3110 return 1;
3111 }
3112
3113 static unsigned int
3114 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3115 {
3116 dict_iterator_t it, next;
3117 unsigned int matched;
3118
3119 for (it = dict_first(nickserv_handle_dict), matched = 0;
3120 it && (matched < discrim->limit);
3121 it = next) {
3122 next = iter_next(it);
3123 if (nickserv_discrim_match(discrim, iter_data(it))) {
3124 dsf(source, iter_data(it));
3125 matched++;
3126 }
3127 }
3128 return matched;
3129 }
3130
3131 static void
3132 search_print_func(struct userNode *source, struct handle_info *match)
3133 {
3134 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3135 }
3136
3137 static void
3138 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3139 {
3140 }
3141
3142 static void
3143 search_unregister_func (struct userNode *source, struct handle_info *match)
3144 {
3145 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3146 nickserv_unregister_handle(match, source);
3147 }
3148
3149 static int
3150 nickserv_sort_accounts_by_access(const void *a, const void *b)
3151 {
3152 const struct handle_info *hi_a = *(const struct handle_info**)a;
3153 const struct handle_info *hi_b = *(const struct handle_info**)b;
3154 if (hi_a->opserv_level != hi_b->opserv_level)
3155 return hi_b->opserv_level - hi_a->opserv_level;
3156 return irccasecmp(hi_a->handle, hi_b->handle);
3157 }
3158
3159 void
3160 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3161 {
3162 struct handle_info_list hil;
3163 struct helpfile_table tbl;
3164 unsigned int ii;
3165 dict_iterator_t it;
3166 const char **ary;
3167
3168 memset(&hil, 0, sizeof(hil));
3169 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3170 struct handle_info *hi = iter_data(it);
3171 if (hi->opserv_level)
3172 handle_info_list_append(&hil, hi);
3173 }
3174 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3175 tbl.length = hil.used + 1;
3176 tbl.width = 2;
3177 tbl.flags = TABLE_NO_FREE;
3178 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3179 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3180 ary[0] = "Account";
3181 ary[1] = "Level";
3182 for (ii = 0; ii < hil.used; ) {
3183 ary = malloc(tbl.width * sizeof(ary[0]));
3184 ary[0] = hil.list[ii]->handle;
3185 ary[1] = strtab(hil.list[ii]->opserv_level);
3186 tbl.contents[++ii] = ary;
3187 }
3188 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3189 reply("MSG_MATCH_COUNT", hil.used);
3190 for (ii = 0; ii < hil.used; ii++)
3191 free(tbl.contents[ii]);
3192 free(tbl.contents);
3193 free(hil.list);
3194 }
3195
3196 static NICKSERV_FUNC(cmd_search)
3197 {
3198 struct nickserv_discrim *discrim;
3199 discrim_search_func action;
3200 struct svccmd *subcmd;
3201 unsigned int matches;
3202 char buf[MAXLEN];
3203
3204 NICKSERV_MIN_PARMS(3);
3205 sprintf(buf, "search %s", argv[1]);
3206 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3207 if (!irccasecmp(argv[1], "print"))
3208 action = search_print_func;
3209 else if (!irccasecmp(argv[1], "count"))
3210 action = search_count_func;
3211 else if (!irccasecmp(argv[1], "unregister"))
3212 action = search_unregister_func;
3213 else {
3214 reply("NSMSG_INVALID_ACTION", argv[1]);
3215 return 0;
3216 }
3217
3218 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3219 return 0;
3220
3221 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3222 if (!discrim)
3223 return 0;
3224
3225 if (action == search_print_func)
3226 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3227 else if (action == search_count_func)
3228 discrim->limit = INT_MAX;
3229
3230 matches = nickserv_discrim_search(discrim, action, user);
3231
3232 if (matches)
3233 reply("MSG_MATCH_COUNT", matches);
3234 else
3235 reply("MSG_NO_MATCHES");
3236
3237 free(discrim);
3238 return 0;
3239 }
3240
3241 static MODCMD_FUNC(cmd_checkpass)
3242 {
3243 struct handle_info *hi;
3244
3245 NICKSERV_MIN_PARMS(3);
3246 if (!(hi = get_handle_info(argv[1]))) {
3247 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3248 return 0;
3249 }
3250 if (checkpass(argv[2], hi->passwd))
3251 reply("CHECKPASS_YES");
3252 else
3253 reply("CHECKPASS_NO");
3254 argv[2] = "****";
3255 return 1;
3256 }
3257
3258 static void
3259 nickserv_db_read_handle(const char *handle, dict_t obj)
3260 {
3261 const char *str;
3262 struct string_list *masks, *slist;
3263 struct handle_info *hi;
3264 struct userNode *authed_users;
3265 unsigned long int id;
3266 unsigned int ii;
3267 dict_t subdb;
3268
3269 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3270 id = str ? strtoul(str, NULL, 0) : 0;
3271 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3272 if (!str) {
3273 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3274 return;
3275 }
3276 if ((hi = get_handle_info(handle))) {
3277 authed_users = hi->users;
3278 hi->users = NULL;
3279 dict_remove(nickserv_handle_dict, hi->handle);
3280 } else {
3281 authed_users = NULL;
3282 }
3283 hi = register_handle(handle, str, id);
3284 if (authed_users) {
3285 hi->users = authed_users;
3286 while (authed_users) {
3287 authed_users->handle_info = hi;
3288 authed_users = authed_users->next_authed;
3289 }
3290 }
3291 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3292 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3293 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3294 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3295 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3296 hi->language = language_find(str ? str : "C");
3297 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3298 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3299 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3300 if (str)
3301 hi->infoline = strdup(str);
3302 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3303 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3304 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3305 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3306 /* We want to read the nicks even if disable_nicks is set. This is so
3307 * that we don't lose the nick data entirely. */
3308 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3309 if (slist) {
3310 for (ii=0; ii<slist->used; ii++)
3311 register_nick(slist->list[ii], hi);
3312 }
3313 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3314 if (str) {
3315 for (ii=0; str[ii]; ii++)
3316 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3317 }
3318 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3319 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3320 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3321 hi->announcements = str ? str[0] : '?';
3322 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3323 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3324 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3325 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3326 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3327 if (!str)
3328 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3329 if (str)
3330 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3331 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3332 if (str)
3333 nickserv_set_email_addr(hi, str);
3334 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3335 if (str)
3336 hi->epithet = strdup(str);
3337 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3338 if (str)
3339 hi->fakehost = strdup(str);
3340 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3341 if (subdb) {
3342 const char *data, *type, *expires, *cookie_str;
3343 struct handle_cookie *cookie;
3344
3345 cookie = calloc(1, sizeof(*cookie));
3346 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3347 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3348 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3349 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3350 if (!type || !expires || !cookie_str) {
3351 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3352 goto cookie_out;
3353 }
3354 if (!irccasecmp(type, KEY_ACTIVATION))
3355 cookie->type = ACTIVATION;
3356 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3357 cookie->type = PASSWORD_CHANGE;
3358 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3359 cookie->type = EMAIL_CHANGE;
3360 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3361 cookie->type = ALLOWAUTH;
3362 else {
3363 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3364 goto cookie_out;
3365 }
3366 cookie->expires = strtoul(expires, NULL, 0);
3367 if (cookie->expires < now)
3368 goto cookie_out;
3369 if (data)
3370 cookie->data = strdup(data);
3371 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3372 cookie->hi = hi;
3373 cookie_out:
3374 if (cookie->hi)
3375 nickserv_bake_cookie(cookie);
3376 else
3377 nickserv_free_cookie(cookie);
3378 }
3379 }
3380
3381 static int
3382 nickserv_saxdb_read(dict_t db) {
3383 dict_iterator_t it;
3384 struct record_data *rd;
3385
3386 for (it=dict_first(db); it; it=iter_next(it)) {
3387 rd = iter_data(it);
3388 nickserv_db_read_handle(iter_key(it), rd->d.object);
3389 }
3390 return 0;
3391 }
3392
3393 static NICKSERV_FUNC(cmd_mergedb)
3394 {
3395 struct timeval start, stop;
3396 dict_t db;
3397
3398 NICKSERV_MIN_PARMS(2);
3399 gettimeofday(&start, NULL);
3400 if (!(db = parse_database(argv[1]))) {
3401 reply("NSMSG_DB_UNREADABLE", argv[1]);
3402 return 0;
3403 }
3404 nickserv_saxdb_read(db);
3405 free_database(db);
3406 gettimeofday(&stop, NULL);
3407 stop.tv_sec -= start.tv_sec;
3408 stop.tv_usec -= start.tv_usec;
3409 if (stop.tv_usec < 0) {
3410 stop.tv_sec -= 1;
3411 stop.tv_usec += 1000000;
3412 }
3413 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3414 return 1;
3415 }
3416
3417 static void
3418 expire_handles(UNUSED_ARG(void *data))
3419 {
3420 dict_iterator_t it, next;
3421 time_t expiry;
3422 struct handle_info *hi;
3423
3424 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3425 next = iter_next(it);
3426 hi = iter_data(it);
3427 if ((hi->opserv_level > 0)
3428 || hi->users
3429 || HANDLE_FLAGGED(hi, FROZEN)
3430 || HANDLE_FLAGGED(hi, NODELETE)) {
3431 continue;
3432 }
3433 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3434 if ((now - hi->lastseen) > expiry) {
3435 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3436 nickserv_unregister_handle(hi, NULL);
3437 }
3438 }
3439
3440 if (nickserv_conf.handle_expire_frequency)
3441 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3442 }
3443
3444 static void
3445 nickserv_load_dict(const char *fname)
3446 {
3447 FILE *file;
3448 char line[128];
3449 if (!(file = fopen(fname, "r"))) {
3450 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3451 return;
3452 }
3453 while (!feof(file)) {
3454 fgets(line, sizeof(line), file);
3455 if (!line[0])
3456 continue;
3457 if (line[strlen(line)-1] == '\n')
3458 line[strlen(line)-1] = 0;
3459 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3460 }
3461 fclose(file);
3462 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3463 }
3464
3465 static enum reclaim_action
3466 reclaim_action_from_string(const char *str) {
3467 if (!str)
3468 return RECLAIM_NONE;
3469 else if (!irccasecmp(str, "warn"))
3470 return RECLAIM_WARN;
3471 else if (!irccasecmp(str, "svsnick"))
3472 return RECLAIM_SVSNICK;
3473 else if (!irccasecmp(str, "kill"))
3474 return RECLAIM_KILL;
3475 else
3476 return RECLAIM_NONE;
3477 }
3478
3479 static void
3480 nickserv_conf_read(void)
3481 {
3482 dict_t conf_node, child;
3483 const char *str;
3484 dict_iterator_t it;
3485
3486 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3487 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3488 return;
3489 }
3490 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3491 if (!str)
3492 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3493 if (nickserv_conf.valid_handle_regex_set)
3494 regfree(&nickserv_conf.valid_handle_regex);
3495 if (str) {
3496 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3497 nickserv_conf.valid_handle_regex_set = !err;
3498 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3499 } else {
3500 nickserv_conf.valid_handle_regex_set = 0;
3501 }
3502 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3503 if (nickserv_conf.valid_nick_regex_set)
3504 regfree(&nickserv_conf.valid_nick_regex);
3505 if (str) {
3506 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3507 nickserv_conf.valid_nick_regex_set = !err;
3508 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3509 } else {
3510 nickserv_conf.valid_nick_regex_set = 0;
3511 }
3512 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3513 if (!str)
3514 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3515 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3516 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3517 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3518 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3519 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3520 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3521 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3522 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3523 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3524 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3525 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3526 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3527 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3528 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3529 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3530 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3531 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3532 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3533 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3534 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3535 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3536 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3537 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3538 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3539 if (!str)
3540 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3541 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3542 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3543 if (!str)
3544 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3545 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3546 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3547 if (!str)
3548 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3549 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3550 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3551 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3552 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3553 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3554 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3555 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3556 if (!nickserv_conf.disable_nicks) {
3557 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3558 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3559 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3560 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3561 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3562 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3563 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3564 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3565 }
3566 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3567 for (it=dict_first(child); it; it=iter_next(it)) {
3568 const char *key = iter_key(it), *value;
3569 unsigned char flag;
3570 int pos;
3571
3572 if (!strncasecmp(key, "uc_", 3))
3573 flag = toupper(key[3]);
3574 else if (!strncasecmp(key, "lc_", 3))
3575 flag = tolower(key[3]);
3576 else
3577 flag = key[0];
3578
3579 if ((pos = handle_inverse_flags[flag])) {
3580 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3581 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3582 }
3583 }
3584 if (nickserv_conf.weak_password_dict)
3585 dict_delete(nickserv_conf.weak_password_dict);
3586 nickserv_conf.weak_password_dict = dict_new();
3587 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3588 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3589 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3590 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3591 if (str)
3592 nickserv_load_dict(str);
3593 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3594 if (nickserv && str)
3595 NickChange(nickserv, str, 0);
3596 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3597 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3598 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3599 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3600 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3601 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3602 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3603 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3604 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3605 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3606 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3607 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3608 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3609 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3610 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3611 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3612 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3613 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3614 str = conf_get_data("server/network", RECDB_QSTRING);
3615 nickserv_conf.network_name = str ? str : "some IRC network";
3616 if (!nickserv_conf.auth_policer_params) {
3617 nickserv_conf.auth_policer_params = policer_params_new();
3618 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3619 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3620 }
3621 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3622 for (it=dict_first(child); it; it=iter_next(it))
3623 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3624 }
3625
3626 static void
3627 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3628 const char *msg;
3629 char newnick[NICKLEN+1];
3630
3631 assert(user);
3632 assert(ni);
3633 switch (action) {
3634 case RECLAIM_NONE:
3635 /* do nothing */
3636 break;
3637 case RECLAIM_WARN:
3638 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3639 break;
3640 case RECLAIM_SVSNICK:
3641 do {
3642 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3643 } while (GetUserH(newnick));
3644 irc_svsnick(nickserv, user, newnick);
3645 break;
3646 case RECLAIM_KILL:
3647 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3648 irc_kill(nickserv, user, msg);
3649 break;
3650 }
3651 }
3652
3653 static void
3654 nickserv_reclaim_p(void *data) {
3655 struct userNode *user = data;
3656 struct nick_info *ni = get_nick_info(user->nick);
3657 if (ni)
3658 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3659 }
3660
3661 static int
3662 check_user_nick(struct userNode *user) {
3663 struct nick_info *ni;
3664 user->modes &= ~FLAGS_REGNICK;
3665 if (!(ni = get_nick_info(user->nick)))
3666 return 0;
3667 if (user->handle_info == ni->owner) {
3668 user->modes |= FLAGS_REGNICK;
3669 irc_regnick(user);
3670 return 0;
3671 }
3672 if (nickserv_conf.warn_nick_owned)
3673 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3674 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3675 return 0;
3676 if (nickserv_conf.auto_reclaim_delay)
3677 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3678 else
3679 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3680 return 0;
3681 }
3682
3683 int
3684 handle_new_user(struct userNode *user)
3685 {
3686 return check_user_nick(user);
3687 }
3688
3689 void
3690 handle_account(struct userNode *user, const char *stamp)
3691 {
3692 struct handle_info *hi;
3693
3694 #ifdef WITH_PROTOCOL_P10
3695 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3696 #else
3697 hi = dict_find(nickserv_id_dict, stamp, NULL);
3698 #endif
3699
3700 if (hi) {
3701 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3702 return;
3703 }
3704 set_user_handle_info(user, hi, 0);
3705 } else {
3706 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3707 }
3708 }
3709
3710 void
3711 handle_nick_change(struct userNode *user, const char *old_nick)
3712 {
3713 struct handle_info *hi;
3714
3715 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3716 dict_remove(nickserv_allow_auth_dict, old_nick);
3717 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3718 }
3719 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3720 check_user_nick(user);
3721 }
3722
3723 void
3724 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3725 {
3726 dict_remove(nickserv_allow_auth_dict, user->nick);
3727 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3728 set_user_handle_info(user, NULL, 0);
3729 }
3730
3731 static struct modcmd *
3732 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3733 {
3734 if (min_level > 0) {
3735 char buf[16];
3736 sprintf(buf, "%u", min_level);
3737 if (must_be_qualified) {
3738 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3739 } else {
3740 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3741 }
3742 } else if (min_level == 0) {
3743 if (must_be_qualified) {
3744 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3745 } else {
3746 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3747 }
3748 } else {
3749 if (must_be_qualified) {
3750 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3751 } else {
3752 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3753 }
3754 }
3755 }
3756
3757 static void
3758 nickserv_db_cleanup(void)
3759 {
3760 unreg_del_user_func(nickserv_remove_user);
3761 userList_clean(&curr_helpers);
3762 policer_params_delete(nickserv_conf.auth_policer_params);
3763 dict_delete(nickserv_handle_dict);
3764 dict_delete(nickserv_nick_dict);
3765 dict_delete(nickserv_opt_dict);
3766 dict_delete(nickserv_allow_auth_dict);
3767 dict_delete(nickserv_email_dict);
3768 dict_delete(nickserv_id_dict);
3769 dict_delete(nickserv_conf.weak_password_dict);
3770 free(auth_func_list);
3771 free(unreg_func_list);
3772 free(rf_list);
3773 free(allowauth_func_list);
3774 free(handle_merge_func_list);
3775 free(failpw_func_list);
3776 if (nickserv_conf.valid_handle_regex_set)
3777 regfree(&nickserv_conf.valid_handle_regex);
3778 if (nickserv_conf.valid_nick_regex_set)
3779 regfree(&nickserv_conf.valid_nick_regex);
3780 }
3781
3782 void
3783 init_nickserv(const char *nick)
3784 {
3785 unsigned int i;
3786 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3787 reg_new_user_func(handle_new_user);
3788 reg_nick_change_func(handle_nick_change);
3789 reg_del_user_func(nickserv_remove_user);
3790 reg_account_func(handle_account);
3791
3792 /* set up handle_inverse_flags */
3793 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3794 for (i=0; handle_flags[i]; i++) {
3795 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3796 flag_access_levels[i] = 0;
3797 }
3798
3799 conf_register_reload(nickserv_conf_read);
3800 nickserv_opt_dict = dict_new();
3801 nickserv_email_dict = dict_new();
3802 dict_set_free_keys(nickserv_email_dict, free);
3803 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3804
3805 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3806 /* Removed qualified_host as default requirement for AUTH, REGISTER, PASS, etc. nets
3807 * can enable it per command using modcmd. (its a shitty default IMO, and now in 1.3
3808 * a big pain to disable since its nolonger in the config file. ) -Rubin
3809 */
3810 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+loghostmask", NULL);
3811 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3812 nickserv_define_func("REGISTER", cmd_register, -1, 0, 0);
3813 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3814 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 0);
3815 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3816 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3817 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3818 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3819 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3820 nickserv_define_func("PASS", cmd_pass, -1, 1, 0);
3821 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3822 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3823 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3824 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3825 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3826 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3827 nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3828 if (!nickserv_conf.disable_nicks) {
3829 /* nick management commands */
3830 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3831 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3832 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3833 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3834 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3835 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3836 }
3837 if (nickserv_conf.email_enabled) {
3838 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3839 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 0);
3840 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 0);
3841 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3842 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3843 }
3844 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3845 /* miscellaneous commands */
3846 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3847 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3848 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3849 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3850 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3851 /* other options */
3852 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3853 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3854 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3855 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3856 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3857 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3858 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3859 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3860 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3861 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3862 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3863 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3864 if (nickserv_conf.titlehost_suffix) {
3865 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
3866 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
3867 }
3868 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3869 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3870 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3871
3872 nickserv_handle_dict = dict_new();
3873 dict_set_free_keys(nickserv_handle_dict, free);
3874 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3875
3876 nickserv_id_dict = dict_new();
3877 dict_set_free_keys(nickserv_id_dict, free);
3878
3879 nickserv_nick_dict = dict_new();
3880 dict_set_free_data(nickserv_nick_dict, free);
3881
3882 nickserv_allow_auth_dict = dict_new();
3883
3884 userList_init(&curr_helpers);
3885
3886 if (nick) {
3887 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
3888 nickserv = AddService(nick, modes ? modes : NULL, "Nick Services", NULL);
3889 nickserv_service = service_register(nickserv);
3890 }
3891 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3892 reg_exit_func(nickserv_db_cleanup);
3893 if(nickserv_conf.handle_expire_frequency)
3894 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3895 message_register_table(msgtab);
3896 }