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