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