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