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