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