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