Fix NickServ language crash; convert four ChanServ binary options to level options
[srvx.git] / src / nickserv.c
1 /* nickserv.c - Nick/authentication service
2  * Copyright 2000-2004 srvx Development Team
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.  Important limitations are
8  * listed in the COPYING file that accompanies this software.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, email srvx-maintainers@srvx.net.
17  */
18
19 #include "chanserv.h"
20 #include "conf.h"
21 #include "global.h"
22 #include "modcmd.h"
23 #include "opserv.h" /* for gag_create(), opserv_bad_channel() */
24 #include "saxdb.h"
25 #include "sendmail.h"
26 #include "timeq.h"
27
28 #ifdef HAVE_REGEX_H
29 #include <regex.h>
30 #endif
31
32 #define NICKSERV_CONF_NAME "services/nickserv"
33
34 #define KEY_DISABLE_NICKS "disable_nicks"
35 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
36 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
37 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
38 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
39 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
40 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
41 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
42 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
43 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
44 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
45 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
46 #define KEY_MODOPER_LEVEL "modoper_level"
47 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
48 #define KEY_FLAG_LEVELS "flag_levels"
49 #define KEY_HANDLE_EXPIRE_FREQ  "handle_expire_freq"
50 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
51 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
52 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
53 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
54 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
55 #define KEY_DICT_FILE "dict_file"
56 #define KEY_NICK "nick"
57 #define KEY_LANGUAGE "language"
58 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
59 #define KEY_AUTOGAG_DURATION "autogag_duration"
60 #define KEY_AUTH_POLICER "auth_policer"
61 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
62 #define KEY_EMAIL_ENABLED "email_enabled"
63 #define KEY_EMAIL_REQUIRED "email_required"
64 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
65 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
66 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
67
68 #define KEY_ID "id"
69 #define KEY_PASSWD "passwd"
70 #define KEY_NICKS "nicks"
71 #define KEY_MASKS "masks"
72 #define KEY_OPSERV_LEVEL "opserv_level"
73 #define KEY_FLAGS "flags"
74 #define KEY_REGISTER_ON "register"
75 #define KEY_LAST_SEEN "lastseen"
76 #define KEY_INFO "info"
77 #define KEY_USERLIST_STYLE "user_style"
78 #define KEY_SCREEN_WIDTH "screen_width"
79 #define KEY_LAST_AUTHED_HOST "last_authed_host"
80 #define KEY_LAST_QUIT_HOST "last_quit_host"
81 #define KEY_EMAIL_ADDR "email_addr"
82 #define KEY_COOKIE "cookie"
83 #define KEY_COOKIE_DATA "data"
84 #define KEY_COOKIE_TYPE "type"
85 #define KEY_COOKIE_EXPIRES "expires"
86 #define KEY_ACTIVATION "activation"
87 #define KEY_PASSWORD_CHANGE "password change"
88 #define KEY_EMAIL_CHANGE "email change"
89 #define KEY_ALLOWAUTH "allowauth"
90 #define KEY_EPITHET "epithet"
91 #define KEY_TABLE_WIDTH "table_width"
92 #define KEY_ANNOUNCEMENTS "announcements"
93 #define KEY_MAXLOGINS "maxlogins"
94
95 #define NICKSERV_VALID_CHARS    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
96
97 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
98 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
99 typedef OPTION_FUNC(option_func_t);
100
101 DEFINE_LIST(handle_info_list, struct handle_info*);
102
103 #define NICKSERV_MIN_PARMS(N) do { \
104   if (argc < N) { \
105     reply("MSG_MISSING_PARAMS", argv[0]); \
106     svccmd_send_help(user, nickserv, cmd); \
107     return 0; \
108   } } while (0)
109
110 struct userNode *nickserv;
111 struct userList curr_helpers;
112 const char *handle_flags = HANDLE_FLAGS;
113
114 static struct module *nickserv_module;
115 static struct service *nickserv_service;
116 static struct log_type *NS_LOG;
117 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
118 static dict_t nickserv_id_dict; /* contains struct handle_info* */
119 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
120 static dict_t nickserv_opt_dict; /* contains option_func_t* */
121 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
122 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
123 static char handle_inverse_flags[256];
124 static unsigned int flag_access_levels[32];
125 static const struct message_entry msgtab[] = {
126     { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
127     { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
128     { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
129     { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
130     { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
131     { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
132     { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
133     { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
134     { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
135     { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
136     { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
137     { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
138     { "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." },
139     { "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." },
140     { "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." },
141     { "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." },
142     { "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." },
143     { "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." },
144     { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding.  Please use the cookie or wait for it to expire." },
145     { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
146     { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
147     { "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." },
148     { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered).  You are now authenticated to your account." },
149     { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
150     { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
151     { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
152     { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
153     { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
154     { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry.  I am confused.  Please report this bug." },
155     { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
156     { "NSMSG_ATE_COOKIE", "I ate the cookie for your account.  You may now have another." },
157     { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
158     { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
159     { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
160     { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
161     { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
162     { "NSMSG_USE_AUTHCOOKIE", "Your hostmask is not valid for account $b%s$b.  Please use the $bauthcookie$b command to grant yourself access.  (/msg $S authcookie %s)" },
163     { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
164     { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
165     { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
166     { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
167     { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
168     { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
169     { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
170     { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
171     { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
172     { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
173     { "NSMSG_STAMPED_AUTHCOOKIE",  "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
174     { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
175     { "NSMSG_HANDLEINFO_ID", "  Account ID: %lu" },
176     { "NSMSG_HANDLEINFO_REGGED", "  Registered on: %s" },
177     { "NSMSG_HANDLEINFO_LASTSEEN", "  Last seen: %s" },
178     { "NSMSG_HANDLEINFO_LASTSEEN_NOW", "  Last seen: Right now!" },
179     { "NSMSG_HANDLEINFO_VACATION", "  On vacation." },
180     { "NSMSG_HANDLEINFO_EMAIL_ADDR", "  Email address: %s" },
181     { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", "  Cookie: There is currently an activation cookie issued for this account" },
182     { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", "  Cookie: There is currently a password change cookie issued for this account" },
183     { "NSMSG_HANDLEINFO_COOKIE_EMAIL", "  Cookie: There is currently an email change cookie issued for this account" },
184     { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", "  Cookie: There is currently an allowauth cookie issued for this account" },
185     { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", "  Cookie: There is currently an unknown cookie issued for this account" },
186     { "NSMSG_HANDLEINFO_INFOLINE", "  Infoline: %s" },
187     { "NSMSG_HANDLEINFO_FLAGS", "  Flags: %s" },
188     { "NSMSG_HANDLEINFO_EPITHET", "  Epithet: %s" },
189     { "NSMSG_HANDLEINFO_LAST_HOST", "  Last quit hostmask: %s" },
190     { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", "  Last quit hostmask: Unknown" },
191     { "NSMSG_HANDLEINFO_NICKS", "  Nickname(s): %s" },
192     { "NSMSG_HANDLEINFO_MASKS", "  Hostmask(s): %s" },
193     { "NSMSG_HANDLEINFO_CHANNELS", "  Channel(s): %s" },
194     { "NSMSG_HANDLEINFO_CURRENT", "  Current nickname(s): %s" },
195     { "NSMSG_HANDLEINFO_DNR", "  Do-not-register (by %s): %s" },
196     { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
197     { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
198     { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
199     { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
200     { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users.  Please use the $bset email$b command to set your email address!" },
201     { "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)." },
202     { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
203     { "NSMSG_AUTH_SUCCESS", "I recognize you." },
204     { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
205     { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
206     { "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." },
207     { "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." },
208     { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
209     { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
210     { "NSMSG_MUST_AUTH", "You must be authenticated first." },
211     { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
212     { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
213     { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
214     { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
215     { "NSMSG_PASS_SUCCESS", "Password changed." },
216     { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
217     { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
218     { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
219     { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
220     { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
221     { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
222     { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
223     { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
224     { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
225     { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
226     { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
227     { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
228     { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
229     { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
230     { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
231     { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
232     { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
233     { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
234     { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
235     { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
236     { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
237     { "NSMSG_ON_VACATION", "You are now on vacation.  Your account will be preserved until you authenticate again." },
238     { "NSMSG_NO_ACCESS", "Access denied." },
239     { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
240     { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
241     { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
242     { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
243     { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
244     { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
245     { "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." },
246     { "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." },
247     { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
248     { "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." },
249     { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
250     { "NSMSG_SEARCH_MATCH", "Match: %s" },
251     { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
252     { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
253     { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
254     { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
255     { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
256     { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
257     { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
258     { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
259     { "NSMSG_RECLAIMED_KILL",  "Disconnected %s from the network." },
260     { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
261     { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
262     { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
263     { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an announcements value." },
264     { "NSMSG_SET_INFO", "$bINFO:         $b%s" },
265     { "NSMSG_SET_WIDTH", "$bWIDTH:        $b%d" },
266     { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH:   $b%d" },
267     { "NSMSG_SET_COLOR", "$bCOLOR:        $b%s" },
268     { "NSMSG_SET_PRIVMSG", "$bPRIVMSG:      $b%s" },
269     { "NSMSG_SET_STYLE", "$bSTYLE:        $b%s" },
270     { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
271     { "NSMSG_SET_PASSWORD", "$bPASSWORD:     $b%s" },
272     { "NSMSG_SET_FLAGS", "$bFLAGS:        $b%s" },
273     { "NSMSG_SET_EMAIL", "$bEMAIL:        $b%s" },
274     { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS:    $b%d" },
275     { "NSMSG_SET_LANGUAGE", "$bLANGUAGE:     $b%s" },
276     { "NSMSG_SET_LEVEL", "$bLEVEL:        $b%d" },
277     { "NSMSG_SET_EPITHET", "$bEPITHET:      $b%s" },
278     { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
279     { "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\nIf you did NOT request this account, you do not need to do anything.  Please contact the %1$s staff if you have questions." },
280     { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
281     { "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." },
282     { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
283     { "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." },
284     { "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." },
285     { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
286     { "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 %1$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." },
287     { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
288     { "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 %1$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." },
289     { "CHECKPASS_YES", "Yes." },
290     { "CHECKPASS_NO", "No." },
291     { NULL, NULL }
292 };
293
294 enum reclaim_action {
295     RECLAIM_NONE,
296     RECLAIM_WARN,
297     RECLAIM_SVSNICK,
298     RECLAIM_KILL
299 };
300 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
301 static void nickserv_reclaim_p(void *data);
302
303 static struct {
304     unsigned int disable_nicks : 1;
305     unsigned int valid_handle_regex_set : 1;
306     unsigned int valid_nick_regex_set : 1;
307     unsigned int autogag_enabled : 1;
308     unsigned int email_enabled : 1;
309     unsigned int email_required : 1;
310     unsigned int default_hostmask : 1;
311     unsigned int warn_nick_owned : 1;
312     unsigned int warn_clone_auth : 1;
313     unsigned long nicks_per_handle;
314     unsigned long password_min_length;
315     unsigned long password_min_digits;
316     unsigned long password_min_upper;
317     unsigned long password_min_lower;
318     unsigned long db_backup_frequency;
319     unsigned long handle_expire_frequency;
320     unsigned long autogag_duration;
321     unsigned long email_visible_level;
322     unsigned long cookie_timeout;
323     unsigned long handle_expire_delay;
324     unsigned long nochan_handle_expire_delay;
325     unsigned long modoper_level;
326     unsigned long set_epithet_level;
327     unsigned long handles_per_email;
328     unsigned long email_search_level;
329     const char *network_name;
330     const char *titlehost_suffix;
331     regex_t valid_handle_regex;
332     regex_t valid_nick_regex;
333     dict_t weak_password_dict;
334     struct policer_params *auth_policer_params;
335     enum reclaim_action reclaim_action;
336     enum reclaim_action auto_reclaim_action;
337     unsigned long auto_reclaim_delay;
338     unsigned char default_maxlogins;
339     unsigned char hard_maxlogins;
340 } nickserv_conf;
341
342 /* We have 2^32 unique account IDs to use. */
343 unsigned long int highest_id = 0;
344
345 static char *
346 canonicalize_hostmask(char *mask)
347 {
348     char *out = mask, *temp;
349     if ((temp = strchr(mask, '!'))) {
350         temp++;
351         while (*temp) *out++ = *temp++;
352         *out++ = 0;
353     }
354     return mask;
355 }
356
357 static struct handle_info *
358 register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
359 {
360     struct handle_info *hi;
361
362 #ifdef WITH_PROTOCOL_BAHAMUT
363     char id_base64[IDLEN + 1];
364     do
365     {
366         /* Assign a unique account ID to the account; note that 0 is
367            an invalid account ID. 1 is therefore the first account ID. */
368         if (!id) {
369             id = 1 + highest_id++;
370         } else {
371             /* Note: highest_id is and must always be the highest ID. */
372             if(id > highest_id) {
373                 highest_id = id;
374             }
375         }
376         inttobase64(id_base64, id, IDLEN);
377
378         /* Make sure an account with the same ID doesn't exist. If a
379            duplicate is found, log some details and assign a new one.
380            This should be impossible, but it never hurts to expect it. */
381         if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
382             log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
383             id = 0;
384         }
385     } while(!id);
386 #endif
387
388     hi = calloc(1, sizeof(*hi));
389     hi->userlist_style = HI_DEFAULT_STYLE;
390     hi->announcements = '?';
391     hi->handle = strdup(handle);
392     safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
393     hi->infoline = NULL;
394     dict_insert(nickserv_handle_dict, hi->handle, hi);
395
396 #ifdef WITH_PROTOCOL_BAHAMUT
397     hi->id = id;
398     dict_insert(nickserv_id_dict, strdup(id_base64), hi);
399 #endif
400
401     return hi;
402 }
403
404 static void
405 register_nick(const char *nick, struct handle_info *owner)
406 {
407     struct nick_info *ni;
408     ni = malloc(sizeof(struct nick_info));
409     safestrncpy(ni->nick, nick, sizeof(ni->nick));
410     ni->owner = owner;
411     ni->next = owner->nicks;
412     owner->nicks = ni;
413     dict_insert(nickserv_nick_dict, ni->nick, ni);
414 }
415
416 static void
417 free_nick_info(void *vni)
418 {
419     struct nick_info *ni = vni;
420     free(ni);
421 }
422
423 static void
424 delete_nick(struct nick_info *ni)
425 {
426     struct nick_info *last, *next;
427     struct userNode *user;
428     /* Check to see if we should mark a user as unregistered. */
429     if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
430         user->modes &= ~FLAGS_REGNICK;
431         irc_regnick(user);
432     }
433     /* Remove ni from the nick_info linked list. */
434     if (ni == ni->owner->nicks) {
435         ni->owner->nicks = ni->next;
436     } else {
437         last = ni->owner->nicks;
438         next = last->next;
439         while (next != ni) {
440             last = next;
441             next = last->next;
442         }
443         last->next = next->next;
444     }
445     dict_remove(nickserv_nick_dict, ni->nick);
446 }
447
448 static unreg_func_t *unreg_func_list;
449 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
450
451 void
452 reg_unreg_func(unreg_func_t func)
453 {
454     if (unreg_func_used == unreg_func_size) {
455         if (unreg_func_size) {
456             unreg_func_size <<= 1;
457             unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
458         } else {
459             unreg_func_size = 8;
460             unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
461         }
462     }
463     unreg_func_list[unreg_func_used++] = func;
464 }
465
466 static void
467 nickserv_free_cookie(void *data)
468 {
469     struct handle_cookie *cookie = data;
470     if (cookie->hi) cookie->hi->cookie = NULL;
471     if (cookie->data) free(cookie->data);
472     free(cookie);
473 }
474
475 static void
476 free_handle_info(void *vhi)
477 {
478     struct handle_info *hi = vhi;
479
480 #ifdef WITH_PROTOCOL_BAHAMUT
481     char id[IDLEN + 1];
482
483     inttobase64(id, hi->id, IDLEN);
484     dict_remove(nickserv_id_dict, id);
485 #endif
486
487     free_string_list(hi->masks);
488     assert(!hi->users);
489
490     while (hi->nicks)
491         delete_nick(hi->nicks);
492     free(hi->infoline);
493     free(hi->epithet);
494     if (hi->cookie) {
495         timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
496         nickserv_free_cookie(hi->cookie);
497     }
498     if (hi->email_addr) {
499         struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
500         handle_info_list_remove(hil, hi);
501         if (!hil->used)
502             dict_remove(nickserv_email_dict, hi->email_addr);
503     }
504     free(hi);
505 }
506
507 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
508
509 static void
510 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
511 {
512     unsigned int n;
513
514     for (n=0; n<unreg_func_used; n++)
515         unreg_func_list[n](notify, hi);
516     while (hi->users)
517         set_user_handle_info(hi->users, NULL, 0);
518     if (notify) {
519         if (nickserv_conf.disable_nicks)
520             send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
521         else
522             send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
523     }
524     dict_remove(nickserv_handle_dict, hi->handle);
525 }
526
527 struct handle_info*
528 get_handle_info(const char *handle)
529 {
530     return dict_find(nickserv_handle_dict, handle, 0);
531 }
532
533 struct nick_info*
534 get_nick_info(const char *nick)
535 {
536     return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
537 }
538
539 struct modeNode *
540 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
541 {
542     unsigned int nn;
543     struct modeNode *mn;
544
545     for (nn=0; nn<channel->members.used; ++nn) {
546         mn = channel->members.list[nn];
547         if ((mn->user != except) && (mn->user->handle_info == handle))
548             return mn;
549     }
550     return NULL;
551 }
552
553 int
554 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
555     if (!user->handle_info) {
556         if (!quiet)
557             send_message(user, bot, "MSG_AUTHENTICATE");
558         return 0;
559     }
560
561     if (!IsOper(user) && (!IsHelping(user) || min_level)) {
562         if (!quiet)
563             send_message(user, bot, "NSMSG_NO_ACCESS");
564         return 0;
565     }
566
567     if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
568         if (!quiet)
569             send_message(user, bot, "MSG_OPER_SUSPENDED");
570         return 0;
571     }
572
573     if (user->handle_info->opserv_level < min_level) {
574         if (!quiet)
575             send_message(user, bot, "NSMSG_NO_ACCESS");
576         return 0;
577     }
578
579     return 1;
580 }
581
582 static int
583 is_valid_handle(const char *handle)
584 {
585     struct userNode *user;
586     /* cant register a juped nick/service nick as handle, to prevent confusion */
587     user = GetUserH(handle);
588     if (user && IsLocal(user))
589         return 0;
590     /* check against maximum length */
591     if (strlen(handle) > NICKSERV_HANDLE_LEN)
592         return 0;
593     /* for consistency, only allow account names that could be nicks */
594     if (!is_valid_nick(handle))
595         return 0;
596     /* disallow account names that look like bad words */
597     if (opserv_bad_channel(handle))
598         return 0;
599     /* test either regex or containing all valid chars */
600     if (nickserv_conf.valid_handle_regex_set) {
601         int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
602         if (err) {
603             char buff[256];
604             buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
605             log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
606         }
607         return !err;
608     } else {
609         return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
610     }
611 }
612
613 static int
614 is_registerable_nick(const char *nick)
615 {
616     /* make sure it could be used as an account name */
617     if (!is_valid_handle(nick))
618         return 0;
619     /* check length */
620     if (strlen(nick) > NICKLEN)
621         return 0;
622     /* test either regex or as valid handle */
623     if (nickserv_conf.valid_nick_regex_set) {
624         int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
625         if (err) {
626             char buff[256];
627             buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
628             log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
629         }
630         return !err;
631     }
632     return 1;
633 }
634
635 static int
636 is_valid_email_addr(const char *email)
637 {
638     return strchr(email, '@') != NULL;
639 }
640
641 static const char *
642 visible_email_addr(struct userNode *user, struct handle_info *hi)
643 {
644     if (hi->email_addr) {
645         if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
646             return hi->email_addr;
647         } else {
648             return "Set.";
649         }
650     } else {
651         return "Not set.";
652     }
653 }
654
655 struct handle_info *
656 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
657 {
658     struct handle_info *hi;
659     struct userNode *target;
660
661     switch (*name) {
662     case '*':
663         if (!(hi = get_handle_info(++name))) {
664             send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
665             return 0;
666         }
667         return hi;
668     default:
669         if (!(target = GetUserH(name))) {
670             send_message(user, service, "MSG_NICK_UNKNOWN", name);
671             return 0;
672         }
673         if (IsLocal(target)) {
674             send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
675             return 0;
676         }
677         if (!(hi = target->handle_info)) {
678             send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
679             return 0;
680         }
681         return hi;
682     }
683 }
684
685 int
686 oper_outranks(struct userNode *user, struct handle_info *hi) {
687     if (user->handle_info->opserv_level > hi->opserv_level)
688         return 1;
689     if (user->handle_info->opserv_level == hi->opserv_level) {
690         if ((user->handle_info->opserv_level == 1000)
691             || (user->handle_info == hi)
692             || ((user->handle_info->opserv_level == 0)
693                 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
694                 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
695             return 1;
696         }
697     }
698     send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
699     return 0;
700 }
701
702 static struct handle_info *
703 get_victim_oper(struct userNode *user, const char *target)
704 {
705     struct handle_info *hi;
706     if (!(hi = smart_get_handle_info(nickserv, user, target)))
707         return 0;
708     if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
709         send_message(user, nickserv, "MSG_OPER_SUSPENDED");
710         return 0;
711     }
712     return oper_outranks(user, hi) ? hi : NULL;
713 }
714
715 static int
716 valid_user_for(struct userNode *user, struct handle_info *hi)
717 {
718     unsigned int ii;
719
720     /* If no hostmasks on the account, allow it. */
721     if (!hi->masks->used)
722         return 1;
723     /* If any hostmask matches, allow it. */
724     for (ii=0; ii<hi->masks->used; ii++)
725         if (user_matches_glob(user, hi->masks->list[ii], 0))
726             return 1;
727     /* If they are allowauthed to this account, allow it (removing the aa). */
728     if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
729         dict_remove(nickserv_allow_auth_dict, user->nick);
730         return 2;
731     }
732     /* The user is not allowed to use this account. */
733     return 0;
734 }
735
736 static int
737 is_secure_password(const char *handle, const char *pass, struct userNode *user)
738 {
739     unsigned int i, len;
740     unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
741     len = strlen(pass);
742     if (len < nickserv_conf.password_min_length) {
743         if (user)
744             send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
745         return 0;
746     }
747     if (!irccasecmp(pass, handle)) {
748         if (user)
749             send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
750         return 0;
751     }
752     dict_find(nickserv_conf.weak_password_dict, pass, &i);
753     if (i) {
754         if (user)
755             send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
756         return 0;
757     }
758     for (i=0; i<len; i++) {
759         if (isdigit(pass[i]))
760             cnt_digits++;
761         if (isupper(pass[i]))
762             cnt_upper++;
763         if (islower(pass[i]))
764             cnt_lower++;
765     }
766     if ((cnt_lower < nickserv_conf.password_min_lower)
767         || (cnt_upper < nickserv_conf.password_min_upper)
768         || (cnt_digits < nickserv_conf.password_min_digits)) {
769         if (user)
770             send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
771         return 0;
772     }
773     return 1;
774 }
775
776 static auth_func_t *auth_func_list;
777 static unsigned int auth_func_size = 0, auth_func_used = 0;
778
779 void
780 reg_auth_func(auth_func_t func)
781 {
782     if (auth_func_used == auth_func_size) {
783         if (auth_func_size) {
784             auth_func_size <<= 1;
785             auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
786         } else {
787             auth_func_size = 8;
788             auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
789         }
790     }
791     auth_func_list[auth_func_used++] = func;
792 }
793
794 static handle_rename_func_t *rf_list;
795 static unsigned int rf_list_size, rf_list_used;
796
797 void
798 reg_handle_rename_func(handle_rename_func_t func)
799 {
800     if (rf_list_used == rf_list_size) {
801         if (rf_list_size) {
802             rf_list_size <<= 1;
803             rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
804         } else {
805             rf_list_size = 8;
806             rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
807         }
808     }
809     rf_list[rf_list_used++] = func;
810 }
811
812 static void
813 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
814 {
815     unsigned int n;
816     struct handle_info *old_info;
817
818     /* This can happen if somebody uses COOKIE while authed, or if
819      * they re-auth to their current handle (which is silly, but users
820      * are like that). */
821     if (user->handle_info == hi)
822         return;
823
824     if (user->handle_info) {
825         struct userNode *other;
826
827         if (IsHelper(user))
828             userList_remove(&curr_helpers, user);
829
830         /* remove from next_authed linked list */
831         if (user->handle_info->users == user) {
832             user->handle_info->users = user->next_authed;
833         } else {
834             for (other = user->handle_info->users;
835                  other->next_authed != user;
836                  other = other->next_authed) ;
837             other->next_authed = user->next_authed;
838         }
839         /* if nobody left on old handle, and they're not an oper, remove !god */
840         if (!user->handle_info->users && !user->handle_info->opserv_level)
841             HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
842         /* record them as being last seen at this time */
843         user->handle_info->lastseen = now;
844         /* and record their hostmask */
845         snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
846     }
847     old_info = user->handle_info;
848     user->handle_info = hi;
849     if (hi && !hi->users && !hi->opserv_level)
850         HANDLE_CLEAR_FLAG(hi, HELPING);
851     for (n=0; n<auth_func_used; n++)
852         auth_func_list[n](user, old_info);
853     if (hi) {
854         struct nick_info *ni;
855
856         HANDLE_CLEAR_FLAG(hi, FROZEN);
857         if (nickserv_conf.warn_clone_auth) {
858             struct userNode *other;
859             for (other = hi->users; other; other = other->next_authed)
860                 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
861         }
862         user->next_authed = hi->users;
863         hi->users = user;
864         hi->lastseen = now;
865         if (IsHelper(user))
866             userList_append(&curr_helpers, user);
867
868         if (stamp) {
869 #ifdef WITH_PROTOCOL_BAHAMUT
870             /* Stamp users with their account ID. */
871             char id[IDLEN + 1];
872             inttobase64(id, hi->id, IDLEN);
873 #elif WITH_PROTOCOL_P10
874             /* Stamp users with their account name. */
875             char *id = hi->handle;
876 #else
877             const char *id = "???";
878 #endif
879             if (!nickserv_conf.disable_nicks) {
880                 struct nick_info *ni;
881                 for (ni = hi->nicks; ni; ni = ni->next) {
882                     if (!irccasecmp(user->nick, ni->nick)) {
883                         user->modes |= FLAGS_REGNICK;
884                         break;
885                     }
886                 }
887             }
888             StampUser(user, id);
889         }
890
891         if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
892             timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
893     } else {
894         /* We cannot clear the user's account ID, unfortunately. */
895         user->next_authed = NULL;
896     }
897 }
898
899 static struct handle_info*
900 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
901 {
902     struct handle_info *hi;
903     struct nick_info *ni;
904     char crypted[MD5_CRYPT_LENGTH];
905
906     if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
907         send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
908         return 0;
909     }
910
911     if (!is_secure_password(handle, passwd, user))
912         return 0;
913
914     cryptpass(passwd, crypted);
915     hi = register_handle(handle, crypted, 0);
916     hi->masks = alloc_string_list(1);
917     hi->users = NULL;
918     hi->registered = now;
919     hi->lastseen = now;
920     hi->flags = HI_DEFAULT_FLAGS;
921     if (settee && !no_auth)
922         set_user_handle_info(settee, hi, 1);
923
924     if (user != settee)
925         send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
926     else if (nickserv_conf.disable_nicks)
927         send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
928     else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
929         send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
930     else {
931         register_nick(user->nick, hi);
932         send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
933     }
934     if (settee && (user != settee))
935         send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
936     return hi;
937 }
938
939 static void
940 nickserv_bake_cookie(struct handle_cookie *cookie)
941 {
942     cookie->hi->cookie = cookie;
943     timeq_add(cookie->expires, nickserv_free_cookie, cookie);
944 }
945
946 static void
947 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
948 {
949     struct handle_cookie *cookie;
950     char subject[128], body[4096], *misc;
951     const char *netname, *fmt;
952     int first_time = 0;
953
954     if (hi->cookie) {
955         send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
956         return;
957     }
958
959     cookie = calloc(1, sizeof(*cookie));
960     cookie->hi = hi;
961     cookie->type = type;
962     cookie->data = cookie_data ? strdup(cookie_data) : NULL;
963     cookie->expires = now + nickserv_conf.cookie_timeout;
964     inttobase64(cookie->cookie, rand(), 5);
965     inttobase64(cookie->cookie+5, rand(), 5);
966
967     netname = nickserv_conf.network_name;
968     subject[0] = 0;
969
970     switch (cookie->type) {
971     case ACTIVATION:
972         hi->passwd[0] = 0; /* invalidate password */
973         send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
974         fmt = user_find_message(user, "NSEMAIL_ACTIVATION_SUBJECT");
975         snprintf(subject, sizeof(subject), fmt, netname);
976         fmt = user_find_message(user, "NSEMAIL_ACTIVATION_BODY");
977         snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
978         first_time = 1;
979         break;
980     case PASSWORD_CHANGE:
981         send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
982         fmt = user_find_message(user, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
983         snprintf(subject, sizeof(subject), fmt, netname);
984         fmt = user_find_message(user, "NSEMAIL_PASSWORD_CHANGE_BODY");
985         snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
986         break;
987     case EMAIL_CHANGE:
988         misc = hi->email_addr;
989         hi->email_addr = cookie->data;
990         if (misc) {
991             send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
992             fmt = user_find_message(user, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
993             snprintf(subject, sizeof(subject), fmt, netname);
994             fmt = user_find_message(user, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
995             snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
996             sendmail(nickserv, hi, subject, body, 1);
997             fmt = user_find_message(user, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
998             snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
999         } else {
1000             send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1001             fmt = user_find_message(user, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1002             snprintf(subject, sizeof(subject), fmt, netname);
1003             fmt = user_find_message(user, "NSEMAIL_EMAIL_VERIFY_BODY");
1004             snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1005             sendmail(nickserv, hi, subject, body, 1);
1006             subject[0] = 0;
1007         }
1008         hi->email_addr = misc;
1009         break;
1010     case ALLOWAUTH:
1011         fmt = user_find_message(user, "NSEMAIL_ALLOWAUTH_SUBJECT");
1012         snprintf(subject, sizeof(subject), fmt, netname);
1013         fmt = user_find_message(user, "NSEMAIL_ALLOWAUTH_BODY");
1014         snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1015         break;
1016     default:
1017         log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1018         break;
1019     }
1020     if (subject[0])
1021         sendmail(nickserv, hi, subject, body, first_time);
1022     nickserv_bake_cookie(cookie);
1023 }
1024
1025 static void
1026 nickserv_eat_cookie(struct handle_cookie *cookie)
1027 {
1028     cookie->hi->cookie = NULL;
1029     timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1030     nickserv_free_cookie(cookie);
1031 }
1032
1033 static void
1034 nickserv_free_email_addr(void *data)
1035 {
1036     handle_info_list_clean(data);
1037     free(data);
1038 }
1039
1040 static void
1041 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1042 {
1043     struct handle_info_list *hil;
1044     /* Remove from old handle_info_list ... */
1045     if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1046         handle_info_list_remove(hil, hi);
1047         if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1048         hi->email_addr = NULL;
1049     }
1050     /* Add to the new list.. */
1051     if (new_email_addr) {
1052         if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1053             hil = calloc(1, sizeof(*hil));
1054             hil->tag = strdup(new_email_addr);
1055             handle_info_list_init(hil);
1056             dict_insert(nickserv_email_dict, hil->tag, hil);
1057         }
1058         handle_info_list_append(hil, hi);
1059         hi->email_addr = hil->tag;
1060     }
1061 }
1062
1063 static NICKSERV_FUNC(cmd_register)
1064 {
1065     struct handle_info *hi;
1066     const char *email_addr, *password;
1067     int no_auth;
1068
1069     if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1070         /* Require the first handle registered to belong to someone +o. */
1071         reply("NSMSG_REQUIRE_OPER");
1072         return 0;
1073     }
1074
1075     if (user->handle_info) {
1076         reply("NSMSG_USE_RENAME", user->handle_info->handle);
1077         return 0;
1078     }
1079
1080     if (IsStamped(user)) {
1081         /* Unauthenticated users might still have been stamped
1082            previously and could therefore have a hidden host;
1083            do not allow them to register a new account. */
1084         reply("NSMSG_STAMPED_REGISTER");
1085         return 0;
1086     }
1087
1088     NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1089
1090     if (!is_valid_handle(argv[1])) {
1091         reply("NSMSG_BAD_HANDLE", argv[1]);
1092         return 0;
1093     }
1094
1095     if ((argc >= 4) && nickserv_conf.email_enabled) {
1096         struct handle_info_list *hil;
1097         const char *str;
1098
1099         /* Remember email address. */
1100         email_addr = argv[3];
1101
1102         /* Check that the email address looks valid.. */
1103         if (!is_valid_email_addr(email_addr)) {
1104             reply("NSMSG_BAD_EMAIL_ADDR");
1105             return 0;
1106         }
1107
1108         /* .. and that we are allowed to send to it. */
1109         if ((str = sendmail_prohibited_address(email_addr))) {
1110             reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1111             return 0;
1112         }
1113
1114         /* If we do email verify, make sure we don't spam the address. */
1115         if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1116             unsigned int nn;
1117             for (nn=0; nn<hil->used; nn++) {
1118                 if (hil->list[nn]->cookie) {
1119                     reply("NSMSG_EMAIL_UNACTIVATED");
1120                     return 0;
1121                 }
1122             }
1123             if (hil->used >= nickserv_conf.handles_per_email) {
1124                 reply("NSMSG_EMAIL_OVERUSED");
1125                 return 0;
1126             }
1127         }
1128
1129         no_auth = 1;
1130     } else {
1131         email_addr = 0;
1132         no_auth = 0;
1133     }
1134
1135     password = argv[2];
1136     argv[2] = "****";
1137     if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1138         return 0;
1139     /* Add any masks they should get. */
1140     if (nickserv_conf.default_hostmask) {
1141         string_list_append(hi->masks, strdup("*@*"));
1142     } else {
1143         string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1144         if (user->ip.s_addr && user->hostname[strspn(user->hostname, "0123456789.")])
1145             string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1146     }
1147
1148     /* If they're the first to register, give them level 1000. */
1149     if (dict_size(nickserv_handle_dict) == 1) {
1150         hi->opserv_level = 1000;
1151         reply("NSMSG_ROOT_HANDLE", argv[1]);
1152     }
1153
1154     /* Set their email address. */
1155     if (email_addr)
1156         nickserv_set_email_addr(hi, email_addr);
1157
1158     /* If they need to do email verification, tell them. */
1159     if (no_auth)
1160         nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1161
1162     return 1;
1163 }
1164
1165 static NICKSERV_FUNC(cmd_oregister)
1166 {
1167     char *mask;
1168     struct userNode *settee;
1169     struct handle_info *hi;
1170
1171     NICKSERV_MIN_PARMS(4);
1172
1173     if (!is_valid_handle(argv[1])) {
1174         reply("NSMSG_BAD_HANDLE", argv[1]);
1175         return 0;
1176     }
1177
1178     if (strchr(argv[3], '@')) {
1179         mask = canonicalize_hostmask(strdup(argv[3]));
1180         if (argc > 4) {
1181             settee = GetUserH(argv[4]);
1182             if (!settee) {
1183                 reply("MSG_NICK_UNKNOWN", argv[4]);
1184                 free(mask);
1185                 return 0;
1186             }
1187         } else {
1188             settee = NULL;
1189         }
1190     } else if ((settee = GetUserH(argv[3]))) {
1191         mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1192     } else {
1193         reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1194         return 0;
1195     }
1196     if (settee && settee->handle_info) {
1197         reply("NSMSG_USER_PREV_AUTH", settee->nick);
1198         free(mask);
1199         return 0;
1200     }
1201     if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1202         free(mask);
1203         return 0;
1204     }
1205     string_list_append(hi->masks, mask);
1206     return 1;
1207 }
1208
1209 static NICKSERV_FUNC(cmd_handleinfo)
1210 {
1211     char buff[400];
1212     unsigned int i, pos=0, herelen;
1213     struct userNode *target, *next_un;
1214     struct handle_info *hi;
1215     const char *nsmsg_none;
1216
1217     if (argc < 2) {
1218         if (!(hi = user->handle_info)) {
1219             reply("NSMSG_MUST_AUTH");
1220             return 0;
1221         }
1222     } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1223         return 0;
1224     }
1225
1226     nsmsg_none = handle_find_message(hi, "MSG_NONE");
1227     reply("NSMSG_HANDLEINFO_ON", hi->handle);
1228 #ifdef WITH_PROTOCOL_BAHAMUT
1229     reply("NSMSG_HANDLEINFO_ID", hi->id);
1230 #endif
1231     reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1232
1233     if (!hi->users) {
1234         intervalString(buff, now - hi->lastseen);
1235         reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1236     } else {
1237         reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1238     }
1239
1240     reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1241     if (HANDLE_FLAGGED(hi, FROZEN))
1242         reply("NSMSG_HANDLEINFO_VACATION");
1243
1244     if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1245         struct do_not_register *dnr;
1246         if ((dnr = chanserv_is_dnr(NULL, hi)))
1247             reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1248         if (!oper_outranks(user, hi))
1249             return 1;
1250     } else if (hi != user->handle_info)
1251         return 1;
1252
1253     if (nickserv_conf.email_enabled)
1254         reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1255
1256     if (hi->cookie) {
1257         const char *type;
1258         switch (hi->cookie->type) {
1259         case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1260         case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1261         case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1262         case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1263         default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1264         }
1265         reply(type);
1266     }
1267
1268     if (hi->flags) {
1269         unsigned long flen = 1;
1270         char flags[34]; /* 32 bits possible plus '+' and '\0' */
1271         flags[0] = '+';
1272         for (i=0, flen=1; handle_flags[i]; i++)
1273             if (hi->flags & 1 << i)
1274                 flags[flen++] = handle_flags[i];
1275         flags[flen] = 0;
1276         reply("NSMSG_HANDLEINFO_FLAGS", flags);
1277     } else {
1278         reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1279     }
1280
1281     if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1282         || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1283         || (hi->opserv_level > 0)) {
1284         reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1285     }
1286
1287     if (hi->last_quit_host[0])
1288         reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1289     else
1290         reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1291
1292     if (nickserv_conf.disable_nicks) {
1293         /* nicks disabled; don't show anything about registered nicks */
1294     } else if (hi->nicks) {
1295         struct nick_info *ni, *next_ni;
1296         for (ni = hi->nicks; ni; ni = next_ni) {
1297             herelen = strlen(ni->nick);
1298             if (pos + herelen + 1 > ArrayLength(buff)) {
1299                 next_ni = ni;
1300                 goto print_nicks_buff;
1301             } else {
1302                 next_ni = ni->next;
1303             }
1304             memcpy(buff+pos, ni->nick, herelen);
1305             pos += herelen; buff[pos++] = ' ';
1306             if (!next_ni) {
1307               print_nicks_buff:
1308                 buff[pos-1] = 0;
1309                 reply("NSMSG_HANDLEINFO_NICKS", buff);
1310                 pos = 0;
1311             }
1312         }
1313     } else {
1314         reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1315     }
1316
1317     if (hi->masks->used) {
1318         for (i=0; i < hi->masks->used; i++) {
1319             herelen = strlen(hi->masks->list[i]);
1320             if (pos + herelen + 1 > ArrayLength(buff)) {
1321                 i--;
1322                 goto print_mask_buff;
1323             }
1324             memcpy(buff+pos, hi->masks->list[i], herelen);
1325             pos += herelen; buff[pos++] = ' ';
1326             if (i+1 == hi->masks->used) {
1327               print_mask_buff:
1328                 buff[pos-1] = 0;
1329                 reply("NSMSG_HANDLEINFO_MASKS", buff);
1330                 pos = 0;
1331             }
1332         }
1333     } else {
1334         reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1335     }
1336
1337     if (hi->channels) {
1338         struct userData *channel, *next;
1339         char *name;
1340
1341         for (channel = hi->channels; channel; channel = next) {
1342             next = channel->u_next;
1343             name = channel->channel->channel->name;
1344             herelen = strlen(name);
1345             if (pos + herelen + 7 > ArrayLength(buff)) {
1346                 next = channel;
1347                 goto print_chans_buff;
1348             }
1349             if (IsUserSuspended(channel))
1350                 buff[pos++] = '-';
1351             pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1352             if (next == NULL) {
1353               print_chans_buff:
1354                 buff[pos-1] = 0;
1355                 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1356                 pos = 0;
1357             }
1358         }
1359     } else {
1360         reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1361     }
1362
1363     for (target = hi->users; target; target = next_un) {
1364         herelen = strlen(target->nick);
1365         if (pos + herelen + 1 > ArrayLength(buff)) {
1366             next_un = target;
1367             goto print_cnick_buff;
1368         } else {
1369             next_un = target->next_authed;
1370         }
1371         memcpy(buff+pos, target->nick, herelen);
1372         pos += herelen; buff[pos++] = ' ';
1373         if (!next_un) {
1374           print_cnick_buff:
1375             buff[pos-1] = 0;
1376             reply("NSMSG_HANDLEINFO_CURRENT", buff);
1377             pos = 0;
1378         }
1379     }
1380
1381     return 1;
1382 }
1383
1384 static NICKSERV_FUNC(cmd_userinfo)
1385 {
1386     struct userNode *target;
1387
1388     NICKSERV_MIN_PARMS(2);
1389     if (!(target = GetUserH(argv[1]))) {
1390         reply("MSG_NICK_UNKNOWN", argv[1]);
1391         return 0;
1392     }
1393     if (target->handle_info)
1394         reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1395     else
1396         reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1397     return 1;
1398 }
1399
1400 static NICKSERV_FUNC(cmd_nickinfo)
1401 {
1402     struct nick_info *ni;
1403
1404     NICKSERV_MIN_PARMS(2);
1405     if (!(ni = get_nick_info(argv[1]))) {
1406         reply("MSG_NICK_UNKNOWN", argv[1]);
1407         return 0;
1408     }
1409     reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1410     return 1;
1411 }
1412
1413 static NICKSERV_FUNC(cmd_rename_handle)
1414 {
1415     struct handle_info *hi;
1416     char msgbuf[MAXLEN], *old_handle;
1417     unsigned int nn;
1418
1419     NICKSERV_MIN_PARMS(3);
1420     if (!(hi = get_victim_oper(user, argv[1])))
1421         return 0;
1422     if (!is_valid_handle(argv[2])) {
1423         reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1424         return 0;
1425     }
1426     if (get_handle_info(argv[2])) {
1427         reply("NSMSG_HANDLE_EXISTS", argv[2]);
1428         return 0;
1429     }
1430
1431     dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1432     hi->handle = strdup(argv[2]);
1433     dict_insert(nickserv_handle_dict, hi->handle, hi);
1434     for (nn=0; nn<rf_list_used; nn++)
1435         rf_list[nn](hi, old_handle);
1436     snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1437     reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1438     global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1439     free(old_handle);
1440     return 1;
1441 }
1442
1443 static failpw_func_t *failpw_func_list;
1444 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1445
1446 void
1447 reg_failpw_func(failpw_func_t func)
1448 {
1449     if (failpw_func_used == failpw_func_size) {
1450         if (failpw_func_size) {
1451             failpw_func_size <<= 1;
1452             failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1453         } else {
1454             failpw_func_size = 8;
1455             failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1456         }
1457     }
1458     failpw_func_list[failpw_func_used++] = func;
1459 }
1460
1461 static NICKSERV_FUNC(cmd_auth)
1462 {
1463     int pw_arg, used, maxlogins;
1464     struct handle_info *hi;
1465     const char *passwd;
1466     struct userNode *other;
1467
1468     if (user->handle_info) {
1469         reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1470         return 0;
1471     }
1472     if (IsStamped(user)) {
1473         /* Unauthenticated users might still have been stamped
1474            previously and could therefore have a hidden host;
1475            do not allow them to authenticate. */
1476         reply("NSMSG_STAMPED_AUTH");
1477         return 0;
1478     }
1479     if (argc == 3) {
1480         hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1481         pw_arg = 2;
1482     } else if (argc == 2) {
1483         if (nickserv_conf.disable_nicks) {
1484             if (!(hi = get_handle_info(user->nick))) {
1485                 reply("NSMSG_HANDLE_NOT_FOUND");
1486                 return 0;
1487             }
1488         } else {
1489             /* try to look up their handle from their nick */
1490             struct nick_info *ni;
1491             ni = get_nick_info(user->nick);
1492             if (!ni) {
1493                 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1494                 return 0;
1495             }
1496             hi = ni->owner;
1497         }
1498         pw_arg = 1;
1499     } else {
1500         reply("MSG_MISSING_PARAMS", argv[0]);
1501         svccmd_send_help(user, nickserv, cmd);
1502         return 0;
1503     }
1504     if (!hi) {
1505         reply("NSMSG_HANDLE_NOT_FOUND");
1506         return 0;
1507     }
1508     passwd = argv[pw_arg];
1509     if (!valid_user_for(user, hi)) {
1510         if (hi->email_addr && nickserv_conf.email_enabled)
1511             reply("NSMSG_USE_AUTHCOOKIE", hi->handle, hi->handle);
1512         else
1513             reply("NSMSG_HOSTMASK_INVALID", hi->handle);
1514         argv[pw_arg] = "BADMASK";
1515         return 1;
1516     }
1517     if (!checkpass(passwd, hi->passwd)) {
1518         unsigned int n;
1519         reply("NSMSG_PASSWORD_INVALID");
1520         argv[pw_arg] = "BADPASS";
1521         for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1522         if (nickserv_conf.autogag_enabled) {
1523             if (!user->auth_policer.params) {
1524                 user->auth_policer.last_req = now;
1525                 user->auth_policer.params = nickserv_conf.auth_policer_params;
1526             }
1527             if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1528                 char *hostmask;
1529                 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1530                 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1531                 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1532                 free(hostmask);
1533                 argv[pw_arg] = "GAGGED";
1534             }
1535         }
1536         return 1;
1537     }
1538     if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1539         reply("NSMSG_HANDLE_SUSPENDED");
1540         argv[pw_arg] = "SUSPENDED";
1541         return 1;
1542     }
1543     maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1544     for (used = 0, other = hi->users; other; other = other->next_authed) {
1545         if (++used >= maxlogins) {
1546             reply("NSMSG_MAX_LOGINS", maxlogins);
1547             argv[pw_arg] = "MAXLOGINS";
1548             return 1;
1549         }
1550     }
1551
1552     if (nickserv_conf.email_required && !hi->email_addr)
1553         reply("NSMSG_PLEASE_SET_EMAIL");
1554     if (!is_secure_password(hi->handle, passwd, NULL))
1555         reply("NSMSG_WEAK_PASSWORD");
1556     if (hi->passwd[0] != '$')
1557         cryptpass(passwd, hi->passwd);
1558
1559     reply("NSMSG_AUTH_SUCCESS");
1560     argv[pw_arg] = "****";
1561     set_user_handle_info(user, hi, 1);
1562     return 1;
1563 }
1564
1565 static allowauth_func_t *allowauth_func_list;
1566 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1567
1568 void
1569 reg_allowauth_func(allowauth_func_t func)
1570 {
1571     if (allowauth_func_used == allowauth_func_size) {
1572         if (allowauth_func_size) {
1573             allowauth_func_size <<= 1;
1574             allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1575         } else {
1576             allowauth_func_size = 8;
1577             allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1578         }
1579     }
1580     allowauth_func_list[allowauth_func_used++] = func;
1581 }
1582
1583 static NICKSERV_FUNC(cmd_allowauth)
1584 {
1585     struct userNode *target;
1586     struct handle_info *hi;
1587     unsigned int n;
1588
1589     NICKSERV_MIN_PARMS(2);
1590     if (!(target = GetUserH(argv[1]))) {
1591         reply("MSG_NICK_UNKNOWN", argv[1]);
1592         return 0;
1593     }
1594     if (target->handle_info) {
1595         reply("NSMSG_USER_PREV_AUTH", target->nick);
1596         return 0;
1597     }
1598     if (IsStamped(target)) {
1599         /* Unauthenticated users might still have been stamped
1600            previously and could therefore have a hidden host;
1601            do not allow them to authenticate to an account. */
1602         send_message(target, nickserv, "NSMSG_USER_PREV_STAMP", target->nick);
1603         return 0;
1604     }
1605     if (argc == 2)
1606         hi = NULL;
1607     else if (!(hi = get_handle_info(argv[2]))) {
1608         reply("MSG_HANDLE_UNKNOWN", argv[2]);
1609         return 0;
1610     }
1611     if (hi) {
1612         if (hi->opserv_level > user->handle_info->opserv_level) {
1613             reply("MSG_USER_OUTRANKED", hi->handle);
1614             return 0;
1615         }
1616         if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1617              || (hi->opserv_level > 0))
1618             && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1619             reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1620             return 0;
1621         }
1622         dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1623         reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1624         send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1625         if (nickserv_conf.email_enabled)
1626             send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1627     } else {
1628         if (dict_remove(nickserv_allow_auth_dict, target->nick))
1629             reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1630         else
1631             reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1632     }
1633     for (n=0; n<allowauth_func_used; n++)
1634         allowauth_func_list[n](user, target, hi);
1635     return 1;
1636 }
1637
1638 static NICKSERV_FUNC(cmd_authcookie)
1639 {
1640     struct handle_info *hi;
1641
1642     NICKSERV_MIN_PARMS(2);
1643     if (user->handle_info) {
1644         reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1645         return 0;
1646     }
1647     if (IsStamped(user)) {
1648         /* Unauthenticated users might still have been stamped
1649            previously and could therefore have a hidden host;
1650            do not allow them to authenticate to an account. */
1651         reply("NSMSG_STAMPED_AUTHCOOKIE");
1652         return 0;
1653     }
1654     if (!(hi = get_handle_info(argv[1]))) {
1655         reply("MSG_HANDLE_UNKNOWN", argv[1]);
1656         return 0;
1657     }
1658     if (!hi->email_addr) {
1659         reply("MSG_SET_EMAIL_ADDR");
1660         return 0;
1661     }
1662     nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1663     reply("NSMSG_USE_COOKIE_AUTH");
1664     return 1;
1665 }
1666
1667 static NICKSERV_FUNC(cmd_delcookie)
1668 {
1669     struct handle_info *hi;
1670
1671     hi = user->handle_info;
1672     if (!hi->cookie) {
1673         reply("NSMSG_NO_COOKIE");
1674         return 0;
1675     }
1676     switch (hi->cookie->type) {
1677     case ACTIVATION:
1678     case EMAIL_CHANGE:
1679         reply("NSMSG_MUST_TIME_OUT");
1680         break;
1681     default:
1682         nickserv_eat_cookie(hi->cookie);
1683         reply("NSMSG_ATE_COOKIE");
1684         break;
1685     }
1686     return 1;
1687 }
1688
1689 static NICKSERV_FUNC(cmd_resetpass)
1690 {
1691     struct handle_info *hi;
1692     char crypted[MD5_CRYPT_LENGTH];
1693
1694     NICKSERV_MIN_PARMS(3);
1695     if (user->handle_info) {
1696         reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1697         return 0;
1698     }
1699     if (IsStamped(user)) {
1700         /* Unauthenticated users might still have been stamped
1701            previously and could therefore have a hidden host;
1702            do not allow them to activate an account. */
1703         reply("NSMSG_STAMPED_RESETPASS");
1704         return 0;
1705     }
1706     if (!(hi = get_handle_info(argv[1]))) {
1707         reply("MSG_HANDLE_UNKNOWN", argv[1]);
1708         return 0;
1709     }
1710     if (!hi->email_addr) {
1711         reply("MSG_SET_EMAIL_ADDR");
1712         return 0;
1713     }
1714     cryptpass(argv[2], crypted);
1715     argv[2] = "****";
1716     nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1717     return 1;
1718 }
1719
1720 static NICKSERV_FUNC(cmd_cookie)
1721 {
1722     struct handle_info *hi;
1723     const char *cookie;
1724
1725     if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1726         cookie = argv[1];
1727     } else {
1728         NICKSERV_MIN_PARMS(3);
1729         if (!(hi = get_handle_info(argv[1]))) {
1730             reply("MSG_HANDLE_UNKNOWN", argv[1]);
1731             return 0;
1732         }
1733         cookie = argv[2];
1734     }
1735
1736     if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1737         reply("NSMSG_HANDLE_SUSPENDED");
1738         return 0;
1739     }
1740
1741     if (!hi->cookie) {
1742         reply("NSMSG_NO_COOKIE");
1743         return 0;
1744     }
1745
1746     /* Check validity of operation before comparing cookie to
1747      * prohibit guessing by authed users. */
1748     if (user->handle_info
1749         && (hi->cookie->type != EMAIL_CHANGE)
1750         && (hi->cookie->type != PASSWORD_CHANGE)) {
1751         reply("NSMSG_CANNOT_COOKIE");
1752         return 0;
1753     }
1754
1755     if (strcmp(cookie, hi->cookie->cookie)) {
1756         reply("NSMSG_BAD_COOKIE");
1757         return 0;
1758     }
1759
1760     switch (hi->cookie->type) {
1761     case ACTIVATION:
1762         safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1763         set_user_handle_info(user, hi, 1);
1764         reply("NSMSG_HANDLE_ACTIVATED");
1765         break;
1766     case PASSWORD_CHANGE:
1767         set_user_handle_info(user, hi, 1);
1768         safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1769         reply("NSMSG_PASSWORD_CHANGED");
1770         break;
1771     case EMAIL_CHANGE:
1772         nickserv_set_email_addr(hi, hi->cookie->data);
1773         reply("NSMSG_EMAIL_CHANGED");
1774         break;
1775     case ALLOWAUTH:
1776         set_user_handle_info(user, hi, 1);
1777         reply("NSMSG_AUTH_SUCCESS");
1778         break;
1779     default:
1780         reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1781         log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1782         break;
1783     }
1784
1785     nickserv_eat_cookie(hi->cookie);
1786
1787     return 1;
1788 }
1789
1790 static NICKSERV_FUNC(cmd_oregnick) {
1791     const char *nick;
1792     struct handle_info *target;
1793     struct nick_info *ni;
1794
1795     NICKSERV_MIN_PARMS(3);
1796     if (!(target = modcmd_get_handle_info(user, argv[1])))
1797         return 0;
1798     nick = argv[2];
1799     if (!is_registerable_nick(nick)) {
1800         reply("NSMSG_BAD_NICK", nick);
1801         return 0;
1802     }
1803     ni = dict_find(nickserv_nick_dict, nick, NULL);
1804     if (ni) {
1805         reply("NSMSG_NICK_EXISTS", nick);
1806         return 0;
1807     }
1808     register_nick(nick, target);
1809     reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1810     return 1;
1811 }
1812
1813 static NICKSERV_FUNC(cmd_regnick) {
1814     unsigned n;
1815     struct nick_info *ni;
1816
1817     if (!is_registerable_nick(user->nick)) {
1818         reply("NSMSG_BAD_NICK", user->nick);
1819         return 0;
1820     }
1821     /* count their nicks, see if it's too many */
1822     for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1823     if (n >= nickserv_conf.nicks_per_handle) {
1824         reply("NSMSG_TOO_MANY_NICKS");
1825         return 0;
1826     }
1827     ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1828     if (ni) {
1829         reply("NSMSG_NICK_EXISTS", user->nick);
1830         return 0;
1831     }
1832     register_nick(user->nick, user->handle_info);
1833     reply("NSMSG_REGNICK_SUCCESS", user->nick);
1834     return 1;
1835 }
1836
1837 static NICKSERV_FUNC(cmd_pass)
1838 {
1839     struct handle_info *hi;
1840     const char *old_pass, *new_pass;
1841
1842     NICKSERV_MIN_PARMS(3);
1843     hi = user->handle_info;
1844     old_pass = argv[1];
1845     new_pass = argv[2];
1846     argv[2] = "****";
1847     if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1848     if (!checkpass(old_pass, hi->passwd)) {
1849         argv[1] = "BADPASS";
1850         reply("NSMSG_PASSWORD_INVALID");
1851         return 0;
1852     }
1853     cryptpass(new_pass, hi->passwd);
1854     argv[1] = "****";
1855     reply("NSMSG_PASS_SUCCESS");
1856     return 1;
1857 }
1858
1859 static int
1860 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1861 {
1862     unsigned int i;
1863     char *new_mask = canonicalize_hostmask(strdup(mask));
1864     for (i=0; i<hi->masks->used; i++) {
1865         if (!irccasecmp(new_mask, hi->masks->list[i])) {
1866             send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1867             free(new_mask);
1868             return 0;
1869         }
1870     }
1871     string_list_append(hi->masks, new_mask);
1872     send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
1873     return 1;
1874 }
1875
1876 static NICKSERV_FUNC(cmd_addmask)
1877 {
1878     if (argc < 2) {
1879         char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1880         int res = nickserv_addmask(user, user->handle_info, mask);
1881         free(mask);
1882         return res;
1883     } else {
1884         if (!is_gline(argv[1])) {
1885             reply("NSMSG_MASK_INVALID", argv[1]);
1886             return 0;
1887         }
1888         return nickserv_addmask(user, user->handle_info, argv[1]);
1889     }
1890 }
1891
1892 static NICKSERV_FUNC(cmd_oaddmask)
1893 {
1894     struct handle_info *hi;
1895
1896     NICKSERV_MIN_PARMS(3);
1897     if (!(hi = get_victim_oper(user, argv[1])))
1898         return 0;
1899     return nickserv_addmask(user, hi, argv[2]);
1900 }
1901
1902 static int
1903 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
1904 {
1905     unsigned int i;
1906     for (i=0; i<hi->masks->used; i++) {
1907         if (!strcmp(del_mask, hi->masks->list[i])) {
1908             char *old_mask = hi->masks->list[i];
1909             if (hi->masks->used == 1) {
1910                 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
1911                 return 0;
1912             }
1913             hi->masks->list[i] = hi->masks->list[--hi->masks->used];
1914             send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
1915             free(old_mask);
1916             return 1;
1917         }
1918     }
1919     send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
1920     return 0;
1921 }
1922
1923 static NICKSERV_FUNC(cmd_delmask)
1924 {
1925     NICKSERV_MIN_PARMS(2);
1926     return nickserv_delmask(user, user->handle_info, argv[1]);
1927 }
1928
1929 static NICKSERV_FUNC(cmd_odelmask)
1930 {
1931     struct handle_info *hi;
1932     NICKSERV_MIN_PARMS(3);
1933     if (!(hi = get_victim_oper(user, argv[1])))
1934         return 0;
1935     return nickserv_delmask(user, hi, argv[2]);
1936 }
1937
1938 int
1939 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
1940     unsigned int nn, add = 1, pos;
1941     unsigned long added, removed, flag;
1942
1943     for (added=removed=nn=0; str[nn]; nn++) {
1944         switch (str[nn]) {
1945         case '+': add = 1; break;
1946         case '-': add = 0; break;
1947         default:
1948             if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
1949                 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
1950                 return 0;
1951             }
1952             if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
1953                 /* cheesy avoidance of looking up the flag name.. */
1954                 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
1955                 return 0;
1956             }
1957             flag = 1 << (pos - 1);
1958             if (add)
1959                 added |= flag, removed &= ~flag;
1960             else
1961                 removed |= flag, added &= ~flag;
1962             break;
1963         }
1964     }
1965     *padded = added;
1966     *premoved = removed;
1967     return 1;
1968 }
1969
1970 static int
1971 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
1972 {
1973     unsigned long before, after, added, removed;
1974     struct userNode *uNode;
1975
1976     before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
1977     if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
1978         return 0;
1979     hi->flags = (hi->flags | added) & ~removed;
1980     after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
1981
1982     /* Strip helping flag if they're only a support helper and not
1983      * currently in #support. */
1984     if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
1985         struct channelList *schannels;
1986         unsigned int ii;
1987         schannels = chanserv_support_channels();
1988         for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
1989             for (ii = 0; ii < schannels->used; ++ii)
1990                 if (GetUserMode(schannels->list[ii], uNode))
1991                     break;
1992             if (ii < schannels->used)
1993                 break;
1994         }
1995         if (!uNode)
1996             HANDLE_CLEAR_FLAG(hi, HELPING);
1997     }
1998
1999     if (after && !before) {
2000         /* Add user to current helper list. */
2001         for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2002             userList_append(&curr_helpers, uNode);
2003     } else if (!after && before) {
2004         /* Remove user from current helper list. */
2005         for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2006             userList_remove(&curr_helpers, uNode);
2007     }
2008
2009     return 1;
2010 }
2011
2012 static void
2013 set_list(struct userNode *user, struct handle_info *hi, int override)
2014 {
2015     option_func_t *opt;
2016     unsigned int i;
2017     char *set_display[] = {
2018         "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2019         "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS"
2020     };
2021
2022     send_message(user, nickserv, "NSMSG_SETTING_LIST");
2023
2024     /* Do this so options are presented in a consistent order. */
2025     for (i = 0; i < ArrayLength(set_display); ++i)
2026         if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2027             opt(user, hi, override, 0, NULL);
2028 }
2029
2030 static NICKSERV_FUNC(cmd_set)
2031 {
2032     struct handle_info *hi;
2033     option_func_t *opt;
2034
2035     hi = user->handle_info;
2036     if (argc < 2) {
2037         set_list(user, hi, 0);
2038         return 1;
2039     }
2040     if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2041         reply("NSMSG_INVALID_OPTION", argv[1]);
2042         return 0;
2043     }
2044     return opt(user, hi, 0, argc-1, argv+1);
2045 }
2046
2047 static NICKSERV_FUNC(cmd_oset)
2048 {
2049     struct handle_info *hi;
2050     option_func_t *opt;
2051
2052     NICKSERV_MIN_PARMS(2);
2053
2054     if (!(hi = get_victim_oper(user, argv[1])))
2055         return 0;
2056
2057     if (argc < 3) {
2058         set_list(user, hi, 0);
2059         return 1;
2060     }
2061
2062     if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2063         reply("NSMSG_INVALID_OPTION", argv[2]);
2064         return 0;
2065     }
2066
2067     return opt(user, hi, 1, argc-2, argv+2);
2068 }
2069
2070 static OPTION_FUNC(opt_info)
2071 {
2072     const char *info;
2073     if (argc > 1) {
2074         if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2075             free(hi->infoline);
2076             hi->infoline = NULL;
2077         } else {
2078             hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2079         }
2080     }
2081
2082     info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2083     send_message(user, nickserv, "NSMSG_SET_INFO", info);
2084     return 1;
2085 }
2086
2087 static OPTION_FUNC(opt_width)
2088 {
2089     if (argc > 1) {
2090         unsigned int new_width = strtoul(argv[1], NULL, 0);
2091         hi->screen_width = new_width;
2092     }
2093
2094     if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2095         hi->screen_width = MIN_LINE_SIZE;
2096     else if (hi->screen_width > MAX_LINE_SIZE)
2097         hi->screen_width = MAX_LINE_SIZE;
2098
2099     send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2100     return 1;
2101 }
2102
2103 static OPTION_FUNC(opt_tablewidth)
2104 {
2105     if (argc > 1) {
2106         unsigned int new_width = strtoul(argv[1], NULL, 0);
2107         hi->table_width = new_width;
2108     }
2109
2110     if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2111         hi->table_width = MIN_LINE_SIZE;
2112     else if (hi->screen_width > MAX_LINE_SIZE)
2113         hi->table_width = MAX_LINE_SIZE;
2114
2115     send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2116     return 1;
2117 }
2118
2119 static OPTION_FUNC(opt_color)
2120 {
2121     if (argc > 1) {
2122         if (enabled_string(argv[1]))
2123             HANDLE_SET_FLAG(hi, MIRC_COLOR);
2124         else if (disabled_string(argv[1]))
2125             HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2126         else {
2127             send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2128             return 0;
2129         }
2130     }
2131
2132     send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2133     return 1;
2134 }
2135
2136 static OPTION_FUNC(opt_privmsg)
2137 {
2138     if (argc > 1) {
2139         if (enabled_string(argv[1]))
2140             HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2141         else if (disabled_string(argv[1]))
2142             HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2143         else {
2144             send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2145             return 0;
2146         }
2147     }
2148
2149     send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2150     return 1;
2151 }
2152
2153 static OPTION_FUNC(opt_style)
2154 {
2155     char *style;
2156
2157     if (argc > 1) {
2158         if (!irccasecmp(argv[1], "Zoot"))
2159             hi->userlist_style = HI_STYLE_ZOOT;
2160         else if (!irccasecmp(argv[1], "def"))
2161             hi->userlist_style = HI_STYLE_DEF;
2162     }
2163
2164     switch (hi->userlist_style) {
2165     case HI_STYLE_DEF:
2166         style = "def";
2167         break;
2168     case HI_STYLE_ZOOT:
2169     default:
2170         style = "Zoot";
2171     }
2172
2173     send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2174     return 1;
2175 }
2176
2177 static OPTION_FUNC(opt_announcements)
2178 {
2179     const char *choice;
2180
2181     if (argc > 1) {
2182         if (enabled_string(argv[1]))
2183             hi->announcements = 'y';
2184         else if (disabled_string(argv[1]))
2185             hi->announcements = 'n';
2186         else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2187             hi->announcements = '?';
2188         else {
2189             send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2190             return 0;
2191         }
2192     }
2193
2194     switch (hi->announcements) {
2195     case 'y': choice = user_find_message(user, "MSG_ON"); break;
2196     case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2197     case '?': choice = "default"; break;
2198     default: choice = "unknown"; break;
2199     }
2200     send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2201     return 1;
2202 }
2203
2204 static OPTION_FUNC(opt_password)
2205 {
2206     if (!override) {
2207         send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2208         return 0;
2209     }
2210
2211     if (argc > 1)
2212         cryptpass(argv[1], hi->passwd);
2213
2214     send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2215     return 1;
2216 }
2217
2218 static OPTION_FUNC(opt_flags)
2219 {
2220     char flags[33];
2221     unsigned int ii, flen;
2222
2223     if (!override) {
2224         send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2225         return 0;
2226     }
2227
2228     if (argc > 1)
2229         nickserv_apply_flags(user, hi, argv[1]);
2230
2231     for (ii = flen = 0; handle_flags[ii]; ii++)
2232         if (hi->flags & (1 << ii))
2233             flags[flen++] = handle_flags[ii];
2234     flags[flen] = '\0';
2235     if (hi->flags)
2236         send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2237     else
2238         send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2239     return 1;
2240 }
2241
2242 static OPTION_FUNC(opt_email)
2243 {
2244     if (argc > 1) {
2245         const char *str;
2246         if (!is_valid_email_addr(argv[1])) {
2247             send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2248             return 0;
2249         }
2250         if ((str = sendmail_prohibited_address(argv[1]))) {
2251             send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2252             return 0;
2253         }
2254         if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2255             send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2256         else if (!override)
2257                 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2258         else {
2259             nickserv_set_email_addr(hi, argv[1]);
2260             if (hi->cookie)
2261                 nickserv_eat_cookie(hi->cookie);
2262             send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2263         }
2264     } else
2265         send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2266     return 1;
2267 }
2268
2269 static OPTION_FUNC(opt_maxlogins)
2270 {
2271     char maxlogins;
2272     if (argc > 1) {
2273         maxlogins = strtoul(argv[1], NULL, 0);
2274         if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2275             send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2276             return 0;
2277         }
2278         hi->maxlogins = maxlogins;
2279     }
2280     maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2281     send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2282     return 1;
2283 }
2284
2285 static OPTION_FUNC(opt_language)
2286 {
2287     struct language *lang;
2288     if (argc > 1) {
2289         lang = language_find(argv[1]);
2290         if (irccasecmp(lang->name, argv[1]))
2291             send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2292         hi->language = lang;
2293     }
2294     lang = hi->language ? hi->language : lang_C;
2295     send_message(user, nickserv, "NSMSG_SET_LANGUAGE", lang->name);
2296     return 1;
2297 }
2298
2299 int
2300 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2301     if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2302         return 0;
2303     if ((user->handle_info->opserv_level < target->opserv_level)
2304         || ((user->handle_info->opserv_level == target->opserv_level)
2305             && (user->handle_info->opserv_level < 1000))) {
2306         send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2307         return 0;
2308     }
2309     if ((user->handle_info->opserv_level < new_level)
2310         || ((user->handle_info->opserv_level == new_level)
2311             && (user->handle_info->opserv_level < 1000))) {
2312         send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2313         return 0;
2314     }
2315     if (user->handle_info == target) {
2316         send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2317         return 0;
2318     }
2319     if (target->opserv_level == new_level)
2320         return 0;
2321     log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2322         user->handle_info->handle, target->handle, new_level, target->opserv_level);
2323     target->opserv_level = new_level;
2324     return 1;
2325 }
2326
2327 static OPTION_FUNC(opt_level)
2328 {
2329     int res;
2330
2331     if (!override) {
2332         send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2333         return 0;
2334     }
2335
2336     res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2337     send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2338     return res;
2339 }
2340
2341 static OPTION_FUNC(opt_epithet)
2342 {
2343     if (!override) {
2344         send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2345         return 0;
2346     }
2347
2348     if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2349         char *epithet = unsplit_string(argv+1, argc-1, NULL);
2350         if (hi->epithet)
2351             free(hi->epithet);
2352         if ((epithet[0] == '*') && !epithet[1])
2353             hi->epithet = NULL;
2354         else
2355             hi->epithet = strdup(epithet);
2356     }
2357
2358     if (hi->epithet)
2359         send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2360     else
2361         send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2362     return 1;
2363 }
2364
2365 static NICKSERV_FUNC(cmd_reclaim)
2366 {
2367     struct handle_info *hi;
2368     struct nick_info *ni;
2369     struct userNode *victim;
2370
2371     NICKSERV_MIN_PARMS(2);
2372     hi = user->handle_info;
2373     ni = dict_find(nickserv_nick_dict, argv[1], 0);
2374     if (!ni) {
2375         reply("NSMSG_UNKNOWN_NICK", argv[1]);
2376         return 0;
2377     }
2378     if (ni->owner != user->handle_info) {
2379         reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2380         return 0;
2381     }
2382     victim = GetUserH(ni->nick);
2383     if (!victim) {
2384         reply("MSG_NICK_UNKNOWN", ni->nick);
2385         return 0;
2386     }
2387     if (victim == user) {
2388         reply("NSMSG_NICK_USER_YOU");
2389         return 0;
2390     }
2391     nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2392     switch (nickserv_conf.reclaim_action) {
2393     case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2394     case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2395     case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2396     case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2397     }
2398     return 1;
2399 }
2400
2401 static NICKSERV_FUNC(cmd_unregnick)
2402 {
2403     const char *nick;
2404     struct handle_info *hi;
2405     struct nick_info *ni;
2406
2407     hi = user->handle_info;
2408     nick = (argc < 2) ? user->nick : (const char*)argv[1];
2409     ni = dict_find(nickserv_nick_dict, nick, NULL);
2410     if (!ni) {
2411         reply("NSMSG_UNKNOWN_NICK", nick);
2412         return 0;
2413     }
2414     if (hi != ni->owner) {
2415         reply("NSMSG_NOT_YOUR_NICK", nick);
2416         return 0;
2417     }
2418     reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2419     delete_nick(ni);
2420     return 1;
2421 }
2422
2423 static NICKSERV_FUNC(cmd_ounregnick)
2424 {
2425     struct nick_info *ni;
2426
2427     NICKSERV_MIN_PARMS(2);
2428     if (!(ni = get_nick_info(argv[1]))) {
2429         reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2430         return 0;
2431     }
2432     if (ni->owner->opserv_level >= user->handle_info->opserv_level) {
2433         reply("MSG_USER_OUTRANKED", ni->nick);
2434         return 0;
2435     }
2436     reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2437     delete_nick(ni);
2438     return 1;
2439 }
2440
2441 static NICKSERV_FUNC(cmd_unregister)
2442 {
2443     struct handle_info *hi;
2444     char *passwd;
2445
2446     NICKSERV_MIN_PARMS(2);
2447     hi = user->handle_info;
2448     passwd = argv[1];
2449     argv[1] = "****";
2450     if (checkpass(passwd, hi->passwd)) {
2451         nickserv_unregister_handle(hi, user);
2452         return 1;
2453     } else {
2454         log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2455         reply("NSMSG_PASSWORD_INVALID");
2456         return 0;
2457     }
2458 }
2459
2460 static NICKSERV_FUNC(cmd_ounregister)
2461 {
2462     struct handle_info *hi;
2463
2464     NICKSERV_MIN_PARMS(2);
2465     if (!(hi = get_victim_oper(user, argv[1])))
2466         return 0;
2467     nickserv_unregister_handle(hi, user);
2468     return 0;
2469 }
2470
2471 static NICKSERV_FUNC(cmd_status)
2472 {
2473     if (nickserv_conf.disable_nicks) {
2474         reply("NSMSG_GLOBAL_STATS_NONICK",
2475                         dict_size(nickserv_handle_dict));
2476     } else {
2477         if (user->handle_info) {
2478             int cnt=0;
2479             struct nick_info *ni;
2480             for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2481             reply("NSMSG_HANDLE_STATS", cnt);
2482         } else {
2483             reply("NSMSG_HANDLE_NONE");
2484         }
2485         reply("NSMSG_GLOBAL_STATS",
2486               dict_size(nickserv_handle_dict),
2487               dict_size(nickserv_nick_dict));
2488     }
2489     return 1;
2490 }
2491
2492 static NICKSERV_FUNC(cmd_ghost)
2493 {
2494     struct userNode *target;
2495     char reason[MAXLEN];
2496
2497     NICKSERV_MIN_PARMS(2);
2498     if (!(target = GetUserH(argv[1]))) {
2499         reply("MSG_NICK_UNKNOWN", argv[1]);
2500         return 0;
2501     }
2502     if (target == user) {
2503         reply("NSMSG_CANNOT_GHOST_SELF");
2504         return 0;
2505     }
2506     if (!target->handle_info || (target->handle_info != user->handle_info)) {
2507         reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2508         return 0;
2509     }
2510     snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2511     DelUser(target, nickserv, 1, reason);
2512     reply("NSMSG_GHOST_KILLED", argv[1]);
2513     return 1;
2514 }
2515
2516 static NICKSERV_FUNC(cmd_vacation)
2517 {
2518     HANDLE_SET_FLAG(user->handle_info, FROZEN);
2519     reply("NSMSG_ON_VACATION");
2520     return 1;
2521 }
2522
2523 static int
2524 nickserv_saxdb_write(struct saxdb_context *ctx) {
2525     dict_iterator_t it;
2526     struct handle_info *hi;
2527     char flags[33];
2528
2529     for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2530         hi = iter_data(it);
2531 #ifdef WITH_PROTOCOL_BAHAMUT
2532         assert(hi->id);
2533 #endif
2534         saxdb_start_record(ctx, iter_key(it), 0);
2535         if (hi->announcements != '?') {
2536             flags[0] = hi->announcements;
2537             flags[1] = 0;
2538             saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2539         }
2540         if (hi->cookie) {
2541             struct handle_cookie *cookie = hi->cookie;
2542             char *type;
2543
2544             switch (cookie->type) {
2545             case ACTIVATION: type = KEY_ACTIVATION; break;
2546             case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2547             case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2548             case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2549             default: type = NULL; break;
2550             }
2551             if (type) {
2552                 saxdb_start_record(ctx, KEY_COOKIE, 0);
2553                 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2554                 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2555                 if (cookie->data)
2556                     saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2557                 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2558                 saxdb_end_record(ctx);
2559             }
2560         }
2561         if (hi->email_addr)
2562             saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2563         if (hi->epithet)
2564             saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2565         if (hi->flags) {
2566             int ii, flen;
2567
2568             for (ii=flen=0; handle_flags[ii]; ++ii)
2569                 if (hi->flags & (1 << ii))
2570                     flags[flen++] = handle_flags[ii];
2571             flags[flen] = 0;
2572             saxdb_write_string(ctx, KEY_FLAGS, flags);
2573         }
2574 #ifdef WITH_PROTOCOL_BAHAMUT
2575         saxdb_write_int(ctx, KEY_ID, hi->id);
2576 #endif
2577         if (hi->infoline)
2578             saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2579         if (hi->last_quit_host[0])
2580             saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2581         saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2582         if (hi->masks->used)
2583             saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2584         if (hi->maxlogins)
2585             saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2586         if (hi->nicks) {
2587             struct string_list *slist;
2588             struct nick_info *ni;
2589
2590             slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2591             for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2592             saxdb_write_string_list(ctx, KEY_NICKS, slist);
2593             free(slist->list);
2594             free(slist);
2595         }
2596         if (hi->opserv_level)
2597             saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2598         if (hi->language && (hi->language != lang_C))
2599             saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2600         saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2601         saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2602         if (hi->screen_width)
2603             saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2604         if (hi->table_width)
2605             saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2606         flags[0] = hi->userlist_style;
2607         flags[1] = 0;
2608         saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2609         saxdb_end_record(ctx);
2610     }
2611     return 0;
2612 }
2613
2614 static handle_merge_func_t *handle_merge_func_list;
2615 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2616
2617 void
2618 reg_handle_merge_func(handle_merge_func_t func)
2619 {
2620     if (handle_merge_func_used == handle_merge_func_size) {
2621         if (handle_merge_func_size) {
2622             handle_merge_func_size <<= 1;
2623             handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2624         } else {
2625             handle_merge_func_size = 8;
2626             handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2627         }
2628     }
2629     handle_merge_func_list[handle_merge_func_used++] = func;
2630 }
2631
2632 static NICKSERV_FUNC(cmd_merge)
2633 {
2634     struct handle_info *hi_from, *hi_to;
2635     struct userNode *last_user;
2636     struct userData *cList, *cListNext;
2637     unsigned int ii, jj, n;
2638     char buffer[MAXLEN];
2639
2640     NICKSERV_MIN_PARMS(3);
2641
2642     if (!(hi_from = get_victim_oper(user, argv[1])))
2643         return 0;
2644     if (!(hi_to = get_victim_oper(user, argv[2])))
2645         return 0;
2646     if (hi_to == hi_from) {
2647         reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2648         return 0;
2649     }
2650
2651     for (n=0; n<handle_merge_func_used; n++)
2652         handle_merge_func_list[n](user, hi_to, hi_from);
2653
2654     /* Append "from" handle's nicks to "to" handle's nick list. */
2655     if (hi_to->nicks) {
2656         struct nick_info *last_ni;
2657         for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2658         last_ni->next = hi_from->nicks;
2659     }
2660     while (hi_from->nicks) {
2661         hi_from->nicks->owner = hi_to;
2662         hi_from->nicks = hi_from->nicks->next;
2663     }
2664
2665     /* Merge the hostmasks. */
2666     for (ii=0; ii<hi_from->masks->used; ii++) {
2667         char *mask = hi_from->masks->list[ii];
2668         for (jj=0; jj<hi_to->masks->used; jj++)
2669             if (match_ircglobs(hi_to->masks->list[jj], mask))
2670                 break;
2671         if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2672             string_list_append(hi_to->masks, strdup(mask));
2673     }
2674
2675     /* Merge the lists of authed users. */
2676     if (hi_to->users) {
2677         for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2678         last_user->next_authed = hi_from->users;
2679     } else {
2680         hi_to->users = hi_from->users;
2681     }
2682     /* Repoint the old "from" handle's users. */
2683     for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2684         last_user->handle_info = hi_to;
2685     }
2686     hi_from->users = NULL;
2687
2688     /* Merge channel userlists. */
2689     for (cList=hi_from->channels; cList; cList=cListNext) {
2690         struct userData *cList2;
2691         cListNext = cList->u_next;
2692         for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2693             if (cList->channel == cList2->channel)
2694                 break;
2695         log_module(NS_LOG, LOG_DEBUG, "Merging %s->%s@%s: before %p->%p->%-p, %p->%p->%p",
2696                    hi_from->handle, hi_to->handle, cList->channel->channel->name,
2697                    cList->u_prev, cList, cList->u_next,
2698                    (cList2?cList2->u_prev:0), cList2, (cList2?cList2->u_next:0));
2699         if (cList2 && (cList2->access >= cList->access)) {
2700             /* keep cList2 in hi_to; remove cList from hi_from */
2701             log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList);
2702             del_channel_user(cList, 1);
2703         } else {
2704             if (cList2) {
2705                 /* remove the lower-ranking cList2 from hi_to */
2706                 log_module(NS_LOG, LOG_DEBUG, "Deleting %p", cList2);
2707                 del_channel_user(cList2, 1);
2708             }
2709             /* cList needs to be moved from hi_from to hi_to */
2710             cList->handle = hi_to;
2711             /* Remove from linked list for hi_from */
2712             assert(!cList->u_prev);
2713             hi_from->channels = cList->u_next;
2714             if (cList->u_next)
2715                 cList->u_next->u_prev = cList->u_prev;
2716             /* Add to linked list for hi_to */
2717             cList->u_prev = NULL;
2718             cList->u_next = hi_to->channels;
2719             if (hi_to->channels)
2720                 hi_to->channels->u_prev = cList;
2721             hi_to->channels = cList;
2722             log_module(NS_LOG, LOG_DEBUG, "Now %p->%p->%p",
2723                        cList->u_prev, cList, cList->u_next);
2724         }
2725     }
2726
2727     /* Do they get an OpServ level promotion? */
2728     if (hi_from->opserv_level > hi_to->opserv_level)
2729         hi_to->opserv_level = hi_from->opserv_level;
2730
2731     /* What about last seen time? */
2732     if (hi_from->lastseen > hi_to->lastseen)
2733         hi_to->lastseen = hi_from->lastseen;
2734
2735     /* Notify of success. */
2736     sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2737     reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2738     global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2739
2740     /* Unregister the "from" handle. */
2741     nickserv_unregister_handle(hi_from, NULL);
2742
2743     return 1;
2744 }
2745
2746 struct nickserv_discrim {
2747     unsigned int limit, min_level, max_level;
2748     unsigned long flags_on, flags_off;
2749     time_t min_registered, max_registered;
2750     time_t lastseen;
2751     enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2752     const char *nickmask;
2753     const char *hostmask;
2754     const char *handlemask;
2755     const char *emailmask;
2756 };
2757
2758 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2759
2760 struct discrim_apply_info {
2761     struct nickserv_discrim *discrim;
2762     discrim_search_func func;
2763     struct userNode *source;
2764     unsigned int matched;
2765 };
2766
2767 static struct nickserv_discrim *
2768 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2769 {
2770     unsigned int i;
2771     struct nickserv_discrim *discrim;
2772
2773     discrim = malloc(sizeof(*discrim));
2774     memset(discrim, 0, sizeof(*discrim));
2775     discrim->min_level = 0;
2776     discrim->max_level = ~0;
2777     discrim->limit = 50;
2778     discrim->min_registered = 0;
2779     discrim->max_registered = INT_MAX;
2780     discrim->lastseen = now;
2781
2782     for (i=0; i<argc; i++) {
2783         if (i == argc - 1) {
2784             send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2785             goto fail;
2786         }
2787         if (!irccasecmp(argv[i], "limit")) {
2788             discrim->limit = atoi(argv[++i]);
2789         } else if (!irccasecmp(argv[i], "flags")) {
2790             nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2791         } else if (!irccasecmp(argv[i], "registered")) {
2792             const char *cmp = argv[++i];
2793             if (cmp[0] == '<') {
2794                 if (cmp[1] == '=') {
2795                     discrim->min_registered = now - ParseInterval(cmp+2);
2796                 } else {
2797                     discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2798                 }
2799             } else if (cmp[0] == '=') {
2800                 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2801             } else if (cmp[0] == '>') {
2802                 if (cmp[1] == '=') {
2803                     discrim->max_registered = now - ParseInterval(cmp+2);
2804                 } else {
2805                     discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2806                 }
2807             } else {
2808                 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2809             }
2810         } else if (!irccasecmp(argv[i], "seen")) {
2811             discrim->lastseen = now - ParseInterval(argv[++i]);
2812         } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2813             discrim->nickmask = argv[++i];
2814         } else if (!irccasecmp(argv[i], "hostmask")) {
2815             i++;
2816             if (!irccasecmp(argv[i], "exact")) {
2817                 if (i == argc - 1) {
2818                     send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2819                     goto fail;
2820                 }
2821                 discrim->hostmask_type = EXACT;
2822             } else if (!irccasecmp(argv[i], "subset")) {
2823                 if (i == argc - 1) {
2824                     send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2825                     goto fail;
2826                 }
2827                 discrim->hostmask_type = SUBSET;
2828             } else if (!irccasecmp(argv[i], "superset")) {
2829                 if (i == argc - 1) {
2830                     send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2831                     goto fail;
2832                 }
2833                 discrim->hostmask_type = SUPERSET;
2834             } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2835                if (i == argc - 1) {
2836                    send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2837                    goto fail;
2838                }
2839                discrim->hostmask_type = LASTQUIT;
2840             } else {
2841                 i--;
2842                 discrim->hostmask_type = SUPERSET;
2843             }
2844             discrim->hostmask = argv[++i];
2845         } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
2846             if (!irccasecmp(argv[++i], "*")) {
2847                 discrim->handlemask = 0;
2848             } else {
2849                 discrim->handlemask = argv[i];
2850             }
2851         } else if (!irccasecmp(argv[i], "email")) {
2852             if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
2853                 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
2854                 goto fail;
2855             } else if (!irccasecmp(argv[++i], "*")) {
2856                 discrim->emailmask = 0;
2857             } else {
2858                 discrim->emailmask = argv[i];
2859             }
2860         } else if (!irccasecmp(argv[i], "access")) {
2861             const char *cmp = argv[++i];
2862             if (cmp[0] == '<') {
2863                 if (discrim->min_level == 0) discrim->min_level = 1;
2864                 if (cmp[1] == '=') {
2865                     discrim->max_level = strtoul(cmp+2, NULL, 0);
2866                 } else {
2867                     discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
2868                 }
2869             } else if (cmp[0] == '=') {
2870                 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
2871             } else if (cmp[0] == '>') {
2872                 if (cmp[1] == '=') {
2873                     discrim->min_level = strtoul(cmp+2, NULL, 0);
2874                 } else {
2875                     discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
2876                 }
2877             } else {
2878                 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2879             }
2880         } else {
2881             send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
2882             goto fail;
2883         }
2884     }
2885     return discrim;
2886   fail:
2887     free(discrim);
2888     return NULL;
2889 }
2890
2891 static int
2892 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
2893 {
2894     if (((discrim->flags_on & hi->flags) != discrim->flags_on)
2895         || (discrim->flags_off & hi->flags)
2896         || (discrim->min_registered > hi->registered)
2897         || (discrim->max_registered < hi->registered)
2898         || (discrim->lastseen < (hi->users?now:hi->lastseen))
2899         || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
2900         || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
2901         || (discrim->min_level > hi->opserv_level)
2902         || (discrim->max_level < hi->opserv_level)) {
2903         return 0;
2904     }
2905     if (discrim->hostmask) {
2906         unsigned int i;
2907         for (i=0; i<hi->masks->used; i++) {
2908             const char *mask = hi->masks->list[i];
2909             if ((discrim->hostmask_type == SUBSET)
2910                 && (match_ircglobs(discrim->hostmask, mask))) break;
2911             else if ((discrim->hostmask_type == EXACT)
2912                      && !irccasecmp(discrim->hostmask, mask)) break;
2913             else if ((discrim->hostmask_type == SUPERSET)
2914                      && (match_ircglobs(mask, discrim->hostmask))) break;
2915             else if ((discrim->hostmask_type == LASTQUIT)
2916                      && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
2917         }
2918         if (i==hi->masks->used) return 0;
2919     }
2920     if (discrim->nickmask) {
2921         struct nick_info *nick = hi->nicks;
2922         while (nick) {
2923             if (match_ircglob(nick->nick, discrim->nickmask)) break;
2924             nick = nick->next;
2925         }
2926         if (!nick) return 0;
2927     }
2928     return 1;
2929 }
2930
2931 static unsigned int
2932 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
2933 {
2934     dict_iterator_t it, next;
2935     unsigned int matched;
2936
2937     for (it = dict_first(nickserv_handle_dict), matched = 0;
2938          it && (matched < discrim->limit);
2939          it = next) {
2940         next = iter_next(it);
2941         if (nickserv_discrim_match(discrim, iter_data(it))) {
2942             dsf(source, iter_data(it));
2943             matched++;
2944         }
2945     }
2946     return matched;
2947 }
2948
2949 static void
2950 search_print_func(struct userNode *source, struct handle_info *match)
2951 {
2952     send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
2953 }
2954
2955 static void
2956 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
2957 {
2958 }
2959
2960 static void
2961 search_unregister_func (struct userNode *source, struct handle_info *match)
2962 {
2963     if (oper_has_access(source, nickserv, match->opserv_level, 0))
2964         nickserv_unregister_handle(match, source);
2965 }
2966
2967 static int
2968 nickserv_sort_accounts_by_access(const void *a, const void *b)
2969 {
2970     const struct handle_info *hi_a = *(const struct handle_info**)a;
2971     const struct handle_info *hi_b = *(const struct handle_info**)b;
2972     if (hi_a->opserv_level != hi_b->opserv_level)
2973         return hi_b->opserv_level - hi_a->opserv_level;
2974     return irccasecmp(hi_a->handle, hi_b->handle);
2975 }
2976
2977 void
2978 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
2979 {
2980     struct handle_info_list hil;
2981     struct helpfile_table tbl;
2982     unsigned int ii;
2983     dict_iterator_t it;
2984     const char **ary;
2985
2986     memset(&hil, 0, sizeof(hil));
2987     for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2988         struct handle_info *hi = iter_data(it);
2989         if (hi->opserv_level)
2990             handle_info_list_append(&hil, hi);
2991     }
2992     qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
2993     tbl.length = hil.used + 1;
2994     tbl.width = 2;
2995     tbl.flags = TABLE_NO_FREE;
2996     tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
2997     tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
2998     ary[0] = "Account";
2999     ary[1] = "Level";
3000     for (ii = 0; ii < hil.used; ) {
3001         ary = malloc(tbl.width * sizeof(ary[0]));
3002         ary[0] = hil.list[ii]->handle;
3003         ary[1] = strtab(hil.list[ii]->opserv_level);
3004         tbl.contents[++ii] = ary;
3005     }
3006     table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3007     reply("MSG_MATCH_COUNT", hil.used);
3008     for (ii = 0; ii < hil.used; )
3009         free(tbl.contents[++ii]);
3010     free(tbl.contents);
3011     free(hil.list);
3012 }
3013
3014 static NICKSERV_FUNC(cmd_search)
3015 {
3016     struct nickserv_discrim *discrim;
3017     discrim_search_func action;
3018     struct svccmd *subcmd;
3019     unsigned int matches;
3020     char buf[MAXLEN];
3021
3022     NICKSERV_MIN_PARMS(3);
3023     sprintf(buf, "search %s", argv[1]);
3024     subcmd = dict_find(nickserv_service->commands, buf, NULL);
3025     if (!irccasecmp(argv[1], "print"))
3026         action = search_print_func;
3027     else if (!irccasecmp(argv[1], "count"))
3028         action = search_count_func;
3029     else if (!irccasecmp(argv[1], "unregister"))
3030         action = search_unregister_func;
3031     else {
3032         reply("NSMSG_INVALID_ACTION", argv[1]);
3033         return 0;
3034     }
3035
3036     if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3037         return 0;
3038
3039     discrim = nickserv_discrim_create(user, argc-2, argv+2);
3040     if (!discrim)
3041         return 0;
3042
3043     if (action == search_print_func)
3044         reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3045     else if (action == search_count_func)
3046         discrim->limit = INT_MAX;
3047
3048     matches = nickserv_discrim_search(discrim, action, user);
3049
3050     if (matches)
3051         reply("MSG_MATCH_COUNT", matches);
3052     else
3053         reply("MSG_NO_MATCHES");
3054
3055     free(discrim);
3056     return 0;
3057 }
3058
3059 static MODCMD_FUNC(cmd_checkpass)
3060 {
3061     struct handle_info *hi;
3062
3063     NICKSERV_MIN_PARMS(3);
3064     if (!(hi = get_handle_info(argv[1]))) {
3065         reply("MSG_HANDLE_UNKNOWN", argv[1]);
3066         return 0;
3067     }
3068     if (checkpass(argv[2], hi->passwd))
3069         reply("CHECKPASS_YES");
3070     else
3071         reply("CHECKPASS_NO");
3072     argv[2] = "****";
3073     return 1;
3074 }
3075
3076 static void
3077 nickserv_db_read_handle(const char *handle, dict_t obj)
3078 {
3079     const char *str;
3080     struct string_list *masks, *slist;
3081     struct handle_info *hi;
3082     struct userNode *authed_users;
3083     unsigned long int id;
3084     unsigned int ii;
3085     dict_t subdb;
3086
3087     str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3088     id = str ? strtoul(str, NULL, 0) : 0;
3089     str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3090     if (!str) {
3091         log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3092         return;
3093     }
3094     if ((hi = get_handle_info(handle))) {
3095         authed_users = hi->users;
3096         hi->users = NULL;
3097         dict_remove(nickserv_handle_dict, hi->handle);
3098     } else {
3099         authed_users = NULL;
3100     }
3101     hi = register_handle(handle, str, id);
3102     if (authed_users) {
3103         hi->users = authed_users;
3104         while (authed_users) {
3105             authed_users->handle_info = hi;
3106             authed_users = authed_users->next_authed;
3107         }
3108     }
3109     masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3110     hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3111     str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3112     hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3113     str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3114     hi->language = language_find(str ? str : "C");
3115     str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3116     hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3117     str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3118     if (str) hi->infoline = strdup(str);
3119     str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3120     hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3121     str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3122     hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3123     /* We want to read the nicks even if disable_nicks is set.  This is so
3124      * that we don't lose the nick data entirely. */
3125     slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3126     if (slist) {
3127         for (ii=0; ii<slist->used; ii++)
3128             register_nick(slist->list[ii], hi);
3129     }
3130     str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3131     if (str) {
3132         for (ii=0; str[ii]; ii++)
3133             hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3134     }
3135     str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3136     hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3137     str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3138     hi->announcements = str ? str[0] : '?';
3139     str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3140     hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3141     str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3142     hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3143     str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3144     if (!str) str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3145     if (str) safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3146     str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3147     if (str) nickserv_set_email_addr(hi, str);
3148     str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3149     if (str) hi->epithet = strdup(str);
3150     subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3151     if (subdb) {
3152         const char *data, *type, *expires, *cookie_str;
3153         struct handle_cookie *cookie;
3154
3155         cookie = calloc(1, sizeof(*cookie));
3156         type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3157         data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3158         expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3159         cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3160         if (!type || !expires || !cookie_str) {
3161             log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3162             goto cookie_out;
3163         }
3164         if (!irccasecmp(type, KEY_ACTIVATION))
3165             cookie->type = ACTIVATION;
3166         else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3167             cookie->type = PASSWORD_CHANGE;
3168         else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3169             cookie->type = EMAIL_CHANGE;
3170         else if (!irccasecmp(type, KEY_ALLOWAUTH))
3171             cookie->type = ALLOWAUTH;
3172         else {
3173             log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3174             goto cookie_out;
3175         }
3176         cookie->expires = strtoul(expires, NULL, 0);
3177         if (cookie->expires < now)
3178             goto cookie_out;
3179         if (data)
3180             cookie->data = strdup(data);
3181         safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3182         cookie->hi = hi;
3183       cookie_out:
3184         if (cookie->hi)
3185             nickserv_bake_cookie(cookie);
3186         else
3187             nickserv_free_cookie(cookie);
3188     }
3189 }
3190
3191 static int
3192 nickserv_saxdb_read(dict_t db) {
3193     dict_iterator_t it;
3194     struct record_data *rd;
3195
3196     for (it=dict_first(db); it; it=iter_next(it)) {
3197         rd = iter_data(it);
3198         nickserv_db_read_handle(iter_key(it), rd->d.object);
3199     }
3200     return 0;
3201 }
3202
3203 static NICKSERV_FUNC(cmd_mergedb)
3204 {
3205     struct timeval start, stop;
3206     dict_t db;
3207
3208     NICKSERV_MIN_PARMS(2);
3209     gettimeofday(&start, NULL);
3210     if (!(db = parse_database(argv[1]))) {
3211         reply("NSMSG_DB_UNREADABLE", argv[1]);
3212         return 0;
3213     }
3214     nickserv_saxdb_read(db);
3215     free_database(db);
3216     gettimeofday(&stop, NULL);
3217     stop.tv_sec -= start.tv_sec;
3218     stop.tv_usec -= start.tv_usec;
3219     if (stop.tv_usec < 0) {
3220         stop.tv_sec -= 1;
3221         stop.tv_usec += 1000000;
3222     }
3223     reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3224     return 1;
3225 }
3226
3227 static void
3228 expire_handles(UNUSED_ARG(void *data))
3229 {
3230     dict_iterator_t it, next;
3231     time_t expiry;
3232     struct handle_info *hi;
3233
3234     for (it=dict_first(nickserv_handle_dict); it; it=next) {
3235         next = iter_next(it);
3236         hi = iter_data(it);
3237         if ((hi->opserv_level > 0)
3238             || hi->users
3239             || HANDLE_FLAGGED(hi, FROZEN)
3240             || HANDLE_FLAGGED(hi, NODELETE)) {
3241             continue;
3242         }
3243         expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3244         if ((now - hi->lastseen) > expiry) {
3245             log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3246             nickserv_unregister_handle(hi, NULL);
3247         }
3248     }
3249
3250     if (nickserv_conf.handle_expire_frequency)
3251         timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3252 }
3253
3254 static void
3255 nickserv_load_dict(const char *fname)
3256 {
3257     FILE *file;
3258     char line[128];
3259     if (!(file = fopen(fname, "r"))) {
3260         log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3261         return;
3262     }
3263     while (!feof(file)) {
3264         fgets(line, sizeof(line), file);
3265         if (!line[0])
3266             continue;
3267         if (line[strlen(line)-1] == '\n')
3268             line[strlen(line)-1] = 0;
3269         dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3270     }
3271     fclose(file);
3272     log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3273 }
3274
3275 static enum reclaim_action
3276 reclaim_action_from_string(const char *str) {
3277     if (!str)
3278         return RECLAIM_NONE;
3279     else if (!irccasecmp(str, "warn"))
3280         return RECLAIM_WARN;
3281     else if (!irccasecmp(str, "svsnick"))
3282         return RECLAIM_SVSNICK;
3283     else if (!irccasecmp(str, "kill"))
3284         return RECLAIM_KILL;
3285     else
3286         return RECLAIM_NONE;
3287 }
3288
3289 static void
3290 nickserv_conf_read(void)
3291 {
3292     dict_t conf_node, child;
3293     const char *str;
3294     dict_iterator_t it;
3295
3296     if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3297         log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3298         return;
3299     }
3300     str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3301     if (!str) str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3302     if (nickserv_conf.valid_handle_regex_set) regfree(&nickserv_conf.valid_handle_regex);
3303     if (str) {
3304         int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3305         nickserv_conf.valid_handle_regex_set = !err;
3306         if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3307     } else {
3308         nickserv_conf.valid_handle_regex_set = 0;
3309     }
3310     str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3311     if (nickserv_conf.valid_nick_regex_set) regfree(&nickserv_conf.valid_nick_regex);
3312     if (str) {
3313         int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3314         nickserv_conf.valid_nick_regex_set = !err;
3315         if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3316     } else {
3317         nickserv_conf.valid_nick_regex_set = 0;
3318     }
3319     str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3320     if (!str) str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3321     nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3322     str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3323     nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3324     str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3325     nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3326     str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3327     nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3328     str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3329     nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3330     str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3331     nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3332     str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3333     nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3334     str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3335     nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3336     str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3337     nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3338     str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3339     nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3340     str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3341     if (!str) str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3342     nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3343     str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3344     if (!str) str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3345     nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3346     str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3347     if (!str) str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3348     nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3349     str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3350     nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3351     str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3352     nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3353     str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3354     nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3355     if (!nickserv_conf.disable_nicks) {
3356         str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3357         nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3358         str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3359         nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3360         str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3361         nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3362         str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3363         nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3364     }
3365     child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3366     for (it=dict_first(child); it; it=iter_next(it)) {
3367         const char *key = iter_key(it), *value;
3368         unsigned char flag;
3369         int pos;
3370
3371         if (!strncasecmp(key, "uc_", 3))
3372             flag = toupper(key[3]);
3373         else if (!strncasecmp(key, "lc_", 3))
3374             flag = tolower(key[3]);
3375         else
3376             flag = key[0];
3377
3378         if ((pos = handle_inverse_flags[flag])) {
3379             value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3380             flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3381         }
3382     }
3383     if (nickserv_conf.weak_password_dict)
3384         dict_delete(nickserv_conf.weak_password_dict);
3385     nickserv_conf.weak_password_dict = dict_new();
3386     dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3387     dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3388     dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3389     str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3390     if (str)
3391         nickserv_load_dict(str);
3392     str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3393     if (nickserv && str)
3394         NickChange(nickserv, str, 0);
3395     str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3396     nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3397     str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3398     nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3399     str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3400     nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3401     str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3402     nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3403     str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3404     nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3405     str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3406     nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3407     str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3408     nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3409     str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3410     nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3411     str = conf_get_data("server/network", RECDB_QSTRING);
3412     nickserv_conf.network_name = str ? str : "some IRC network";
3413     if (!nickserv_conf.auth_policer_params) {
3414         nickserv_conf.auth_policer_params = policer_params_new();
3415         policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3416         policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3417     }
3418     child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3419     for (it=dict_first(child); it; it=iter_next(it))
3420         set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3421 }
3422
3423 static void
3424 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3425     char newnick[NICKLEN+1];
3426
3427     assert(user);
3428     assert(ni);
3429     switch (action) {
3430     case RECLAIM_NONE:
3431         /* do nothing */
3432         break;
3433     case RECLAIM_WARN:
3434         send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3435         break;
3436     case RECLAIM_SVSNICK:
3437         do {
3438             snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3439         } while (GetUserH(newnick));
3440         irc_svsnick(nickserv, user, newnick);
3441         break;
3442     case RECLAIM_KILL:
3443         irc_kill(nickserv, user, "NSMSG_RECLAIM_KILL");
3444         break;
3445     }
3446 }
3447
3448 static void
3449 nickserv_reclaim_p(void *data) {
3450     struct userNode *user = data;
3451     struct nick_info *ni = get_nick_info(user->nick);
3452     if (ni)
3453         nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3454 }
3455
3456 static int
3457 check_user_nick(struct userNode *user) {
3458     struct nick_info *ni;
3459     user->modes &= ~FLAGS_REGNICK;
3460     if (!(ni = get_nick_info(user->nick)))
3461         return 0;
3462     if (user->handle_info == ni->owner) {
3463         user->modes |= FLAGS_REGNICK;
3464         irc_regnick(user);
3465         return 0;
3466     }
3467     if (nickserv_conf.warn_nick_owned)
3468         send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3469     if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3470         return 0;
3471     if (nickserv_conf.auto_reclaim_delay)
3472         timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3473     else
3474         nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3475     return 0;
3476 }
3477
3478 int
3479 handle_new_user(struct userNode *user)
3480 {
3481     return check_user_nick(user);
3482 }
3483
3484 void
3485 handle_account(struct userNode *user, const char *stamp)
3486 {
3487     struct handle_info *hi;
3488
3489 #ifdef WITH_PROTOCOL_P10
3490     hi = dict_find(nickserv_handle_dict, stamp, NULL);
3491 #else
3492     hi = dict_find(nickserv_id_dict, stamp, NULL);
3493 #endif
3494
3495     if (hi) {
3496         if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3497             return;
3498         }
3499         set_user_handle_info(user, hi, 0);
3500     } else {
3501         log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3502     }
3503 }
3504
3505 void
3506 handle_nick_change(struct userNode *user, const char *old_nick)
3507 {
3508     struct handle_info *hi;
3509
3510     if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3511         dict_remove(nickserv_allow_auth_dict, old_nick);
3512         dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3513     }
3514     timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3515     check_user_nick(user);
3516 }
3517
3518 void
3519 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3520 {
3521     dict_remove(nickserv_allow_auth_dict, user->nick);
3522     timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3523     set_user_handle_info(user, NULL, 0);
3524 }
3525
3526 static struct modcmd *
3527 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3528 {
3529     if (min_level > 0) {
3530         char buf[16];
3531         sprintf(buf, "%u", min_level);
3532         if (must_be_qualified) {
3533             return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3534         } else {
3535             return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3536         }
3537     } else if (min_level == 0) {
3538         if (must_be_qualified) {
3539             return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3540         } else {
3541             return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3542         }
3543     } else {
3544         if (must_be_qualified) {
3545             return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3546         } else {
3547             return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3548         }
3549     }
3550 }
3551
3552 static void
3553 nickserv_db_cleanup(void)
3554 {
3555     unreg_del_user_func(nickserv_remove_user);
3556     userList_clean(&curr_helpers);
3557     policer_params_delete(nickserv_conf.auth_policer_params);
3558     dict_delete(nickserv_handle_dict);
3559     dict_delete(nickserv_nick_dict);
3560     dict_delete(nickserv_opt_dict);
3561     dict_delete(nickserv_allow_auth_dict);
3562     dict_delete(nickserv_email_dict);
3563     dict_delete(nickserv_id_dict);
3564     dict_delete(nickserv_conf.weak_password_dict);
3565     free(auth_func_list);
3566     free(unreg_func_list);
3567     free(rf_list);
3568     free(allowauth_func_list);
3569     free(handle_merge_func_list);
3570     free(failpw_func_list);
3571     if (nickserv_conf.valid_handle_regex_set)
3572         regfree(&nickserv_conf.valid_handle_regex);
3573     if (nickserv_conf.valid_nick_regex_set)
3574         regfree(&nickserv_conf.valid_nick_regex);
3575 }
3576
3577 void
3578 init_nickserv(const char *nick)
3579 {
3580     unsigned int i;
3581     NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3582     reg_new_user_func(handle_new_user);
3583     reg_nick_change_func(handle_nick_change);
3584     reg_del_user_func(nickserv_remove_user);
3585     reg_account_func(handle_account);
3586
3587     /* set up handle_inverse_flags */
3588     memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3589     for (i=0; handle_flags[i]; i++) {
3590         handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3591         flag_access_levels[i] = 0;
3592     }
3593
3594     conf_register_reload(nickserv_conf_read);
3595     nickserv_opt_dict = dict_new();
3596     nickserv_email_dict = dict_new();
3597     dict_set_free_keys(nickserv_email_dict, free);
3598     dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3599
3600     nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3601     modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3602     nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3603     nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3604     nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3605     nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3606     nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3607     nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3608     nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3609     nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3610     nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3611     nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3612     nickserv_define_func("SET", cmd_set, -1, 1, 0);
3613     nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3614     nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3615     nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3616     nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3617     nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3618     nickserv_define_func("MERGE", cmd_merge, 0, 1, 0);
3619     if (!nickserv_conf.disable_nicks) {
3620         /* nick management commands */
3621         nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3622         nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3623         nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3624         nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3625         nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3626         nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3627     }
3628     if (nickserv_conf.email_enabled) {
3629         nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3630         nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3631         nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3632         nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3633         dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3634     }
3635     nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3636     /* miscellaneous commands */
3637     nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3638     nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3639     nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3640     nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3641     nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3642     /* other options */
3643     dict_insert(nickserv_opt_dict, "INFO", opt_info);
3644     dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3645     dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3646     dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3647     dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3648     dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3649     dict_insert(nickserv_opt_dict, "PASS", opt_password);
3650     dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3651     dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3652     dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3653     dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3654     dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3655     dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3656     dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3657     dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3658
3659     nickserv_handle_dict = dict_new();
3660     dict_set_free_keys(nickserv_handle_dict, free);
3661     dict_set_free_data(nickserv_handle_dict, free_handle_info);
3662
3663     nickserv_id_dict = dict_new();
3664     dict_set_free_keys(nickserv_id_dict, free);
3665
3666     nickserv_nick_dict = dict_new();
3667     dict_set_free_data(nickserv_nick_dict, free_nick_info);
3668
3669     nickserv_allow_auth_dict = dict_new();
3670
3671     userList_init(&curr_helpers);
3672
3673     if (nick) {
3674         nickserv = AddService(nick, "Nick Services");
3675         nickserv_service = service_register(nickserv, 0);
3676     }
3677     saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3678     reg_exit_func(nickserv_db_cleanup);
3679     if(nickserv_conf.handle_expire_frequency)
3680         timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3681     message_register_table(msgtab);
3682 }