fixed coding fails in the last 2 commits
[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     struct devnull_class *devnull_c;
2655
2656     if (argc > 1) {
2657         if (!override) {
2658             send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2659             return 0;
2660         }
2661         if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2662             hi->devnull_id = 0;
2663         } else {
2664             devnull_name = unsplit_string(argv+1, argc-1, NULL);
2665             devnull_c = devnull_find_name(devnull_name);
2666             if(devnull_c)
2667                 hi->devnull_id = devnull_c->id;
2668         }
2669     }
2670
2671     if(hi->devnull_id) {
2672         devnull_c = devnull_find_id(hi->devnull_id);
2673         if(devnull_c)
2674             devnull_name = devnull_c->name;
2675         else {
2676             devnull_name = user_find_message(user, "MSG_NONE");
2677             hi->devnull_id = 0;
2678         }
2679     } else
2680         devnull_name = user_find_message(user, "MSG_NONE");
2681     send_message(user, nickserv, "NSMSG_SET_DEVNULL", devnull_name);
2682     return 1;
2683 }
2684
2685 void nickserv_devnull_delete(unsigned int devnull_id) {
2686     dict_iterator_t it;
2687     struct handle_info *hi;
2688
2689     for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2690         hi = iter_data(it);
2691         if (hi->devnull_id == devnull_id) {
2692             hi->devnull_id = 0;
2693         }
2694     }
2695 }
2696
2697 static OPTION_FUNC(opt_website)
2698 {
2699     const char *website;
2700     
2701     if (argc > 1) {
2702         if (!HANDLE_FLAGGED(user->handle_info, BOT)) {
2703             send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2704             return 0;
2705         }
2706         if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2707             free(hi->website);
2708             hi->website = NULL;
2709         } else {
2710             website = unsplit_string(argv+1, argc-1, NULL);
2711             hi->website = strdup(website);
2712         }
2713     }
2714     if (HANDLE_FLAGGED(user->handle_info, BOT)) {
2715         website = hi->website ? hi->website : user_find_message(user, "MSG_NONE");
2716         send_message(user, nickserv, "NSMSG_SET_WEBSITE", website);
2717     }
2718     return 1;
2719 }
2720
2721 static OPTION_FUNC(opt_width)
2722 {
2723     if (argc > 1)
2724         hi->screen_width = strtoul(argv[1], NULL, 0);
2725
2726     if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2727         hi->screen_width = MIN_LINE_SIZE;
2728     else if (hi->screen_width > MAX_LINE_SIZE)
2729         hi->screen_width = MAX_LINE_SIZE;
2730
2731     send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2732     return 1;
2733 }
2734
2735 static OPTION_FUNC(opt_tablewidth)
2736 {
2737     if (argc > 1)
2738         hi->table_width = strtoul(argv[1], NULL, 0);
2739
2740     if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2741         hi->table_width = MIN_LINE_SIZE;
2742     else if (hi->screen_width > MAX_LINE_SIZE)
2743         hi->table_width = MAX_LINE_SIZE;
2744
2745     send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2746     return 1;
2747 }
2748
2749 static OPTION_FUNC(opt_color)
2750 {
2751     if (argc > 1) {
2752         if (enabled_string(argv[1]))
2753             HANDLE_SET_FLAG(hi, MIRC_COLOR);
2754         else if (disabled_string(argv[1]))
2755             HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2756         else {
2757             send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2758             return 0;
2759         }
2760     }
2761
2762     send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2763     return 1;
2764 }
2765
2766 static OPTION_FUNC(opt_privmsg)
2767 {
2768     if (argc > 1) {
2769         if (enabled_string(argv[1]))
2770             HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2771         else if (disabled_string(argv[1]))
2772             HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2773         else {
2774             send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2775             return 0;
2776         }
2777     }
2778
2779     send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2780     return 1;
2781 }
2782
2783 static OPTION_FUNC(opt_autohide)
2784 {
2785     if (argc > 1) {
2786         if (enabled_string(argv[1]))
2787             HANDLE_SET_FLAG(hi, AUTOHIDE);
2788         else if (disabled_string(argv[1]))
2789             HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
2790         else {
2791             send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2792             return 0;
2793         }
2794     }
2795
2796     send_message(user, nickserv, "NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
2797     return 1;
2798 }
2799
2800 static OPTION_FUNC(opt_style)
2801 {
2802     char *style;
2803
2804     if (argc > 1) {
2805         if (!irccasecmp(argv[1], "Zoot"))
2806             hi->userlist_style = HI_STYLE_ZOOT;
2807         else if (!irccasecmp(argv[1], "def"))
2808             hi->userlist_style = HI_STYLE_DEF;
2809     }
2810
2811     switch (hi->userlist_style) {
2812     case HI_STYLE_DEF:
2813         style = "def";
2814         break;
2815     case HI_STYLE_ZOOT:
2816     default:
2817         style = "Zoot";
2818     }
2819
2820     send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2821     return 1;
2822 }
2823
2824 static OPTION_FUNC(opt_password)
2825 {
2826     if (!override) {
2827         send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2828         return 0;
2829     }
2830
2831     if (argc > 1)
2832         cryptpass(argv[1], hi->passwd);
2833
2834     send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2835     argv[1] = "****";
2836     
2837     return 1;
2838 }
2839
2840 static OPTION_FUNC(opt_flags)
2841 {
2842     char flags[33];
2843     unsigned int ii, flen;
2844
2845     if (!override) {
2846         send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2847         return 0;
2848     }
2849
2850     if (argc > 1)
2851         nickserv_apply_flags(user, hi, argv[1]);
2852
2853     for (ii = flen = 0; handle_flags[ii]; ii++)
2854         if (hi->flags & (1 << ii))
2855             flags[flen++] = handle_flags[ii];
2856     flags[flen] = '\0';
2857     if (hi->flags)
2858         send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2859     else
2860         send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2861     return 1;
2862 }
2863
2864 static OPTION_FUNC(opt_email)
2865 {
2866     if (argc > 1) {
2867         const char *str;
2868         if (!is_valid_email_addr(argv[1])) {
2869             send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2870             return 0;
2871         }
2872         if ((str = mail_prohibited_address(argv[1]))) {
2873             send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2874             return 0;
2875         }
2876         if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2877             send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2878         else if (!override)
2879                 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2880         else {
2881             nickserv_set_email_addr(hi, argv[1]);
2882             if (hi->cookie)
2883                 nickserv_eat_cookie(hi->cookie);
2884             send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2885         }
2886     } else
2887         send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2888     return 1;
2889 }
2890
2891 static OPTION_FUNC(opt_maxlogins)
2892 {
2893     unsigned char maxlogins;
2894     if (argc > 1) {
2895         maxlogins = strtoul(argv[1], NULL, 0);
2896         if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2897             send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2898             return 0;
2899         }
2900         hi->maxlogins = maxlogins;
2901     }
2902     maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2903     send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2904     return 1;
2905 }
2906
2907 static OPTION_FUNC(opt_language)
2908 {
2909     struct language *lang;
2910     if (argc > 1) {
2911         lang = language_find(argv[1]);
2912         if (irccasecmp(lang->name, argv[1]))
2913             send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2914         hi->language = lang;
2915     }
2916     send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2917     return 1;
2918 }
2919
2920 static OPTION_FUNC(opt_karma)
2921 {
2922     if (!override) {
2923         send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2924         return 0;
2925     }
2926
2927     if (argc > 1) {
2928         if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2929             hi->karma += strtoul(argv[1] + 1, NULL, 10);
2930         } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2931             hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2932         } else {
2933             send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2934         }
2935     }
2936
2937     send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2938     return 1;
2939 }
2940
2941 int
2942 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2943     if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2944         return 0;
2945     if ((user->handle_info->opserv_level < target->opserv_level)
2946         || ((user->handle_info->opserv_level == target->opserv_level)
2947             && (user->handle_info->opserv_level < 1000))) {
2948         send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2949         return 0;
2950     }
2951     if ((user->handle_info->opserv_level < new_level)
2952         || ((user->handle_info->opserv_level == new_level)
2953             && (user->handle_info->opserv_level < 1000))) {
2954         send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2955         return 0;
2956     }
2957     if (user->handle_info == target) {
2958         send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2959         return 0;
2960     }
2961     if (target->opserv_level == new_level)
2962         return 0;
2963     log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2964         user->handle_info->handle, target->handle, new_level, target->opserv_level);
2965     target->opserv_level = new_level;
2966     return 1;
2967 }
2968
2969 int
2970 oper_try_set_staff_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2971     if (!oper_has_access(user, bot, nickserv_conf.modstaff_level, 0))
2972         return 0;
2973     if ((user->handle_info->opserv_level < target->opserv_level)
2974         || ((user->handle_info->opserv_level == target->opserv_level)
2975             && (user->handle_info->opserv_level < 1000))) {
2976         send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2977         return 0;
2978     }
2979     if (target->staff_level == new_level)
2980         return 0;
2981     log_module(NS_LOG, LOG_INFO, "Account %s setting staff level for account %s to %d (from %d).",
2982         user->handle_info->handle, target->handle, new_level, target->staff_level);
2983     target->staff_level = new_level;
2984     return 1;
2985 }
2986
2987 static OPTION_FUNC(opt_level)
2988 {
2989     int res;
2990
2991     if (!override) {
2992         send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2993         return 0;
2994     }
2995
2996     res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2997     send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2998     return res;
2999 }
3000
3001 static OPTION_FUNC(opt_staff_level)
3002 {
3003     int res;
3004
3005     if (!override) {
3006         send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3007         return 0;
3008     }
3009
3010     res = (argc > 1) ? oper_try_set_staff_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
3011     send_message(user, nickserv, "NSMSG_SET_STAFFLEVEL", hi->staff_level);
3012     return res;
3013 }
3014
3015 static OPTION_FUNC(opt_epithet)
3016 {
3017     if (!override) {
3018         send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3019         return 0;
3020     }
3021
3022     if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
3023         char *epithet = unsplit_string(argv+1, argc-1, NULL);
3024         if (hi->epithet)
3025             free(hi->epithet);
3026         if ((epithet[0] == '*') && !epithet[1])
3027             hi->epithet = NULL;
3028         else
3029             hi->epithet = strdup(epithet);
3030     }
3031
3032     if (hi->epithet)
3033         send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
3034     else
3035         send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
3036     return 1;
3037 }
3038
3039 static OPTION_FUNC(opt_title)
3040 {
3041     const char *title;
3042
3043     if (!override) {
3044         send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3045         return 0;
3046     }
3047
3048     if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
3049         title = argv[1];
3050         if (strchr(title, '.')) {
3051             send_message(user, nickserv, "NSMSG_TITLE_INVALID");
3052             return 0;
3053         }
3054         if ((strlen(user->handle_info->handle) + strlen(title) +
3055              strlen(titlehost_suffix) + 2) > HOSTLEN) {
3056             send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
3057             return 0;
3058         }
3059
3060         free(hi->fakehost);
3061         if (!strcmp(title, "*")) {
3062             hi->fakehost = NULL;
3063         } else {
3064             hi->fakehost = malloc(strlen(title)+2);
3065             hi->fakehost[0] = '.';
3066             strcpy(hi->fakehost+1, title);
3067         }
3068         apply_fakehost(hi, NULL);
3069     } else if (hi->fakehost && (hi->fakehost[0] == '.'))
3070         title = hi->fakehost + 1;
3071     else
3072         title = NULL;
3073     if (!title)
3074         title = user_find_message(user, "MSG_NONE");
3075     send_message(user, nickserv, "NSMSG_SET_TITLE", title);
3076     return 1;
3077 }
3078
3079 static OPTION_FUNC(opt_fakehost)
3080 {
3081     char mask[USERLEN + HOSTLEN + 2];
3082     char *host, *ident;
3083
3084     if (!override) {
3085         send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3086         return 0;
3087     }
3088
3089     if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
3090         if(strlen(argv[1]) >= sizeof(mask)) {
3091             send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
3092             return 0;
3093         }
3094
3095         safestrncpy(mask, argv[1], sizeof(mask));
3096
3097         if ((host = strrchr(mask, '@')) && host != mask) {
3098             /* If ident@host was used and the user doesn't have access to set idents, do not change anything. */
3099             if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3100                 host = NULL;
3101                 ident = NULL;
3102             } else {
3103                 ident = mask;
3104                 *host++ = '\0';
3105             }
3106         } else {
3107             ident = NULL;
3108             host = mask;
3109         }
3110
3111         if (ident && strlen(ident) > USERLEN) {
3112             send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3113             return 0;
3114         }
3115
3116         if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
3117             send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
3118             return 0;
3119         }
3120
3121         if (host && host[0]) {
3122             free(hi->fakehost);
3123             if (!strcmp(host, "*"))
3124                 hi->fakehost = NULL;
3125             else
3126                 hi->fakehost = strdup(host);
3127             host = hi->fakehost;
3128         }
3129         else
3130             host = generate_fakehost(hi);
3131
3132         if (ident) {
3133             free(hi->fakeident);
3134             if (!strcmp(ident, "*"))
3135                 hi->fakeident = NULL;
3136             else
3137                 hi->fakeident = strdup(ident);
3138             ident = hi->fakeident;
3139         }
3140         else
3141             ident = generate_fakeident(hi, NULL);
3142
3143         apply_fakehost(hi, NULL);
3144     } else {
3145         host = generate_fakehost(hi);
3146         ident = generate_fakeident(hi, NULL);
3147     }
3148     if (!host)
3149         host = (char *) user_find_message(user, "MSG_NONE");
3150     if(ident)
3151         send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
3152     else
3153         send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
3154     return 1;
3155 }
3156
3157 static OPTION_FUNC(opt_fakeident)
3158 {
3159     const char *ident;
3160
3161     if (!override) {
3162         send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3163         return 0;
3164     }
3165
3166     if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3167         ident = argv[1];
3168         if (strlen(ident) > USERLEN) {
3169             send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3170             return 0;
3171         }
3172         free(hi->fakeident);
3173         if (!strcmp(ident, "*"))
3174             hi->fakeident = NULL;
3175         else
3176             hi->fakeident = strdup(ident);
3177         ident = hi->fakeident;
3178         apply_fakehost(hi, NULL);
3179     } else
3180         ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
3181     if (!ident)
3182         ident = user_find_message(user, "MSG_NONE");
3183     send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
3184     return 1;
3185 }
3186
3187 static NICKSERV_FUNC(cmd_reclaim)
3188 {
3189     struct nick_info *ni;
3190     struct userNode *victim;
3191
3192     NICKSERV_MIN_PARMS(2);
3193     ni = dict_find(nickserv_nick_dict, argv[1], 0);
3194     if (!ni) {
3195         reply("NSMSG_UNKNOWN_NICK", argv[1]);
3196         return 0;
3197     }
3198     if (ni->owner != user->handle_info) {
3199         reply("NSMSG_NOT_YOUR_NICK", ni->nick);
3200         return 0;
3201     }
3202     victim = GetUserH(ni->nick);
3203     if (!victim) {
3204         reply("MSG_NICK_UNKNOWN", ni->nick);
3205         return 0;
3206     }
3207     if (victim == user) {
3208         reply("NSMSG_NICK_USER_YOU");
3209         return 0;
3210     }
3211     nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
3212     switch (nickserv_conf.reclaim_action) {
3213     case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
3214     case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
3215     case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
3216     case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
3217     }
3218     return 1;
3219 }
3220
3221 static NICKSERV_FUNC(cmd_unregnick)
3222 {
3223     const char *nick;
3224     struct handle_info *hi;
3225     struct nick_info *ni;
3226
3227     hi = user->handle_info;
3228     nick = (argc < 2) ? user->nick : (const char*)argv[1];
3229     ni = dict_find(nickserv_nick_dict, nick, NULL);
3230     if (!ni) {
3231         reply("NSMSG_UNKNOWN_NICK", nick);
3232         return 0;
3233     }
3234     if (hi != ni->owner) {
3235         reply("NSMSG_NOT_YOUR_NICK", nick);
3236         return 0;
3237     }
3238     reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3239     delete_nick(ni);
3240     return 1;
3241 }
3242
3243 static NICKSERV_FUNC(cmd_ounregnick)
3244 {
3245     struct nick_info *ni;
3246
3247     NICKSERV_MIN_PARMS(2);
3248     if (!(ni = get_nick_info(argv[1]))) {
3249         reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
3250         return 0;
3251     }
3252     if (!oper_outranks(user, ni->owner))
3253         return 0;
3254     reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3255     delete_nick(ni);
3256     return 1;
3257 }
3258
3259 static NICKSERV_FUNC(cmd_unregister)
3260 {
3261     struct handle_info *hi;
3262     char *passwd;
3263
3264     NICKSERV_MIN_PARMS(2);
3265     hi = user->handle_info;
3266     passwd = argv[1];
3267     argv[1] = "****";
3268     if (checkpass(passwd, hi->passwd)) {
3269         nickserv_unregister_handle(hi, user);
3270         return 1;
3271     } else {
3272         log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3273         reply("NSMSG_PASSWORD_INVALID");
3274         return 0;
3275     }
3276 }
3277
3278 static NICKSERV_FUNC(cmd_ounregister)
3279 {
3280     struct handle_info *hi;
3281     char reason[MAXLEN];
3282     int force;
3283
3284     NICKSERV_MIN_PARMS(2);
3285     if (!(hi = get_victim_oper(user, argv[1])))
3286         return 0;
3287
3288     if (HANDLE_FLAGGED(hi, NODELETE)) {
3289         reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
3290         return 0;
3291     }
3292
3293     force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
3294     if (!force &&
3295         ((hi->flags & nickserv_conf.ounregister_flags)
3296          || hi->users
3297          || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
3298         reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
3299         return 0;
3300     }
3301
3302     snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
3303     global_message(MESSAGE_RECIPIENT_STAFF, reason);
3304     nickserv_unregister_handle(hi, user);
3305     return 1;
3306 }
3307
3308 static NICKSERV_FUNC(cmd_status)
3309 {
3310     if (nickserv_conf.disable_nicks) {
3311         reply("NSMSG_GLOBAL_STATS_NONICK",
3312                         dict_size(nickserv_handle_dict));
3313     } else {
3314         if (user->handle_info) {
3315             int cnt=0;
3316             struct nick_info *ni;
3317             for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
3318             reply("NSMSG_HANDLE_STATS", cnt);
3319         } else {
3320             reply("NSMSG_HANDLE_NONE");
3321         }
3322         reply("NSMSG_GLOBAL_STATS",
3323               dict_size(nickserv_handle_dict),
3324               dict_size(nickserv_nick_dict));
3325     }
3326     return 1;
3327 }
3328
3329 static NICKSERV_FUNC(cmd_ghost)
3330 {
3331     struct userNode *target;
3332     char reason[MAXLEN];
3333
3334     NICKSERV_MIN_PARMS(2);
3335     if (!(target = GetUserH(argv[1]))) {
3336         reply("MSG_NICK_UNKNOWN", argv[1]);
3337         return 0;
3338     }
3339     if (target == user) {
3340         reply("NSMSG_CANNOT_GHOST_SELF");
3341         return 0;
3342     }
3343     if (!target->handle_info || (target->handle_info != user->handle_info)) {
3344         reply("NSMSG_CANNOT_GHOST_USER", target->nick);
3345         return 0;
3346     }
3347     snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
3348     DelUser(target, nickserv, 1, reason);
3349     reply("NSMSG_GHOST_KILLED", argv[1]);
3350     return 1;
3351 }
3352
3353 static NICKSERV_FUNC(cmd_vacation)
3354 {
3355     HANDLE_SET_FLAG(user->handle_info, FROZEN);
3356     reply("NSMSG_ON_VACATION");
3357     return 1;
3358 }
3359
3360 static NICKSERV_FUNC(cmd_addnote)
3361 {
3362     struct handle_info *hi;
3363     unsigned long duration;
3364     char text[MAXLEN];
3365     unsigned int id;
3366     struct handle_note *prev;
3367     struct handle_note *note;
3368
3369     /* Parse parameters and figure out values for note's fields. */
3370     NICKSERV_MIN_PARMS(4);
3371     hi = get_victim_oper(user, argv[1]);
3372     if (!hi)
3373         return 0;
3374     if(!strcmp(argv[2], "0"))
3375         duration = 0;
3376     else if(!(duration = ParseInterval(argv[2])))
3377     {
3378         reply("MSG_INVALID_DURATION", argv[2]);
3379         return 0;
3380     }
3381     if (duration > 2*365*86400) {
3382         reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
3383         return 0;
3384     }
3385     unsplit_string(argv + 3, argc - 3, text);
3386     WALK_NOTES(hi, prev, note) {}
3387     id = prev ? (prev->id + 1) : 1;
3388
3389     /* Create the new note structure. */
3390     note = calloc(1, sizeof(*note) + strlen(text));
3391     note->next = NULL;
3392     note->expires = duration ? (now + duration) : 0;
3393     note->set = now;
3394     note->id = id;
3395     safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
3396     strcpy(note->note, text);
3397     if (prev)
3398         prev->next = note;
3399     else
3400         hi->notes = note;
3401     reply("NSMSG_NOTE_ADDED", id, hi->handle);
3402     return 1;
3403 }
3404
3405 static NICKSERV_FUNC(cmd_delnote)
3406 {
3407     struct handle_info *hi;
3408     struct handle_note *prev;
3409     struct handle_note *note;
3410     int id;
3411
3412     NICKSERV_MIN_PARMS(3);
3413     hi = get_victim_oper(user, argv[1]);
3414     if (!hi)
3415         return 0;
3416     id = strtoul(argv[2], NULL, 10);
3417     WALK_NOTES(hi, prev, note) {
3418         if (id == note->id) {
3419             if (prev)
3420                 prev->next = note->next;
3421             else
3422                 hi->notes = note->next;
3423             free(note);
3424             reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3425             return 1;
3426         }
3427     }
3428     reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3429     return 0;
3430 }
3431
3432 static int
3433 nickserv_saxdb_write(struct saxdb_context *ctx) {
3434     dict_iterator_t it;
3435     struct handle_info *hi;
3436     char flags[33];
3437
3438     for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3439         hi = iter_data(it);
3440         assert(hi->id != 0);
3441         saxdb_start_record(ctx, iter_key(it), 0);
3442         if (hi->cookie) {
3443             struct handle_cookie *cookie = hi->cookie;
3444             char *type;
3445
3446             switch (cookie->type) {
3447             case ACTIVATION: type = KEY_ACTIVATION; break;
3448             case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3449             case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3450             case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3451             default: type = NULL; break;
3452             }
3453             if (type) {
3454                 saxdb_start_record(ctx, KEY_COOKIE, 0);
3455                 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3456                 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3457                 if (cookie->data)
3458                     saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3459                 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3460                 saxdb_end_record(ctx);
3461             }
3462         }
3463         if (hi->notes) {
3464             struct handle_note *prev, *note;
3465             saxdb_start_record(ctx, KEY_NOTES, 0);
3466             WALK_NOTES(hi, prev, note) {
3467                 snprintf(flags, sizeof(flags), "%d", note->id);
3468                 saxdb_start_record(ctx, flags, 0);
3469                 if (note->expires)
3470                     saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3471                 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3472                 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3473                 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3474                 saxdb_end_record(ctx);
3475             }
3476             saxdb_end_record(ctx);
3477         }
3478         if (hi->email_addr)
3479             saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3480         if (hi->epithet)
3481             saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3482         if (hi->fakehost)
3483             saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3484         if (hi->fakeident)
3485             saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3486         if (hi->flags) {
3487             int ii, flen;
3488
3489             for (ii=flen=0; handle_flags[ii]; ++ii)
3490                 if (hi->flags & (1 << ii))
3491                     flags[flen++] = handle_flags[ii];
3492             flags[flen] = 0;
3493             saxdb_write_string(ctx, KEY_FLAGS, flags);
3494         }
3495         saxdb_write_int(ctx, KEY_ID, hi->id);
3496         if (hi->infoline)
3497             saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3498         if (hi->devnull_id)
3499             saxdb_write_int(ctx, KEY_DEVNULL, hi->devnull_id);
3500         if (hi->website)
3501             saxdb_write_string(ctx, KEY_WEBSITE, hi->website);
3502         if (hi->last_quit_host[0])
3503             saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3504         saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3505         if (hi->karma != 0)
3506             saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3507         if (hi->masks->used)
3508             saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3509         if (hi->maxlogins)
3510             saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3511         if (hi->nicks) {
3512             struct string_list *slist;
3513             struct nick_info *ni;
3514
3515             slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3516             for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3517             saxdb_write_string_list(ctx, KEY_NICKS, slist);
3518             free(slist->list);
3519             free(slist);
3520         }
3521         if (hi->opserv_level)
3522             saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3523         if (hi->staff_level)
3524             saxdb_write_int(ctx, KEY_STAFF_LEVEL, hi->staff_level);
3525         if (hi->language != lang_C)
3526             saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3527         saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3528         saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3529         if (hi->screen_width)
3530             saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3531         if (hi->table_width)
3532             saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3533         flags[0] = hi->userlist_style;
3534         flags[1] = 0;
3535         saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3536         if(hi->authlog) {
3537             saxdb_start_record(ctx, KEY_AUTHLOG, 0);
3538             struct authlogEntry *authlog;
3539             int i = 0;
3540             for(authlog = hi->authlog; authlog; authlog = authlog->next) {
3541                 saxdb_start_record(ctx, strtab(++i), 0);
3542                 saxdb_write_int(ctx, KEY_AUTHLOG_LOGIN_TIME, authlog->login_time);
3543                 saxdb_write_int(ctx, KEY_AUTHLOG_LOGOUT_TIME, authlog->logout_time);
3544                 saxdb_write_string(ctx, KEY_AUTHLOG_HOSTMASK, authlog->hostmask);
3545                 if(authlog->quit_reason)
3546                     saxdb_write_string(ctx, KEY_AUTHLOG_QUIT_REASON, authlog->quit_reason);
3547                 saxdb_end_record(ctx);
3548             }
3549             saxdb_end_record(ctx); //END KEY_AUTHLOG
3550         }
3551         saxdb_end_record(ctx);
3552     }
3553     return 0;
3554 }
3555
3556 static handle_merge_func_t *handle_merge_func_list;
3557 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3558
3559 void
3560 reg_handle_merge_func(handle_merge_func_t func)
3561 {
3562     if (handle_merge_func_used == handle_merge_func_size) {
3563         if (handle_merge_func_size) {
3564             handle_merge_func_size <<= 1;
3565             handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3566         } else {
3567             handle_merge_func_size = 8;
3568             handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3569         }
3570     }
3571     handle_merge_func_list[handle_merge_func_used++] = func;
3572 }
3573
3574 static NICKSERV_FUNC(cmd_merge)
3575 {
3576     struct handle_info *hi_from, *hi_to;
3577     struct userNode *last_user;
3578     struct userData *cList, *cListNext;
3579     unsigned int ii, jj, n;
3580     char buffer[MAXLEN];
3581
3582     NICKSERV_MIN_PARMS(3);
3583
3584     if (!(hi_from = get_victim_oper(user, argv[1])))
3585         return 0;
3586     if (!(hi_to = get_victim_oper(user, argv[2])))
3587         return 0;
3588     if (hi_to == hi_from) {
3589         reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3590         return 0;
3591     }
3592
3593     for (n=0; n<handle_merge_func_used; n++)
3594         handle_merge_func_list[n](user, hi_to, hi_from);
3595
3596     /* Append "from" handle's nicks to "to" handle's nick list. */
3597     if (hi_to->nicks) {
3598         struct nick_info *last_ni;
3599         for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3600         last_ni->next = hi_from->nicks;
3601     }
3602     while (hi_from->nicks) {
3603         hi_from->nicks->owner = hi_to;
3604         hi_from->nicks = hi_from->nicks->next;
3605     }
3606
3607     /* Merge the hostmasks. */
3608     for (ii=0; ii<hi_from->masks->used; ii++) {
3609         char *mask = hi_from->masks->list[ii];
3610         for (jj=0; jj<hi_to->masks->used; jj++)
3611             if (match_ircglobs(hi_to->masks->list[jj], mask))
3612                 break;
3613         if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3614             string_list_append(hi_to->masks, strdup(mask));
3615     }
3616
3617     /* Merge the lists of authed users. */
3618     if (hi_to->users) {
3619         for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3620         last_user->next_authed = hi_from->users;
3621     } else {
3622         hi_to->users = hi_from->users;
3623     }
3624     /* Repoint the old "from" handle's users. */
3625     for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3626         last_user->handle_info = hi_to;
3627     }
3628     hi_from->users = NULL;
3629
3630     /* Merge channel userlists. */
3631     for (cList=hi_from->channels; cList; cList=cListNext) {
3632         struct userData *cList2;
3633         cListNext = cList->u_next;
3634         for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3635             if (cList->channel == cList2->channel)
3636                 break;
3637         if (cList2 && (cList2->access >= cList->access)) {
3638             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);
3639             /* keep cList2 in hi_to; remove cList from hi_from */
3640             del_channel_user(cList, 1);
3641         } else {
3642             if (cList2) {
3643                 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);
3644                 /* remove the lower-ranking cList2 from hi_to */
3645                 del_channel_user(cList2, 1);
3646             } else {
3647                 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3648             }
3649             /* cList needs to be moved from hi_from to hi_to */
3650             cList->handle = hi_to;
3651             /* Remove from linked list for hi_from */
3652             assert(!cList->u_prev);
3653             hi_from->channels = cList->u_next;
3654             if (cList->u_next)
3655                 cList->u_next->u_prev = cList->u_prev;
3656             /* Add to linked list for hi_to */
3657             cList->u_prev = NULL;
3658             cList->u_next = hi_to->channels;
3659             if (hi_to->channels)
3660                 hi_to->channels->u_prev = cList;
3661             hi_to->channels = cList;
3662         }
3663     }
3664
3665     /* Do they get an OpServ level promotion? */
3666     if (hi_from->opserv_level > hi_to->opserv_level)
3667         hi_to->opserv_level = hi_from->opserv_level;
3668
3669     /* Do they get a staff level promotion? */
3670     if (hi_from->staff_level > hi_to->staff_level)
3671         hi_to->staff_level = hi_from->staff_level;
3672
3673     /* What about last seen time? */
3674     if (hi_from->lastseen > hi_to->lastseen)
3675         hi_to->lastseen = hi_from->lastseen;
3676
3677     /* New karma is the sum of the two original karmas. */
3678     hi_to->karma += hi_from->karma;
3679
3680     /* Does a fakehost carry over?  (This intentionally doesn't set it
3681      * for users previously attached to hi_to.  They'll just have to
3682      * reconnect.)
3683      */
3684     if (hi_from->fakehost && !hi_to->fakehost)
3685         hi_to->fakehost = strdup(hi_from->fakehost);
3686     if (hi_from->fakeident && !hi_to->fakeident)
3687         hi_to->fakeident = strdup(hi_from->fakeident);
3688
3689     /* Notify of success. */
3690     sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3691     reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3692     global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3693
3694     /* Unregister the "from" handle. */
3695     nickserv_unregister_handle(hi_from, NULL);
3696
3697     return 1;
3698 }
3699
3700 #define NICKSERV_DISCRIM_FIELDS_AUTH     0x01
3701 #define NICKSERV_DISCRIM_FIELDS_EMAIL    0x02
3702 #define NICKSERV_DISCRIM_FIELDS_SEEN     0x04
3703 #define NICKSERV_DISCRIM_FIELDS_ACCESS   0x08
3704 #define NICKSERV_DISCRIM_FIELDS_FAKEHOST 0x10
3705 #define NICKSERV_DISCRIM_FIELDS_WEBSITE  0x20
3706 #define NICKSERV_DISCRIM_FIELDS_DEVNULL  0x40
3707
3708 #define NICKSERV_DISCRIM_FIELD_COUNT     7
3709
3710 struct nickserv_discrim {
3711     unsigned int show_fields;
3712     struct helpfile_table *output_table;
3713     int output_table_pos;
3714     unsigned int output_table_free_fields;
3715     
3716     unsigned long flags_on, flags_off;
3717     unsigned long min_registered, max_registered;
3718     unsigned long lastseen;
3719     unsigned int limit;
3720     int min_level, max_level;
3721     int min_karma, max_karma;
3722     enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3723     const char *nickmask;
3724     const char *hostmask;
3725     const char *fakehostmask;
3726     const char *fakeidentmask;
3727     const char *website;
3728     const char *handlemask;
3729     const char *emailmask;
3730     const char *devnull_name;
3731     unsigned int devnull_id;
3732 };
3733
3734 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi, struct nickserv_discrim *discrim);
3735
3736 struct discrim_apply_info {
3737     struct nickserv_discrim *discrim;
3738     discrim_search_func func;
3739     struct userNode *source;
3740     unsigned int matched;
3741 };
3742
3743 static struct nickserv_discrim *
3744 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3745 {
3746     unsigned int i;
3747     struct nickserv_discrim *discrim;
3748
3749     discrim = malloc(sizeof(*discrim));
3750     memset(discrim, 0, sizeof(*discrim));
3751     discrim->min_level = 0;
3752     discrim->max_level = INT_MAX;
3753     discrim->limit = 50;
3754     discrim->min_registered = 0;
3755     discrim->max_registered = ULONG_MAX;
3756     discrim->lastseen = ULONG_MAX;
3757     discrim->min_karma = INT_MIN;
3758     discrim->max_karma = INT_MAX;
3759
3760     for (i=0; i<argc; i++) {
3761         if (i == argc - 1) {
3762             send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3763             goto fail;
3764         }
3765         if (!irccasecmp(argv[i], "limit")) {
3766             discrim->limit = strtoul(argv[++i], NULL, 0);
3767         } else if (!irccasecmp(argv[i], "flags")) {
3768             nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3769         } else if (!irccasecmp(argv[i], "fields")) {
3770             char *fields = argv[++i];
3771             char *delimiter = strstr(fields, ",");
3772             while(1) {
3773                 if(delimiter)
3774                     *delimiter = '\0';
3775                 if(!irccasecmp(fields, "auth"))
3776                     discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_AUTH;
3777                 else if(!irccasecmp(fields, "email"))
3778                     discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_EMAIL;
3779                 else if(!irccasecmp(fields, "seen"))
3780                     discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_SEEN;
3781                 else if(!irccasecmp(fields, "access"))
3782                     discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_ACCESS;
3783                 else if(!irccasecmp(fields, "fakehost"))
3784                     discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_FAKEHOST;
3785                 else if(!irccasecmp(fields, "website") && IsBot(user))
3786                     discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_WEBSITE;
3787                 else if(!irccasecmp(fields, "devnull"))
3788                     discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_DEVNULL;
3789                 else {
3790                     send_message(user, nickserv, "MSG_INVALID_FIELD", fields);
3791                     goto fail;
3792                 }
3793                 if(delimiter) {
3794                     *delimiter = ',';
3795                     fields = delimiter+1;
3796                     if(*fields) {
3797                         delimiter = strstr(fields, ",");
3798                         continue;
3799                     }
3800                 }
3801                 break;
3802             }
3803         } else if (!irccasecmp(argv[i], "registered")) {
3804             const char *cmp = argv[++i];
3805             if (cmp[0] == '<') {
3806                 if (cmp[1] == '=') {
3807                     discrim->min_registered = now - ParseInterval(cmp+2);
3808                 } else {
3809                     discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3810                 }
3811             } else if (cmp[0] == '=') {
3812                 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3813             } else if (cmp[0] == '>') {
3814                 if (cmp[1] == '=') {
3815                     discrim->max_registered = now - ParseInterval(cmp+2);
3816                 } else {
3817                     discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3818                 }
3819             } else {
3820                 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3821             }
3822         } else if (!irccasecmp(argv[i], "seen")) {
3823             discrim->lastseen = now - ParseInterval(argv[++i]);
3824         } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3825             discrim->nickmask = argv[++i];
3826         } else if (!irccasecmp(argv[i], "hostmask")) {
3827             i++;
3828             if (!irccasecmp(argv[i], "exact")) {
3829                 if (i == argc - 1) {
3830                     send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3831                     goto fail;
3832                 }
3833                 discrim->hostmask_type = EXACT;
3834             } else if (!irccasecmp(argv[i], "subset")) {
3835                 if (i == argc - 1) {
3836                     send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3837                     goto fail;
3838                 }
3839                 discrim->hostmask_type = SUBSET;
3840             } else if (!irccasecmp(argv[i], "superset")) {
3841                 if (i == argc - 1) {
3842                     send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3843                     goto fail;
3844                 }
3845                 discrim->hostmask_type = SUPERSET;
3846             } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3847                 if (i == argc - 1) {
3848                     send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3849                     goto fail;
3850                 }
3851                 discrim->hostmask_type = LASTQUIT;
3852             } else {
3853                 i--;
3854                 discrim->hostmask_type = SUPERSET;
3855             }
3856             discrim->hostmask = argv[++i];
3857         } else if (!irccasecmp(argv[i], "fakehost")) {
3858             if (!irccasecmp(argv[++i], "*")) {
3859                 discrim->fakehostmask = 0;
3860             } else {
3861                 discrim->fakehostmask = argv[i];
3862             }
3863         } else if (!irccasecmp(argv[i], "fakeident")) {
3864             if (!irccasecmp(argv[++i], "*")) {
3865                 discrim->fakeidentmask = 0;
3866             } else {
3867                 discrim->fakeidentmask = argv[i];
3868             }
3869         } else if (!irccasecmp(argv[i], "website")) {
3870             if (!irccasecmp(argv[++i], "*")) {
3871                 discrim->website = 0;
3872             } else {
3873                 discrim->website = argv[i];
3874             }
3875         } else if (!irccasecmp(argv[i], "devnull")) {
3876             if (!irccasecmp(argv[++i], "*")) {
3877                 discrim->devnull_id = 0;
3878                 discrim->devnull_name = 0;
3879             } else {
3880                 struct devnull_class *th = devnull_find_name(argv[i]);
3881                 if(!th) {
3882                     send_message(user, nickserv, "OSMSG_DEVNULL_NOTFOUND", argv[i]);
3883                     goto fail;
3884                 }
3885                 discrim->devnull_name = argv[i];
3886                 discrim->devnull_id = th->id;
3887             }
3888         } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3889             if (!irccasecmp(argv[++i], "*")) {
3890                 discrim->handlemask = 0;
3891             } else {
3892                 discrim->handlemask = argv[i];
3893             }
3894         } else if (!irccasecmp(argv[i], "email")) {
3895             if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3896                 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3897                 goto fail;
3898             } else if (!irccasecmp(argv[++i], "*")) {
3899                 discrim->emailmask = 0;
3900             } else {
3901                 discrim->emailmask = argv[i];
3902             }
3903         } else if (!irccasecmp(argv[i], "access")) {
3904             const char *cmp = argv[++i];
3905             if (cmp[0] == '<') {
3906                 if (discrim->min_level == 0) discrim->min_level = 1;
3907                 if (cmp[1] == '=') {
3908                     discrim->max_level = strtoul(cmp+2, NULL, 0);
3909                 } else {
3910                     discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3911                 }
3912             } else if (cmp[0] == '=') {
3913                 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3914             } else if (cmp[0] == '>') {
3915                 if (cmp[1] == '=') {
3916                     discrim->min_level = strtoul(cmp+2, NULL, 0);
3917                 } else {
3918                     discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3919                 }
3920             } else {
3921                 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3922             }
3923         } else if (!irccasecmp(argv[i], "karma")) {
3924             const char *cmp = argv[++i];
3925             if (cmp[0] == '<') {
3926                 if (cmp[1] == '=') {
3927                     discrim->max_karma = strtoul(cmp+2, NULL, 0);
3928                 } else {
3929                     discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3930                 }
3931             } else if (cmp[0] == '=') {
3932                 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3933             } else if (cmp[0] == '>') {
3934                 if (cmp[1] == '=') {
3935                     discrim->min_karma = strtoul(cmp+2, NULL, 0);
3936                 } else {
3937                     discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3938                 }
3939             } else {
3940                 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3941             }
3942         } else {
3943             send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3944             goto fail;
3945         }
3946     }
3947     return discrim;
3948   fail:
3949     free(discrim);
3950     return NULL;
3951 }
3952
3953 static int
3954 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3955 {
3956     if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3957         || (discrim->flags_off & hi->flags)
3958         || (discrim->min_registered > hi->registered)
3959         || (discrim->max_registered < hi->registered)
3960         || (discrim->lastseen < (hi->users?now:hi->lastseen))
3961         || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3962         || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3963         || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3964         || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website)))
3965         || (discrim->devnull_id && discrim->devnull_id != hi->devnull_id)
3966         || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3967         || (discrim->min_level > hi->opserv_level)
3968         || (discrim->max_level < hi->opserv_level)
3969         || (discrim->min_karma > hi->karma)
3970         || (discrim->max_karma < hi->karma)
3971         ) {
3972         return 0;
3973     }
3974     if (discrim->hostmask) {
3975         unsigned int i;
3976         for (i=0; i<hi->masks->used; i++) {
3977             const char *mask = hi->masks->list[i];
3978             if ((discrim->hostmask_type == SUBSET)
3979                 && (match_ircglobs(discrim->hostmask, mask))) break;
3980             else if ((discrim->hostmask_type == EXACT)
3981                      && !irccasecmp(discrim->hostmask, mask)) break;
3982             else if ((discrim->hostmask_type == SUPERSET)
3983                      && (match_ircglobs(mask, discrim->hostmask))) break;
3984             else if ((discrim->hostmask_type == LASTQUIT)
3985                      && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3986         }
3987         if (i==hi->masks->used) return 0;
3988     }
3989     if (discrim->nickmask) {
3990         struct nick_info *nick = hi->nicks;
3991         while (nick) {
3992             if (match_ircglob(nick->nick, discrim->nickmask)) break;
3993             nick = nick->next;
3994         }
3995         if (!nick) return 0;
3996     }
3997     return 1;
3998 }
3999
4000 static unsigned int
4001 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
4002 {
4003     dict_iterator_t it, next;
4004     unsigned int matched;
4005
4006     for (it = dict_first(nickserv_handle_dict), matched = 0;
4007          it && (matched < discrim->limit);
4008          it = next) {
4009         next = iter_next(it);
4010         if (nickserv_discrim_match(discrim, iter_data(it))) {
4011             dsf(source, iter_data(it), discrim);
4012             matched++;
4013         }
4014     }
4015     return matched;
4016 }
4017
4018 static void
4019 search_print_func(struct userNode *source, struct handle_info *match, struct nickserv_discrim *discrim)
4020 {
4021     if(discrim->show_fields) {
4022         //custom fields
4023         if(discrim->output_table) {
4024             discrim->output_table->contents[++discrim->output_table_pos] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
4025             int i = 0;
4026             if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
4027                 discrim->output_table->contents[discrim->output_table_pos][i++] = match->handle;
4028             if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
4029                 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->email_addr ? match->email_addr : "*");
4030             if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN) {
4031                 char *seen;
4032                 char seenBuf[INTERVALLEN];
4033                 if(match->users) {
4034                     seen = "Here";
4035                 } else if(match->lastseen == 0) {
4036                     seen = "Never";
4037                 } else {
4038                     seen = intervalString(seenBuf, now - match->lastseen, source->handle_info);
4039                 }
4040                 discrim->output_table_free_fields |= 1 << i;
4041                 discrim->output_table->contents[discrim->output_table_pos][i++] = strdup(seen);
4042             }
4043             if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
4044                 discrim->output_table->contents[discrim->output_table_pos][i++] = strtab(match->opserv_level);
4045             if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
4046                 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->fakehost ? match->fakehost : "*");
4047             if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
4048                 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->website ? match->website : "*");
4049             if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL) {
4050                 if(discrim->devnull_name) 
4051                     discrim->output_table->contents[discrim->output_table_pos][i++] = discrim->devnull_name;
4052                 else {
4053                     struct devnull_class *devnull = devnull_find_id(match->devnull_id);
4054                     discrim->output_table->contents[discrim->output_table_pos][i++] = (devnull ? devnull->name : "*");
4055                 }
4056             }
4057         }
4058     } else
4059         send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
4060 }
4061
4062 static void
4063 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match), UNUSED_ARG(struct nickserv_discrim *discrim))
4064 {
4065 }
4066
4067 static void
4068 search_unregister_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
4069 {
4070     if (oper_has_access(source, nickserv, match->opserv_level, 0))
4071         nickserv_unregister_handle(match, source);
4072 }
4073
4074 static int
4075 nickserv_sort_accounts_by_access(const void *a, const void *b)
4076 {
4077     const struct handle_info *hi_a = *(const struct handle_info**)a;
4078     const struct handle_info *hi_b = *(const struct handle_info**)b;
4079     if (hi_a->opserv_level != hi_b->opserv_level)
4080         return hi_b->opserv_level - hi_a->opserv_level;
4081     return irccasecmp(hi_a->handle, hi_b->handle);
4082 }
4083
4084 void
4085 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
4086 {
4087     struct handle_info_list hil;
4088     struct helpfile_table tbl;
4089     unsigned int ii;
4090     dict_iterator_t it;
4091     const char **ary;
4092
4093     memset(&hil, 0, sizeof(hil));
4094     for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
4095         struct handle_info *hi = iter_data(it);
4096         if (hi->opserv_level)
4097             handle_info_list_append(&hil, hi);
4098     }
4099     qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
4100     tbl.length = hil.used + 1;
4101     tbl.width = 2;
4102     tbl.flags = TABLE_NO_FREE;
4103     tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4104     tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
4105     ary[0] = "Account";
4106     ary[1] = "Level";
4107     for (ii = 0; ii < hil.used; ) {
4108         ary = malloc(tbl.width * sizeof(ary[0]));
4109         ary[0] = hil.list[ii]->handle;
4110         ary[1] = strtab(hil.list[ii]->opserv_level);
4111         tbl.contents[++ii] = ary;
4112     }
4113     table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4114     reply("MSG_MATCH_COUNT", hil.used);
4115     for (ii = 0; ii < hil.used; ii++)
4116         free(tbl.contents[ii]);
4117     free(tbl.contents);
4118     free(hil.list);
4119 }
4120
4121 static NICKSERV_FUNC(cmd_search)
4122 {
4123     struct nickserv_discrim *discrim;
4124     discrim_search_func action;
4125     struct svccmd *subcmd;
4126     unsigned int matches;
4127     char buf[MAXLEN];
4128
4129     NICKSERV_MIN_PARMS(3);
4130     sprintf(buf, "search %s", argv[1]);
4131     subcmd = dict_find(nickserv_service->commands, buf, NULL);
4132     if (!irccasecmp(argv[1], "print"))
4133         action = search_print_func;
4134     else if (!irccasecmp(argv[1], "count"))
4135         action = search_count_func;
4136     else if (!irccasecmp(argv[1], "unregister"))
4137         action = search_unregister_func;
4138     else {
4139         reply("NSMSG_INVALID_ACTION", argv[1]);
4140         return 0;
4141     }
4142
4143     if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
4144         return 0;
4145
4146     discrim = nickserv_discrim_create(user, argc-2, argv+2);
4147     if (!discrim)
4148         return 0;
4149
4150     if (action == search_print_func)
4151         reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
4152     else if (action == search_count_func)
4153         discrim->limit = INT_MAX;
4154
4155     matches = nickserv_discrim_search(discrim, action, user);
4156     
4157     if(discrim->show_fields) {
4158         int width = 0;
4159         int ii;
4160         for(ii = 0; ii < NICKSERV_DISCRIM_FIELD_COUNT; ii++) {
4161             if(discrim->show_fields & (1 << ii)) width++;
4162         }
4163         discrim->output_table = malloc(sizeof(discrim->output_table[0]));
4164         discrim->output_table->length = matches+1;
4165         discrim->output_table->width = width;
4166         discrim->output_table->flags = TABLE_NO_FREE;
4167         discrim->output_table->contents = malloc(discrim->output_table->length * sizeof(discrim->output_table->contents[0]));
4168         discrim->output_table->contents[0] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
4169         
4170         ii = 0;
4171         if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
4172             discrim->output_table->contents[0][ii++] = "Auth";
4173         if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
4174             discrim->output_table->contents[0][ii++] = "EMail";
4175         if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN)
4176             discrim->output_table->contents[0][ii++] = "Seen";
4177         if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
4178             discrim->output_table->contents[0][ii++] = "Access";
4179         if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
4180             discrim->output_table->contents[0][ii++] = "Fakehost";
4181         if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
4182             discrim->output_table->contents[0][ii++] = "Website";
4183         if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
4184             discrim->output_table->contents[0][ii++] = "DevNull";
4185         
4186         nickserv_discrim_search(discrim, action, user);
4187         
4188         table_send(nickserv, user->nick, 0, NULL, *discrim->output_table);
4189         
4190         for(ii = 1; ii < discrim->output_table->length; ++ii) {
4191             int ij;
4192             for(ij = 0; ij < NICKSERV_DISCRIM_FIELD_COUNT; ij++) {
4193                 if(discrim->output_table_free_fields & (1 << ij))
4194                     free((char*)discrim->output_table->contents[ii][ij]);
4195             }
4196             free(discrim->output_table->contents[ii]);
4197         }
4198         free(discrim->output_table->contents[0]);
4199         free(discrim->output_table->contents);
4200         free(discrim->output_table);
4201     }
4202     if (matches)
4203         reply("MSG_MATCH_COUNT", matches);
4204     else
4205         reply("MSG_NO_MATCHES");
4206
4207     
4208     free(discrim);
4209     return 0;
4210 }
4211
4212 static MODCMD_FUNC(cmd_checkpass)
4213 {
4214     struct handle_info *hi;
4215
4216     NICKSERV_MIN_PARMS(3);
4217     if (!(hi = get_handle_info(argv[1]))) {
4218         reply("MSG_HANDLE_UNKNOWN", argv[1]);
4219         return 0;
4220     }
4221     if (checkpass(argv[2], hi->passwd))
4222         reply("CHECKPASS_YES");
4223     else
4224         reply("CHECKPASS_NO");
4225     argv[2] = "****";
4226     return 1;
4227 }
4228
4229 static MODCMD_FUNC(cmd_checkemail)
4230 {
4231     struct handle_info *hi;
4232
4233     NICKSERV_MIN_PARMS(3);
4234     if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
4235         return 0;
4236     }
4237     if (!hi->email_addr)
4238         reply("CHECKEMAIL_NOT_SET");
4239     else if (!irccasecmp(argv[2], hi->email_addr))
4240         reply("CHECKEMAIL_YES");
4241     else
4242         reply("CHECKEMAIL_NO");
4243     return 1;
4244 }
4245
4246 static int
4247 nickserv_db_read_authlog(UNUSED_ARG(const char *key), void *data, void *extra)
4248 {
4249     struct record_data *rd = data;
4250     struct handle_info *hi = extra;
4251     const char *str;
4252     struct authlogEntry *authlog;
4253     authlog = malloc(sizeof(*authlog));
4254     
4255     str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGIN_TIME, RECDB_QSTRING);
4256     authlog->login_time = str ? strtoul(str, NULL, 0) : 0;
4257     
4258     str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGOUT_TIME, RECDB_QSTRING);
4259     authlog->logout_time = str ? strtoul(str, NULL, 0) : 0;
4260     
4261     str = database_get_data(rd->d.object, KEY_AUTHLOG_HOSTMASK, RECDB_QSTRING);
4262     authlog->hostmask = str ? strdup(str) : NULL;
4263     
4264     str = database_get_data(rd->d.object, KEY_AUTHLOG_QUIT_REASON, RECDB_QSTRING);
4265     authlog->quit_reason = str ? strdup(str) : NULL;
4266     
4267     authlog->user = NULL;
4268     
4269     authlog->next = NULL;
4270     
4271     //append it to the end of the list...
4272     struct authlogEntry *authlog_entry;
4273     if(!hi->authlog) {
4274         hi->authlog = authlog;
4275     } else {
4276         for(authlog_entry = hi->authlog; authlog_entry; authlog_entry = authlog_entry->next) {
4277             if(!authlog_entry->next) {
4278                 authlog_entry->next = authlog;
4279                 break;
4280             }
4281         }
4282     }
4283     return 0;
4284 }
4285
4286 static void
4287 nickserv_db_read_handle(const char *handle, dict_t obj)
4288 {
4289     const char *str;
4290     struct string_list *masks, *slist;
4291     struct handle_info *hi;
4292     struct userNode *authed_users;
4293     struct userData *channel_list;
4294     unsigned long id;
4295     unsigned int ii;
4296     dict_t subdb;
4297
4298     str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
4299     id = str ? strtoul(str, NULL, 0) : 0;
4300     str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
4301     if (!str) {
4302         log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
4303         return;
4304     }
4305     if ((hi = get_handle_info(handle))) {
4306         authed_users = hi->users;
4307         channel_list = hi->channels;
4308         hi->users = NULL;
4309         hi->channels = NULL;
4310         dict_remove(nickserv_handle_dict, hi->handle);
4311     } else {
4312         authed_users = NULL;
4313         channel_list = NULL;
4314     }
4315     hi = register_handle(handle, str, id);
4316     if (authed_users) {
4317         hi->users = authed_users;
4318         while (authed_users) {
4319             authed_users->handle_info = hi;
4320             authed_users = authed_users->next_authed;
4321         }
4322     }
4323     hi->channels = channel_list;
4324     masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
4325     hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
4326     str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
4327     hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
4328     str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
4329     hi->language = language_find(str ? str : "C");
4330     str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
4331     hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
4332     str = database_get_data(obj, KEY_STAFF_LEVEL, RECDB_QSTRING);
4333     hi->staff_level = str ? strtoul(str, NULL, 0) : 0;
4334     str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
4335     if (str)
4336         hi->infoline = strdup(str);
4337     str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING);
4338     if (str)
4339         hi->website = strdup(str);
4340     str = database_get_data(obj, KEY_DEVNULL, RECDB_QSTRING);
4341     hi->devnull_id = str ? strtoul(str, NULL, 0) : 0;
4342     str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
4343     hi->registered = str ? strtoul(str, NULL, 0) : now;
4344     str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
4345     hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
4346     str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
4347     hi->karma = str ? strtoul(str, NULL, 0) : 0;
4348     /* We want to read the nicks even if disable_nicks is set.  This is so
4349      * that we don't lose the nick data entirely. */
4350     slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
4351     if (slist) {
4352         for (ii=0; ii<slist->used; ii++)
4353             register_nick(slist->list[ii], hi);
4354     }
4355     str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
4356     if (str) {
4357         for (ii=0; str[ii]; ii++)
4358             hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
4359     }
4360     str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
4361     hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
4362     str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
4363     hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
4364     str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
4365     hi->table_width = str ? strtoul(str, NULL, 0) : 0;
4366     str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
4367     if (!str)
4368         str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
4369     if (str)
4370         safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
4371     str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
4372     if (str)
4373         nickserv_set_email_addr(hi, str);
4374     str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
4375     if (str)
4376         hi->epithet = strdup(str);
4377     str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
4378     if (str)
4379         hi->fakehost = strdup(str);
4380     str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
4381     if (str)
4382         hi->fakeident = strdup(str);
4383     /* Read the "cookie" sub-database (if it exists). */
4384     subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
4385     if (subdb) {
4386         const char *data, *type, *expires, *cookie_str;
4387         struct handle_cookie *cookie;
4388
4389         cookie = calloc(1, sizeof(*cookie));
4390         type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
4391         data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
4392         expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
4393         cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
4394         if (!type || !expires || !cookie_str) {
4395             log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
4396             goto cookie_out;
4397         }
4398         if (!irccasecmp(type, KEY_ACTIVATION))
4399             cookie->type = ACTIVATION;
4400         else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
4401             cookie->type = PASSWORD_CHANGE;
4402         else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
4403             cookie->type = EMAIL_CHANGE;
4404         else if (!irccasecmp(type, KEY_ALLOWAUTH))
4405             cookie->type = ALLOWAUTH;
4406         else {
4407             log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
4408             goto cookie_out;
4409         }
4410         cookie->expires = strtoul(expires, NULL, 0);
4411         if (cookie->expires < now)
4412             goto cookie_out;
4413         if (data)
4414             cookie->data = strdup(data);
4415         safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
4416         cookie->hi = hi;
4417       cookie_out:
4418         if (cookie->hi)
4419             nickserv_bake_cookie(cookie);
4420         else
4421             nickserv_free_cookie(cookie);
4422     }
4423     /* Read the "notes" sub-database (if it exists). */
4424     subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
4425     if (subdb) {
4426         dict_iterator_t it;
4427         struct handle_note *last_note;
4428         struct handle_note *note;
4429
4430         last_note = NULL;
4431         for (it = dict_first(subdb); it; it = iter_next(it)) {
4432             const char *expires;
4433             const char *setter;
4434             const char *text;
4435             const char *set;
4436             const char *note_id;
4437             dict_t notedb;
4438
4439             note_id = iter_key(it);
4440             notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
4441             if (!notedb) {
4442                 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
4443                 continue;
4444             }
4445             expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
4446             setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
4447             text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
4448             set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
4449             if (!setter || !text || !set) {
4450                 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
4451                 continue;
4452             }
4453             note = calloc(1, sizeof(*note) + strlen(text));
4454             note->next = NULL;
4455             note->expires = expires ? strtoul(expires, NULL, 10) : 0;
4456             note->set = strtoul(set, NULL, 10);
4457             note->id = strtoul(note_id, NULL, 10);
4458             safestrncpy(note->setter, setter, sizeof(note->setter));
4459             strcpy(note->note, text);
4460             if (last_note)
4461                 last_note->next = note;
4462             else
4463                 hi->notes = note;
4464             last_note = note;
4465         }
4466     }
4467     if ((subdb = database_get_data(obj, KEY_AUTHLOG, RECDB_OBJECT)))
4468         dict_foreach(subdb, nickserv_db_read_authlog, hi);
4469 }
4470
4471 static int
4472 nickserv_saxdb_read(dict_t db) {
4473     dict_iterator_t it;
4474     struct record_data *rd;
4475
4476     for (it=dict_first(db); it; it=iter_next(it)) {
4477         rd = iter_data(it);
4478         nickserv_db_read_handle(iter_key(it), rd->d.object);
4479     }
4480     return 0;
4481 }
4482
4483 static NICKSERV_FUNC(cmd_mergedb)
4484 {
4485     struct timeval start, stop;
4486     dict_t db;
4487
4488     NICKSERV_MIN_PARMS(2);
4489     gettimeofday(&start, NULL);
4490     if (!(db = parse_database(argv[1]))) {
4491         reply("NSMSG_DB_UNREADABLE", argv[1]);
4492         return 0;
4493     }
4494     nickserv_saxdb_read(db);
4495     free_database(db);
4496     gettimeofday(&stop, NULL);
4497     stop.tv_sec -= start.tv_sec;
4498     stop.tv_usec -= start.tv_usec;
4499     if (stop.tv_usec < 0) {
4500         stop.tv_sec -= 1;
4501         stop.tv_usec += 1000000;
4502     }
4503     reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
4504     return 1;
4505 }
4506
4507 static void
4508 expire_handles(UNUSED_ARG(void *data))
4509 {
4510     dict_iterator_t it, next;
4511     unsigned long expiry;
4512     struct handle_info *hi;
4513
4514     for (it=dict_first(nickserv_handle_dict); it; it=next) {
4515         next = iter_next(it);
4516         hi = iter_data(it);
4517         if ((hi->opserv_level > 0)
4518             || hi->users
4519             || HANDLE_FLAGGED(hi, FROZEN)
4520             || HANDLE_FLAGGED(hi, NODELETE)) {
4521             continue;
4522         }
4523         expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4524         if ((now - hi->lastseen) > expiry) {
4525             log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
4526             nickserv_unregister_handle(hi, NULL);
4527         }
4528     }
4529
4530     if (nickserv_conf.handle_expire_frequency)
4531         timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4532 }
4533
4534 static void
4535 nickserv_load_dict(const char *fname)
4536 {
4537     FILE *file;
4538     char line[128];
4539     if (!(file = fopen(fname, "r"))) {
4540         log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4541         return;
4542     }
4543     while (fgets(line, sizeof(line), file)) {
4544         if (!line[0])
4545             continue;
4546         if (line[strlen(line)-1] == '\n')
4547             line[strlen(line)-1] = 0;
4548         dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4549     }
4550     fclose(file);
4551     log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4552 }
4553
4554 static enum reclaim_action
4555 reclaim_action_from_string(const char *str) {
4556     if (!str)
4557         return RECLAIM_NONE;
4558     else if (!irccasecmp(str, "warn"))
4559         return RECLAIM_WARN;
4560     else if (!irccasecmp(str, "svsnick"))
4561         return RECLAIM_SVSNICK;
4562     else if (!irccasecmp(str, "kill"))
4563         return RECLAIM_KILL;
4564     else
4565         return RECLAIM_NONE;
4566 }
4567
4568 static void
4569 nickserv_conf_read(void)
4570 {
4571     dict_t conf_node, child;
4572     const char *str;
4573     dict_iterator_t it;
4574
4575     if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4576         log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4577         return;
4578     }
4579     str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4580     if (!str)
4581         str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4582     if (nickserv_conf.valid_handle_regex_set)
4583         regfree(&nickserv_conf.valid_handle_regex);
4584     if (str) {
4585         int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4586         nickserv_conf.valid_handle_regex_set = !err;
4587         if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4588     } else {
4589         nickserv_conf.valid_handle_regex_set = 0;
4590     }
4591     str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4592     if (nickserv_conf.valid_nick_regex_set)
4593         regfree(&nickserv_conf.valid_nick_regex);
4594     if (str) {
4595         int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4596         nickserv_conf.valid_nick_regex_set = !err;
4597         if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4598     } else {
4599         nickserv_conf.valid_nick_regex_set = 0;
4600     }
4601     str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4602     if (!str)
4603         str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4604     nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4605     str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4606     nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4607     str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4608     nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4609     str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4610     nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4611     str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4612     nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4613     str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4614     nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4615     str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4616     nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4617     str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4618     nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4619     str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4620     nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4621     str = database_get_data(conf_node, KEY_MODSTAFF_LEVEL, RECDB_QSTRING);
4622     nickserv_conf.modstaff_level = str ? strtoul(str, NULL, 0) : 800;
4623     str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4624     nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4625     str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4626     nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4627     str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4628     nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4629     str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
4630     nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
4631     str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4632     if (!str)
4633         str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4634     nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4635     str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4636     if (!str)
4637         str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4638     nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4639     str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4640     if (!str)
4641         str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4642     nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4643     str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4644     nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4645     str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4646     nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4647     str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4648     nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4649     str = database_get_data(conf_node, KEY_MAX_AUTHLOG_LEN, RECDB_QSTRING);
4650     nickserv_conf.max_authlog_len = str ? strtoul(str, NULL, 0) : 30;
4651     str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4652     nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4653     str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4654     if (!str)
4655         str = "ShgsfnHbu";
4656     nickserv_conf.ounregister_flags = 0;
4657     while(*str) {
4658         unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4659         str++;
4660         if(pos)
4661             nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4662     }
4663     str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4664     if (!str)
4665         nickserv_conf.handle_ts_mode = TS_IGNORE;
4666     else if (!irccasecmp(str, "ircu"))
4667         nickserv_conf.handle_ts_mode = TS_IRCU;
4668     else
4669         nickserv_conf.handle_ts_mode = TS_IGNORE;
4670     if (!nickserv_conf.disable_nicks) {
4671         str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4672         nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4673         str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4674         nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4675         str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4676         nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4677         str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4678         nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4679     }
4680     child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4681     for (it=dict_first(child); it; it=iter_next(it)) {
4682         const char *key = iter_key(it), *value;
4683         unsigned char flag;
4684         int pos;
4685
4686         if (!strncasecmp(key, "uc_", 3))
4687             flag = toupper(key[3]);
4688         else if (!strncasecmp(key, "lc_", 3))
4689             flag = tolower(key[3]);
4690         else
4691             flag = key[0];
4692
4693         if ((pos = handle_inverse_flags[flag])) {
4694             value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4695             flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4696         }
4697     }
4698     if (nickserv_conf.weak_password_dict)
4699         dict_delete(nickserv_conf.weak_password_dict);
4700     nickserv_conf.weak_password_dict = dict_new();
4701     dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4702     dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4703     dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4704     str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4705     if (str)
4706         nickserv_load_dict(str);
4707     str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4708     if (nickserv && str)
4709         NickChange(nickserv, str, 0);
4710     str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4711     nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4712     str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4713     nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4714     str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4715     nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4716     str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4717     nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4718     str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4719     nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4720     str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4721     nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4722     str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4723     nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4724     str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4725     nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4726     str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4727     titlehost_suffix = str ? str : "example.net";
4728     str = conf_get_data("server/network", RECDB_QSTRING);
4729     nickserv_conf.network_name = str ? str : "some IRC network";
4730     if (!nickserv_conf.auth_policer_params) {
4731         nickserv_conf.auth_policer_params = policer_params_new();
4732         policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4733         policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4734     }
4735     child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4736     for (it=dict_first(child); it; it=iter_next(it))
4737         set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4738 }
4739
4740 static void
4741 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4742     const char *msg;
4743     char newnick[NICKLEN+1];
4744
4745     assert(user);
4746     assert(ni);
4747     switch (action) {
4748     case RECLAIM_NONE:
4749         /* do nothing */
4750         break;
4751     case RECLAIM_WARN:
4752         send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4753         break;
4754     case RECLAIM_SVSNICK:
4755         do {
4756             snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4757         } while (GetUserH(newnick));
4758         irc_svsnick(nickserv, user, newnick);
4759         break;
4760     case RECLAIM_KILL:
4761         msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4762         DelUser(user, nickserv, 1, msg);
4763         break;
4764     }
4765 }
4766
4767 static void
4768 nickserv_reclaim_p(void *data) {
4769     struct userNode *user = data;
4770     struct nick_info *ni = get_nick_info(user->nick);
4771     if (ni)
4772         nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4773 }
4774
4775 static void
4776 check_user_nick(struct userNode *user) {
4777     //check if this user is a pending LOC user
4778     if(pendingLOCUsers) {
4779         struct pendingLOCUser *pending, *next, *prev = NULL;
4780         int remove;
4781         for(pending = pendingLOCUsers; pending; pending = next) {
4782             next = pending->next;
4783             remove = 0;
4784             if(user->handle_info == pending->handle_info) {
4785                 pending->authlog->user = user;
4786                 free((char*) pending->authlog->hostmask);
4787                 pending->authlog->hostmask = generate_hostmask(user, GENMASK_USENICK|GENMASK_STRICT_IDENT|GENMASK_NO_HIDING|GENMASK_STRICT_HOST);
4788                 remove = 1;
4789             } else if(now - pending->time > 10)
4790                 remove = 1;
4791             if(remove) {
4792                 if(prev)
4793                     prev->next = next;
4794                 else
4795                     pendingLOCUsers = next;
4796                 free(pending);
4797             }
4798         }
4799     }
4800     struct nick_info *ni;
4801     user->modes &= ~FLAGS_REGNICK;
4802     if (!(ni = get_nick_info(user->nick)))
4803         return;
4804     if (user->handle_info == ni->owner) {
4805         user->modes |= FLAGS_REGNICK;
4806         irc_regnick(user);
4807         return;
4808     }
4809     if (nickserv_conf.warn_nick_owned)
4810         send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4811     if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4812         return;
4813     if (nickserv_conf.auto_reclaim_delay)
4814         timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4815     else
4816         nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4817 }
4818
4819 void
4820 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4821 {
4822     struct handle_info *hi = NULL;
4823
4824     if (stamp != NULL)
4825         hi = dict_find(nickserv_handle_dict, stamp, NULL);
4826     if ((hi == NULL) && (serial != 0)) {
4827         char id[IDLEN + 1];
4828         inttobase64(id, serial, IDLEN);
4829         hi = dict_find(nickserv_id_dict, id, NULL);
4830     }
4831
4832     if (hi) {
4833         if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4834             && (timestamp != hi->registered)) {
4835             return;
4836         }
4837         if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4838             return;
4839         }
4840         set_user_handle_info(user, hi, 0);
4841     } else {
4842         log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4843     }
4844 }
4845
4846 void
4847 handle_nick_change(struct userNode *user, const char *old_nick)
4848 {
4849     struct handle_info *hi;
4850
4851     if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4852         dict_remove(nickserv_allow_auth_dict, old_nick);
4853         dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4854     }
4855     timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4856     check_user_nick(user);
4857 }
4858
4859 void
4860 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), const char *why)
4861 {
4862     if(user->handle_info) {
4863         //check if theres an open authlog entry
4864         struct authlogEntry *authlog;
4865         for(authlog = user->handle_info->authlog; authlog; authlog = authlog->next) {
4866             if(authlog->user == user) {
4867                 authlog->user = NULL;
4868                 authlog->logout_time = now;
4869                 authlog->quit_reason = strdup(why);
4870                 break;
4871             }
4872         }
4873     }
4874     dict_remove(nickserv_allow_auth_dict, user->nick);
4875     timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4876     set_user_handle_info(user, NULL, 0);
4877 }
4878
4879 static struct modcmd *
4880 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4881 {
4882     if (min_level > 0) {
4883         char buf[16];
4884         sprintf(buf, "%u", min_level);
4885         if (must_be_qualified) {
4886             return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4887         } else {
4888             return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4889         }
4890     } else if (min_level == 0) {
4891         if (must_be_qualified) {
4892             return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4893         } else {
4894             return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4895         }
4896     } else {
4897         if (must_be_qualified) {
4898             return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4899         } else {
4900             return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4901         }
4902     }
4903 }
4904
4905 static void
4906 nickserv_db_cleanup(void)
4907 {
4908     unreg_del_user_func(nickserv_remove_user);
4909     userList_clean(&curr_helpers);
4910     policer_params_delete(nickserv_conf.auth_policer_params);
4911     dict_delete(nickserv_handle_dict);
4912     dict_delete(nickserv_nick_dict);
4913     dict_delete(nickserv_opt_dict);
4914     dict_delete(nickserv_allow_auth_dict);
4915     dict_delete(nickserv_email_dict);
4916     dict_delete(nickserv_id_dict);
4917     dict_delete(nickserv_conf.weak_password_dict);
4918     free(auth_func_list);
4919     free(unreg_func_list);
4920     free(rf_list);
4921     free(allowauth_func_list);
4922     free(handle_merge_func_list);
4923     free(failpw_func_list);
4924     if (nickserv_conf.valid_handle_regex_set)
4925         regfree(&nickserv_conf.valid_handle_regex);
4926     if (nickserv_conf.valid_nick_regex_set)
4927         regfree(&nickserv_conf.valid_nick_regex);
4928     struct pendingLOCUser *pending, *next;
4929     for(pending = pendingLOCUsers; pending; pending = next) {
4930         next = pending->next;
4931         free(pending);
4932     }
4933     pendingLOCUsers = NULL;
4934 }
4935
4936 void
4937 init_nickserv(const char *nick)
4938 {
4939     unsigned int i;
4940     NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4941     reg_new_user_func(check_user_nick);
4942     reg_nick_change_func(handle_nick_change);
4943     reg_del_user_func(nickserv_remove_user);
4944     reg_account_func(handle_account);
4945
4946     /* set up handle_inverse_flags */
4947     memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4948     for (i=0; handle_flags[i]; i++) {
4949         handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4950         flag_access_levels[i] = 0;
4951     }
4952
4953     conf_register_reload(nickserv_conf_read);
4954     nickserv_opt_dict = dict_new();
4955     nickserv_email_dict = dict_new();
4956     dict_set_free_keys(nickserv_email_dict, free);
4957     dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4958
4959     nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4960     modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4961     nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4962     nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4963     nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4964     nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4965     nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4966     nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4967     nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4968     nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4969     nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4970     nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4971     nickserv_define_func("SET", cmd_set, -1, 1, 0);
4972     nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4973     nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4974     nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4975     nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4976     nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4977     nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4978     nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4979     nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4980     nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4981     if (!nickserv_conf.disable_nicks) {
4982         /* nick management commands */
4983         nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4984         nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4985         nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4986         nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4987         nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4988         nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4989     }
4990     if (nickserv_conf.email_enabled) {
4991         nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4992         nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4993         nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4994         nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4995         nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4996         dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4997     }
4998     nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4999     /* miscellaneous commands */
5000     nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
5001     nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
5002     nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
5003     nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
5004     nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
5005     nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
5006     nickserv_define_func("AUTHLOG", cmd_authlog, -1, 1, 0);
5007     nickserv_define_func("OAUTHLOG", cmd_oauthlog, 0, 1, 0);
5008     /* other options */
5009     dict_insert(nickserv_opt_dict, "INFO", opt_info);
5010     dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
5011     dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
5012     dict_insert(nickserv_opt_dict, "COLOR", opt_color);
5013     dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
5014     dict_insert(nickserv_opt_dict, "STYLE", opt_style);
5015     dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
5016     dict_insert(nickserv_opt_dict, "PASS", opt_password);
5017     dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
5018     dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
5019     dict_insert(nickserv_opt_dict, "WEBSITE", opt_website);
5020     dict_insert(nickserv_opt_dict, "DEVNULL", opt_devnull);
5021     dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
5022     dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
5023     dict_insert(nickserv_opt_dict, "STAFF", opt_staff_level);
5024     dict_insert(nickserv_opt_dict, "STAFF_LEVEL", opt_staff_level);
5025     dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
5026     if (titlehost_suffix) {
5027         dict_insert(nickserv_opt_dict, "TITLE", opt_title);
5028         dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
5029         dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
5030     }
5031     dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
5032     dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
5033     dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
5034     nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
5035
5036     nickserv_handle_dict = dict_new();
5037     dict_set_free_keys(nickserv_handle_dict, free);
5038     dict_set_free_data(nickserv_handle_dict, free_handle_info);
5039
5040     nickserv_id_dict = dict_new();
5041     dict_set_free_keys(nickserv_id_dict, free);
5042
5043     nickserv_nick_dict = dict_new();
5044     dict_set_free_data(nickserv_nick_dict, free);
5045
5046     nickserv_allow_auth_dict = dict_new();
5047
5048     userList_init(&curr_helpers);
5049
5050     if (nick) {
5051         const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
5052         nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
5053         nickserv_service = service_register(nickserv);
5054     }
5055     saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
5056     reg_exit_func(nickserv_db_cleanup);
5057     if(nickserv_conf.handle_expire_frequency)
5058         timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
5059     message_register_table(msgtab);
5060 }