1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2006 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for gag_create(), opserv_bad_channel() */
33 # include "rx/rxposix.h"
36 #define NICKSERV_CONF_NAME "services/nickserv"
38 #define KEY_DISABLE_NICKS "disable_nicks"
39 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
40 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
41 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
42 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
43 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
44 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
45 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
46 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
47 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
48 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
49 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
50 #define KEY_MODOPER_LEVEL "modoper_level"
51 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
52 #define KEY_SET_TITLE_LEVEL "set_title_level"
53 #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
54 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
55 #define KEY_FLAG_LEVELS "flag_levels"
56 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
57 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
58 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
59 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
60 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
61 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
62 #define KEY_DICT_FILE "dict_file"
63 #define KEY_NICK "nick"
64 #define KEY_LANGUAGE "language"
65 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
66 #define KEY_AUTOGAG_DURATION "autogag_duration"
67 #define KEY_AUTH_POLICER "auth_policer"
68 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
69 #define KEY_EMAIL_ENABLED "email_enabled"
70 #define KEY_EMAIL_REQUIRED "email_required"
71 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
72 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
73 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
76 #define KEY_PASSWD "passwd"
77 #define KEY_NICKS "nicks"
78 #define KEY_MASKS "masks"
79 #define KEY_OPSERV_LEVEL "opserv_level"
80 #define KEY_FLAGS "flags"
81 #define KEY_REGISTER_ON "register"
82 #define KEY_LAST_SEEN "lastseen"
83 #define KEY_INFO "info"
84 #define KEY_USERLIST_STYLE "user_style"
85 #define KEY_SCREEN_WIDTH "screen_width"
86 #define KEY_LAST_AUTHED_HOST "last_authed_host"
87 #define KEY_LAST_QUIT_HOST "last_quit_host"
88 #define KEY_EMAIL_ADDR "email_addr"
89 #define KEY_COOKIE "cookie"
90 #define KEY_COOKIE_DATA "data"
91 #define KEY_COOKIE_TYPE "type"
92 #define KEY_COOKIE_EXPIRES "expires"
93 #define KEY_ACTIVATION "activation"
94 #define KEY_PASSWORD_CHANGE "password change"
95 #define KEY_EMAIL_CHANGE "email change"
96 #define KEY_ALLOWAUTH "allowauth"
97 #define KEY_EPITHET "epithet"
98 #define KEY_TABLE_WIDTH "table_width"
99 #define KEY_ANNOUNCEMENTS "announcements"
100 #define KEY_MAXLOGINS "maxlogins"
101 #define KEY_FAKEHOST "fakehost"
103 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
105 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
106 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
107 typedef OPTION_FUNC(option_func_t);
109 DEFINE_LIST(handle_info_list, struct handle_info*);
111 #define NICKSERV_MIN_PARMS(N) do { \
113 reply("MSG_MISSING_PARAMS", argv[0]); \
114 svccmd_send_help(user, nickserv, cmd); \
118 struct userNode *nickserv;
119 struct userList curr_helpers;
120 const char *handle_flags = HANDLE_FLAGS;
122 static struct module *nickserv_module;
123 static struct service *nickserv_service;
124 static struct log_type *NS_LOG;
125 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
126 static dict_t nickserv_id_dict; /* contains struct handle_info* */
127 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
128 static dict_t nickserv_opt_dict; /* contains option_func_t* */
129 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
130 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
131 static char handle_inverse_flags[256];
132 static unsigned int flag_access_levels[32];
133 static const struct message_entry msgtab[] = {
134 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
135 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
136 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
137 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
138 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
139 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
140 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
141 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
142 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
143 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
144 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
145 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
146 { "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." },
147 { "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." },
148 { "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." },
149 { "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." },
150 { "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." },
151 { "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." },
152 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
153 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
154 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
155 { "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." },
156 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
157 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
158 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
159 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
160 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
161 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
162 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
163 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
164 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
165 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
166 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
167 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
168 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
169 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
170 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
171 { "NSMSG_USE_AUTHCOOKIE", "Your hostmask is not valid for account $b%1$s$b. Please use the $bauthcookie$b command to grant yourself access. (/msg $S authcookie %1$s)" },
172 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
173 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
174 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
175 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
176 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
177 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
178 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
179 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
180 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
181 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
182 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
183 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
184 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
185 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
186 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
187 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
188 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
189 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
190 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
191 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
192 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
193 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
194 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
195 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
196 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
197 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
198 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
199 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
200 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
201 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
202 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
203 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
204 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
205 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
206 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
207 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
208 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
209 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
210 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
211 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
212 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
213 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
214 { "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)." },
215 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
216 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
217 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
218 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
219 { "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." },
220 { "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." },
221 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
222 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
223 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
224 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
225 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
226 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
227 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
228 { "NSMSG_PASS_SUCCESS", "Password changed." },
229 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
230 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
231 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
232 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
233 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
234 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
235 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
236 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
237 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
238 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
239 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
240 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
241 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
242 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
243 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
244 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
245 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
246 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
247 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
248 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
249 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
250 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
251 { "NSMSG_NO_ACCESS", "Access denied." },
252 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
253 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
254 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
255 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
256 { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." },
257 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
258 { "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." },
259 { "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." },
260 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
261 { "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." },
262 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
263 { "NSMSG_SEARCH_MATCH", "Match: %s" },
264 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
265 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
266 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
267 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
268 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
269 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
270 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
271 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
272 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
273 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
274 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
275 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
276 { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." },
277 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
278 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
279 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
280 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
281 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
282 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
283 { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" },
284 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
285 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
286 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
287 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
288 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
289 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
290 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
291 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
292 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
293 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
294 { "NSEMAIL_ACTIVATION_BODY", "This email has been sent to verify that this email address belongs to the person who tried to register an account on %1$s. Your cookie is:\n %2$s\nTo verify your email address and complete the account registration, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nThis command is only used once to complete your account registration, and never again. Once you have run this command, you will need to authenticate everytime you reconnect to the network. To do this, you will have to type this command every time you reconnect:\n /msg %3$s@%4$s AUTH %5$s your-password\n Please remember to fill in 'your-password' with the actual password you gave to us when you registered.\n\nIf you did NOT request this account, you do not need to do anything. Please contact the %1$s staff if you have questions, and be sure to check our website." },
295 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
296 { "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." },
297 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
298 { "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." },
299 { "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." },
300 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
301 { "NSEMAIL_EMAIL_VERIFY_BODY", "This email has been sent to verify that this address belongs to the same person as %5$s on %1$s. Your cookie is %2$s.\nTo verify your address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request this email address to be associated with this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
302 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
303 { "NSEMAIL_ALLOWAUTH_BODY", "This email has been sent to let you authenticate (auth) to account %5$s on %1$s. Your cookie is %2$s.\nTo auth to that account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request this authorization, you do not need to do anything. Please contact the %1$s staff if you have questions." },
304 { "CHECKPASS_YES", "Yes." },
305 { "CHECKPASS_NO", "No." },
309 enum reclaim_action {
315 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
316 static void nickserv_reclaim_p(void *data);
317 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
320 unsigned int disable_nicks : 1;
321 unsigned int valid_handle_regex_set : 1;
322 unsigned int valid_nick_regex_set : 1;
323 unsigned int autogag_enabled : 1;
324 unsigned int email_enabled : 1;
325 unsigned int email_required : 1;
326 unsigned int default_hostmask : 1;
327 unsigned int warn_nick_owned : 1;
328 unsigned int warn_clone_auth : 1;
329 unsigned long nicks_per_handle;
330 unsigned long password_min_length;
331 unsigned long password_min_digits;
332 unsigned long password_min_upper;
333 unsigned long password_min_lower;
334 unsigned long db_backup_frequency;
335 unsigned long handle_expire_frequency;
336 unsigned long autogag_duration;
337 unsigned long email_visible_level;
338 unsigned long cookie_timeout;
339 unsigned long handle_expire_delay;
340 unsigned long nochan_handle_expire_delay;
341 unsigned long modoper_level;
342 unsigned long set_epithet_level;
343 unsigned long set_title_level;
344 unsigned long set_fakehost_level;
345 unsigned long handles_per_email;
346 unsigned long email_search_level;
347 const char *network_name;
348 const char *titlehost_suffix;
349 regex_t valid_handle_regex;
350 regex_t valid_nick_regex;
351 dict_t weak_password_dict;
352 struct policer_params *auth_policer_params;
353 enum reclaim_action reclaim_action;
354 enum reclaim_action auto_reclaim_action;
355 unsigned long auto_reclaim_delay;
356 unsigned char default_maxlogins;
357 unsigned char hard_maxlogins;
360 /* We have 2^32 unique account IDs to use. */
361 unsigned long int highest_id = 0;
364 canonicalize_hostmask(char *mask)
366 char *out = mask, *temp;
367 if ((temp = strchr(mask, '!'))) {
369 while (*temp) *out++ = *temp++;
375 static struct handle_info *
376 register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id))
378 struct handle_info *hi;
380 #ifdef WITH_PROTOCOL_BAHAMUT
381 char id_base64[IDLEN + 1];
384 /* Assign a unique account ID to the account; note that 0 is
385 an invalid account ID. 1 is therefore the first account ID. */
387 id = 1 + highest_id++;
389 /* Note: highest_id is and must always be the highest ID. */
390 if(id > highest_id) {
394 inttobase64(id_base64, id, IDLEN);
396 /* Make sure an account with the same ID doesn't exist. If a
397 duplicate is found, log some details and assign a new one.
398 This should be impossible, but it never hurts to expect it. */
399 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
400 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
406 hi = calloc(1, sizeof(*hi));
407 hi->userlist_style = HI_DEFAULT_STYLE;
408 hi->announcements = '?';
409 hi->handle = strdup(handle);
410 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
412 dict_insert(nickserv_handle_dict, hi->handle, hi);
414 #ifdef WITH_PROTOCOL_BAHAMUT
416 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
423 register_nick(const char *nick, struct handle_info *owner)
425 struct nick_info *ni;
426 ni = malloc(sizeof(struct nick_info));
427 safestrncpy(ni->nick, nick, sizeof(ni->nick));
429 ni->next = owner->nicks;
431 dict_insert(nickserv_nick_dict, ni->nick, ni);
435 delete_nick(struct nick_info *ni)
437 struct nick_info *last, *next;
438 struct userNode *user;
439 /* Check to see if we should mark a user as unregistered. */
440 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
441 user->modes &= ~FLAGS_REGNICK;
444 /* Remove ni from the nick_info linked list. */
445 if (ni == ni->owner->nicks) {
446 ni->owner->nicks = ni->next;
448 last = ni->owner->nicks;
454 last->next = next->next;
456 dict_remove(nickserv_nick_dict, ni->nick);
459 static unreg_func_t *unreg_func_list;
460 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
463 reg_unreg_func(unreg_func_t func)
465 if (unreg_func_used == unreg_func_size) {
466 if (unreg_func_size) {
467 unreg_func_size <<= 1;
468 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
471 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
474 unreg_func_list[unreg_func_used++] = func;
478 nickserv_free_cookie(void *data)
480 struct handle_cookie *cookie = data;
481 if (cookie->hi) cookie->hi->cookie = NULL;
482 if (cookie->data) free(cookie->data);
487 free_handle_info(void *vhi)
489 struct handle_info *hi = vhi;
491 #ifdef WITH_PROTOCOL_BAHAMUT
494 inttobase64(id, hi->id, IDLEN);
495 dict_remove(nickserv_id_dict, id);
498 free_string_list(hi->masks);
502 delete_nick(hi->nicks);
507 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
508 nickserv_free_cookie(hi->cookie);
510 if (hi->email_addr) {
511 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
512 handle_info_list_remove(hil, hi);
514 dict_remove(nickserv_email_dict, hi->email_addr);
519 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
522 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
526 for (n=0; n<unreg_func_used; n++)
527 unreg_func_list[n](notify, hi);
529 set_user_handle_info(hi->users, NULL, 0);
531 if (nickserv_conf.disable_nicks)
532 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
534 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
536 dict_remove(nickserv_handle_dict, hi->handle);
540 get_handle_info(const char *handle)
542 return dict_find(nickserv_handle_dict, handle, 0);
546 get_nick_info(const char *nick)
548 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
552 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
557 for (nn=0; nn<channel->members.used; ++nn) {
558 mn = channel->members.list[nn];
559 if ((mn->user != except) && (mn->user->handle_info == handle))
566 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
567 if (!user->handle_info) {
569 send_message(user, bot, "MSG_AUTHENTICATE");
573 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
575 send_message(user, bot, "NSMSG_NO_ACCESS");
579 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
581 send_message(user, bot, "MSG_OPER_SUSPENDED");
585 if (user->handle_info->opserv_level < min_level) {
587 send_message(user, bot, "NSMSG_NO_ACCESS");
595 is_valid_handle(const char *handle)
597 struct userNode *user;
598 /* cant register a juped nick/service nick as handle, to prevent confusion */
599 user = GetUserH(handle);
600 if (user && IsLocal(user))
602 /* check against maximum length */
603 if (strlen(handle) > NICKSERV_HANDLE_LEN)
605 /* for consistency, only allow account names that could be nicks */
606 if (!is_valid_nick(handle))
608 /* disallow account names that look like bad words */
609 if (opserv_bad_channel(handle))
611 /* test either regex or containing all valid chars */
612 if (nickserv_conf.valid_handle_regex_set) {
613 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
616 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
617 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
621 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
626 is_registerable_nick(const char *nick)
628 /* make sure it could be used as an account name */
629 if (!is_valid_handle(nick))
632 if (strlen(nick) > NICKLEN)
634 /* test either regex or as valid handle */
635 if (nickserv_conf.valid_nick_regex_set) {
636 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
639 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
640 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
648 is_valid_email_addr(const char *email)
650 return strchr(email, '@') != NULL;
654 visible_email_addr(struct userNode *user, struct handle_info *hi)
656 if (hi->email_addr) {
657 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
658 return hi->email_addr;
668 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
670 struct handle_info *hi;
671 struct userNode *target;
675 if (!(hi = get_handle_info(++name))) {
676 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
681 if (!(target = GetUserH(name))) {
682 send_message(user, service, "MSG_NICK_UNKNOWN", name);
685 if (IsLocal(target)) {
686 if (IsService(target))
687 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
689 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
692 if (!(hi = target->handle_info)) {
693 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
701 oper_outranks(struct userNode *user, struct handle_info *hi) {
702 if (user->handle_info->opserv_level > hi->opserv_level)
704 if (user->handle_info->opserv_level == hi->opserv_level) {
705 if ((user->handle_info->opserv_level == 1000)
706 || (user->handle_info == hi)
707 || ((user->handle_info->opserv_level == 0)
708 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
709 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
713 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
717 static struct handle_info *
718 get_victim_oper(struct userNode *user, const char *target)
720 struct handle_info *hi;
721 if (!(hi = smart_get_handle_info(nickserv, user, target)))
723 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
724 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
727 return oper_outranks(user, hi) ? hi : NULL;
731 valid_user_for(struct userNode *user, struct handle_info *hi)
735 /* If no hostmasks on the account, allow it. */
736 if (!hi->masks->used)
738 /* If any hostmask matches, allow it. */
739 for (ii=0; ii<hi->masks->used; ii++)
740 if (user_matches_glob(user, hi->masks->list[ii], 0))
742 /* If they are allowauthed to this account, allow it (removing the aa). */
743 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
744 dict_remove(nickserv_allow_auth_dict, user->nick);
747 /* The user is not allowed to use this account. */
752 is_secure_password(const char *handle, const char *pass, struct userNode *user)
755 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
759 if (len < nickserv_conf.password_min_length) {
761 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
764 if (!irccasecmp(pass, handle)) {
766 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
769 dict_find(nickserv_conf.weak_password_dict, pass, &p);
772 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
775 for (i=0; i<len; i++) {
776 if (isdigit(pass[i]))
778 if (isupper(pass[i]))
780 if (islower(pass[i]))
783 if ((cnt_lower < nickserv_conf.password_min_lower)
784 || (cnt_upper < nickserv_conf.password_min_upper)
785 || (cnt_digits < nickserv_conf.password_min_digits)) {
787 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
793 static auth_func_t *auth_func_list;
794 static unsigned int auth_func_size = 0, auth_func_used = 0;
797 reg_auth_func(auth_func_t func)
799 if (auth_func_used == auth_func_size) {
800 if (auth_func_size) {
801 auth_func_size <<= 1;
802 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
805 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
808 auth_func_list[auth_func_used++] = func;
811 static handle_rename_func_t *rf_list;
812 static unsigned int rf_list_size, rf_list_used;
815 reg_handle_rename_func(handle_rename_func_t func)
817 if (rf_list_used == rf_list_size) {
820 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
823 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
826 rf_list[rf_list_used++] = func;
830 generate_fakehost(struct handle_info *handle)
832 extern const char *hidden_host_suffix;
833 static char buffer[HOSTLEN+1];
835 if (!handle->fakehost) {
836 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
838 } else if (handle->fakehost[0] == '.') {
839 /* A leading dot indicates the stored value is actually a title. */
840 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix);
843 return handle->fakehost;
847 apply_fakehost(struct handle_info *handle)
849 struct userNode *target;
854 fake = generate_fakehost(handle);
855 for (target = handle->users; target; target = target->next_authed)
856 assign_fakehost(target, fake, 1);
860 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
863 struct handle_info *old_info;
865 /* This can happen if somebody uses COOKIE while authed, or if
866 * they re-auth to their current handle (which is silly, but users
868 if (user->handle_info == hi)
871 if (user->handle_info) {
872 struct userNode *other;
875 userList_remove(&curr_helpers, user);
877 /* remove from next_authed linked list */
878 if (user->handle_info->users == user) {
879 user->handle_info->users = user->next_authed;
881 for (other = user->handle_info->users;
882 other->next_authed != user;
883 other = other->next_authed) ;
884 other->next_authed = user->next_authed;
886 /* if nobody left on old handle, and they're not an oper, remove !god */
887 if (!user->handle_info->users && !user->handle_info->opserv_level)
888 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
889 /* record them as being last seen at this time */
890 user->handle_info->lastseen = now;
891 /* and record their hostmask */
892 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
894 old_info = user->handle_info;
895 user->handle_info = hi;
896 if (hi && !hi->users && !hi->opserv_level)
897 HANDLE_CLEAR_FLAG(hi, HELPING);
898 for (n=0; n<auth_func_used; n++)
899 auth_func_list[n](user, old_info);
901 struct nick_info *ni;
903 HANDLE_CLEAR_FLAG(hi, FROZEN);
904 if (nickserv_conf.warn_clone_auth) {
905 struct userNode *other;
906 for (other = hi->users; other; other = other->next_authed)
907 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
909 user->next_authed = hi->users;
913 userList_append(&curr_helpers, user);
915 if (hi->fakehost || old_info)
919 #ifdef WITH_PROTOCOL_BAHAMUT
920 /* Stamp users with their account ID. */
922 inttobase64(id, hi->id, IDLEN);
923 #elif WITH_PROTOCOL_P10
924 /* Stamp users with their account name. */
925 char *id = hi->handle;
927 const char *id = "???";
929 if (!nickserv_conf.disable_nicks) {
930 struct nick_info *ni;
931 for (ni = hi->nicks; ni; ni = ni->next) {
932 if (!irccasecmp(user->nick, ni->nick)) {
933 user->modes |= FLAGS_REGNICK;
941 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
942 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
944 /* We cannot clear the user's account ID, unfortunately. */
945 user->next_authed = NULL;
949 static struct handle_info*
950 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
952 struct handle_info *hi;
953 struct nick_info *ni;
954 char crypted[MD5_CRYPT_LENGTH];
956 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
957 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
961 if (!is_secure_password(handle, passwd, user))
964 cryptpass(passwd, crypted);
965 hi = register_handle(handle, crypted, 0);
966 hi->masks = alloc_string_list(1);
968 hi->language = lang_C;
969 hi->registered = now;
971 hi->flags = HI_DEFAULT_FLAGS;
972 if (settee && !no_auth)
973 set_user_handle_info(settee, hi, 1);
976 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
977 else if (nickserv_conf.disable_nicks)
978 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
979 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
980 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
982 register_nick(user->nick, hi);
983 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
985 if (settee && (user != settee))
986 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
991 nickserv_bake_cookie(struct handle_cookie *cookie)
993 cookie->hi->cookie = cookie;
994 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
998 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1000 struct handle_cookie *cookie;
1001 char subject[128], body[4096], *misc;
1002 const char *netname, *fmt;
1006 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1010 cookie = calloc(1, sizeof(*cookie));
1012 cookie->type = type;
1013 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1014 cookie->expires = now + nickserv_conf.cookie_timeout;
1015 inttobase64(cookie->cookie, rand(), 5);
1016 inttobase64(cookie->cookie+5, rand(), 5);
1018 netname = nickserv_conf.network_name;
1021 switch (cookie->type) {
1023 hi->passwd[0] = 0; /* invalidate password */
1024 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1025 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1026 snprintf(subject, sizeof(subject), fmt, netname);
1027 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1028 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1031 case PASSWORD_CHANGE:
1032 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1033 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1034 snprintf(subject, sizeof(subject), fmt, netname);
1035 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1036 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1039 misc = hi->email_addr;
1040 hi->email_addr = cookie->data;
1042 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1043 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1044 snprintf(subject, sizeof(subject), fmt, netname);
1045 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1046 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1047 sendmail(nickserv, hi, subject, body, 1);
1048 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1049 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1051 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1052 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1053 snprintf(subject, sizeof(subject), fmt, netname);
1054 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1055 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1056 sendmail(nickserv, hi, subject, body, 1);
1059 hi->email_addr = misc;
1062 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1063 snprintf(subject, sizeof(subject), fmt, netname);
1064 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1065 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1066 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1069 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1073 sendmail(nickserv, hi, subject, body, first_time);
1074 nickserv_bake_cookie(cookie);
1078 nickserv_eat_cookie(struct handle_cookie *cookie)
1080 cookie->hi->cookie = NULL;
1081 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1082 nickserv_free_cookie(cookie);
1086 nickserv_free_email_addr(void *data)
1088 handle_info_list_clean(data);
1093 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1095 struct handle_info_list *hil;
1096 /* Remove from old handle_info_list ... */
1097 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1098 handle_info_list_remove(hil, hi);
1099 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1100 hi->email_addr = NULL;
1102 /* Add to the new list.. */
1103 if (new_email_addr) {
1104 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1105 hil = calloc(1, sizeof(*hil));
1106 hil->tag = strdup(new_email_addr);
1107 handle_info_list_init(hil);
1108 dict_insert(nickserv_email_dict, hil->tag, hil);
1110 handle_info_list_append(hil, hi);
1111 hi->email_addr = hil->tag;
1115 static NICKSERV_FUNC(cmd_register)
1118 struct handle_info *hi;
1119 const char *email_addr, *password;
1122 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1123 /* Require the first handle registered to belong to someone +o. */
1124 reply("NSMSG_REQUIRE_OPER");
1128 if (user->handle_info) {
1129 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1133 if (IsRegistering(user)) {
1134 reply("NSMSG_ALREADY_REGISTERING");
1138 if (IsStamped(user)) {
1139 /* Unauthenticated users might still have been stamped
1140 previously and could therefore have a hidden host;
1141 do not allow them to register a new account. */
1142 reply("NSMSG_STAMPED_REGISTER");
1146 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1148 if (!is_valid_handle(argv[1])) {
1149 reply("NSMSG_BAD_HANDLE", argv[1]);
1153 if ((argc >= 4) && nickserv_conf.email_enabled) {
1154 struct handle_info_list *hil;
1157 /* Remember email address. */
1158 email_addr = argv[3];
1160 /* Check that the email address looks valid.. */
1161 if (!is_valid_email_addr(email_addr)) {
1162 reply("NSMSG_BAD_EMAIL_ADDR");
1166 /* .. and that we are allowed to send to it. */
1167 if ((str = sendmail_prohibited_address(email_addr))) {
1168 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1172 /* If we do email verify, make sure we don't spam the address. */
1173 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1175 for (nn=0; nn<hil->used; nn++) {
1176 if (hil->list[nn]->cookie) {
1177 reply("NSMSG_EMAIL_UNACTIVATED");
1181 if (hil->used >= nickserv_conf.handles_per_email) {
1182 reply("NSMSG_EMAIL_OVERUSED");
1195 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1197 /* Add any masks they should get. */
1198 if (nickserv_conf.default_hostmask) {
1199 string_list_append(hi->masks, strdup("*@*"));
1201 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1202 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1203 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1206 /* If they're the first to register, give them level 1000. */
1207 if (dict_size(nickserv_handle_dict) == 1) {
1208 hi->opserv_level = 1000;
1209 reply("NSMSG_ROOT_HANDLE", argv[1]);
1212 /* Set their email address. */
1214 nickserv_set_email_addr(hi, email_addr);
1216 /* If they need to do email verification, tell them. */
1218 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1220 /* Set registering flag.. */
1221 user->modes |= FLAGS_REGISTERING;
1226 static NICKSERV_FUNC(cmd_oregister)
1229 struct userNode *settee;
1230 struct handle_info *hi;
1232 NICKSERV_MIN_PARMS(3);
1234 if (!is_valid_handle(argv[1])) {
1235 reply("NSMSG_BAD_HANDLE", argv[1]);
1242 } else if (strchr(argv[3], '@')) {
1243 mask = canonicalize_hostmask(strdup(argv[3]));
1245 settee = GetUserH(argv[4]);
1247 reply("MSG_NICK_UNKNOWN", argv[4]);
1254 } else if ((settee = GetUserH(argv[3]))) {
1255 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1257 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1260 if (settee && settee->handle_info) {
1261 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1265 if (!(hi = nickserv_register(user, settee, argv[1], argv[2], 0))) {
1270 string_list_append(hi->masks, mask);
1274 static NICKSERV_FUNC(cmd_handleinfo)
1277 unsigned int i, pos=0, herelen;
1278 struct userNode *target, *next_un;
1279 struct handle_info *hi;
1280 const char *nsmsg_none;
1283 if (!(hi = user->handle_info)) {
1284 reply("NSMSG_MUST_AUTH");
1287 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1291 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1292 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1293 #ifdef WITH_PROTOCOL_BAHAMUT
1294 reply("NSMSG_HANDLEINFO_ID", hi->id);
1296 reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered));
1299 intervalString(buff, now - hi->lastseen, user->handle_info);
1300 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1302 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1305 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1306 if (HANDLE_FLAGGED(hi, FROZEN))
1307 reply("NSMSG_HANDLEINFO_VACATION");
1309 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1310 struct do_not_register *dnr;
1311 if ((dnr = chanserv_is_dnr(NULL, hi)))
1312 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1313 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1315 } else if (hi != user->handle_info)
1318 if (nickserv_conf.email_enabled)
1319 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1323 switch (hi->cookie->type) {
1324 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1325 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1326 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1327 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1328 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1334 unsigned long flen = 1;
1335 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1337 for (i=0, flen=1; handle_flags[i]; i++)
1338 if (hi->flags & 1 << i)
1339 flags[flen++] = handle_flags[i];
1341 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1343 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1346 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1347 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1348 || (hi->opserv_level > 0)) {
1349 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1353 reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE")));
1355 if (hi->last_quit_host[0])
1356 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1358 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1360 if (nickserv_conf.disable_nicks) {
1361 /* nicks disabled; don't show anything about registered nicks */
1362 } else if (hi->nicks) {
1363 struct nick_info *ni, *next_ni;
1364 for (ni = hi->nicks; ni; ni = next_ni) {
1365 herelen = strlen(ni->nick);
1366 if (pos + herelen + 1 > ArrayLength(buff)) {
1368 goto print_nicks_buff;
1372 memcpy(buff+pos, ni->nick, herelen);
1373 pos += herelen; buff[pos++] = ' ';
1377 reply("NSMSG_HANDLEINFO_NICKS", buff);
1382 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1385 if (hi->masks->used) {
1386 for (i=0; i < hi->masks->used; i++) {
1387 herelen = strlen(hi->masks->list[i]);
1388 if (pos + herelen + 1 > ArrayLength(buff)) {
1390 goto print_mask_buff;
1392 memcpy(buff+pos, hi->masks->list[i], herelen);
1393 pos += herelen; buff[pos++] = ' ';
1394 if (i+1 == hi->masks->used) {
1397 reply("NSMSG_HANDLEINFO_MASKS", buff);
1402 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1406 struct userData *channel, *next;
1409 for (channel = hi->channels; channel; channel = next) {
1410 next = channel->u_next;
1411 name = channel->channel->channel->name;
1412 herelen = strlen(name);
1413 if (pos + herelen + 7 > ArrayLength(buff)) {
1415 goto print_chans_buff;
1417 if (IsUserSuspended(channel))
1419 pos += sprintf(buff+pos, "%d:%s ", channel->access, name);
1423 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1428 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1431 for (target = hi->users; target; target = next_un) {
1432 herelen = strlen(target->nick);
1433 if (pos + herelen + 1 > ArrayLength(buff)) {
1435 goto print_cnick_buff;
1437 next_un = target->next_authed;
1439 memcpy(buff+pos, target->nick, herelen);
1440 pos += herelen; buff[pos++] = ' ';
1444 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1452 static NICKSERV_FUNC(cmd_userinfo)
1454 struct userNode *target;
1456 NICKSERV_MIN_PARMS(2);
1457 if (!(target = GetUserH(argv[1]))) {
1458 reply("MSG_NICK_UNKNOWN", argv[1]);
1461 if (target->handle_info)
1462 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1464 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1468 static NICKSERV_FUNC(cmd_nickinfo)
1470 struct nick_info *ni;
1472 NICKSERV_MIN_PARMS(2);
1473 if (!(ni = get_nick_info(argv[1]))) {
1474 reply("MSG_NICK_UNKNOWN", argv[1]);
1477 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1481 static NICKSERV_FUNC(cmd_rename_handle)
1483 struct handle_info *hi;
1484 char msgbuf[MAXLEN], *old_handle;
1487 NICKSERV_MIN_PARMS(3);
1488 if (!(hi = get_victim_oper(user, argv[1])))
1490 if (!is_valid_handle(argv[2])) {
1491 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1494 if (get_handle_info(argv[2])) {
1495 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1499 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1500 hi->handle = strdup(argv[2]);
1501 dict_insert(nickserv_handle_dict, hi->handle, hi);
1502 for (nn=0; nn<rf_list_used; nn++)
1503 rf_list[nn](hi, old_handle);
1504 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1505 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1506 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1511 static failpw_func_t *failpw_func_list;
1512 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1515 reg_failpw_func(failpw_func_t func)
1517 if (failpw_func_used == failpw_func_size) {
1518 if (failpw_func_size) {
1519 failpw_func_size <<= 1;
1520 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1522 failpw_func_size = 8;
1523 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1526 failpw_func_list[failpw_func_used++] = func;
1529 static NICKSERV_FUNC(cmd_auth)
1531 int pw_arg, used, maxlogins;
1532 struct handle_info *hi;
1534 struct userNode *other;
1536 if (user->handle_info) {
1537 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1540 if (IsStamped(user)) {
1541 /* Unauthenticated users might still have been stamped
1542 previously and could therefore have a hidden host;
1543 do not allow them to authenticate. */
1544 reply("NSMSG_STAMPED_AUTH");
1548 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1550 } else if (argc == 2) {
1551 if (nickserv_conf.disable_nicks) {
1552 if (!(hi = get_handle_info(user->nick))) {
1553 reply("NSMSG_HANDLE_NOT_FOUND");
1557 /* try to look up their handle from their nick */
1558 struct nick_info *ni;
1559 ni = get_nick_info(user->nick);
1561 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1568 reply("MSG_MISSING_PARAMS", argv[0]);
1569 svccmd_send_help(user, nickserv, cmd);
1573 reply("NSMSG_HANDLE_NOT_FOUND");
1576 /* Responses from here on look up the language used by the handle they asked about. */
1577 passwd = argv[pw_arg];
1578 if (!valid_user_for(user, hi)) {
1579 if (hi->email_addr && nickserv_conf.email_enabled)
1580 send_message_type(4, user, cmd->parent->bot,
1581 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1584 send_message_type(4, user, cmd->parent->bot,
1585 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1587 argv[pw_arg] = "BADMASK";
1590 if (!checkpass(passwd, hi->passwd)) {
1592 send_message_type(4, user, cmd->parent->bot,
1593 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1594 argv[pw_arg] = "BADPASS";
1595 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1596 if (nickserv_conf.autogag_enabled) {
1597 if (!user->auth_policer.params) {
1598 user->auth_policer.last_req = now;
1599 user->auth_policer.params = nickserv_conf.auth_policer_params;
1601 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1603 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1604 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1605 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1607 argv[pw_arg] = "GAGGED";
1612 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1613 send_message_type(4, user, cmd->parent->bot,
1614 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1615 argv[pw_arg] = "SUSPENDED";
1618 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1619 for (used = 0, other = hi->users; other; other = other->next_authed) {
1620 if (++used >= maxlogins) {
1621 send_message_type(4, user, cmd->parent->bot,
1622 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1624 argv[pw_arg] = "MAXLOGINS";
1629 set_user_handle_info(user, hi, 1);
1630 if (nickserv_conf.email_required && !hi->email_addr)
1631 reply("NSMSG_PLEASE_SET_EMAIL");
1632 if (!is_secure_password(hi->handle, passwd, NULL))
1633 reply("NSMSG_WEAK_PASSWORD");
1634 if (hi->passwd[0] != '$')
1635 cryptpass(passwd, hi->passwd);
1636 if (!hi->masks->used) {
1638 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1639 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1640 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1642 argv[pw_arg] = "****";
1643 reply("NSMSG_AUTH_SUCCESS");
1647 static allowauth_func_t *allowauth_func_list;
1648 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1651 reg_allowauth_func(allowauth_func_t func)
1653 if (allowauth_func_used == allowauth_func_size) {
1654 if (allowauth_func_size) {
1655 allowauth_func_size <<= 1;
1656 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1658 allowauth_func_size = 8;
1659 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1662 allowauth_func_list[allowauth_func_used++] = func;
1665 static NICKSERV_FUNC(cmd_allowauth)
1667 struct userNode *target;
1668 struct handle_info *hi;
1671 NICKSERV_MIN_PARMS(2);
1672 if (!(target = GetUserH(argv[1]))) {
1673 reply("MSG_NICK_UNKNOWN", argv[1]);
1676 if (target->handle_info) {
1677 reply("NSMSG_USER_PREV_AUTH", target->nick);
1680 if (IsStamped(target)) {
1681 /* Unauthenticated users might still have been stamped
1682 previously and could therefore have a hidden host;
1683 do not allow them to authenticate to an account. */
1684 reply("NSMSG_USER_PREV_STAMP", target->nick);
1689 else if (!(hi = get_handle_info(argv[2]))) {
1690 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1694 if (hi->opserv_level > user->handle_info->opserv_level) {
1695 reply("MSG_USER_OUTRANKED", hi->handle);
1698 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1699 || (hi->opserv_level > 0))
1700 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1701 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1704 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1705 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1706 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1707 if (nickserv_conf.email_enabled)
1708 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1710 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1711 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1713 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1715 for (n=0; n<allowauth_func_used; n++)
1716 allowauth_func_list[n](user, target, hi);
1720 static NICKSERV_FUNC(cmd_authcookie)
1722 struct handle_info *hi;
1724 NICKSERV_MIN_PARMS(2);
1725 if (user->handle_info) {
1726 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1729 if (IsStamped(user)) {
1730 /* Unauthenticated users might still have been stamped
1731 previously and could therefore have a hidden host;
1732 do not allow them to authenticate to an account. */
1733 reply("NSMSG_STAMPED_AUTHCOOKIE");
1736 if (!(hi = get_handle_info(argv[1]))) {
1737 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1740 if (!hi->email_addr) {
1741 reply("MSG_SET_EMAIL_ADDR");
1744 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1748 static NICKSERV_FUNC(cmd_delcookie)
1750 struct handle_info *hi;
1752 hi = user->handle_info;
1754 reply("NSMSG_NO_COOKIE");
1757 switch (hi->cookie->type) {
1760 reply("NSMSG_MUST_TIME_OUT");
1763 nickserv_eat_cookie(hi->cookie);
1764 reply("NSMSG_ATE_COOKIE");
1770 static NICKSERV_FUNC(cmd_resetpass)
1772 struct handle_info *hi;
1773 char crypted[MD5_CRYPT_LENGTH];
1775 NICKSERV_MIN_PARMS(3);
1776 if (user->handle_info) {
1777 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1780 if (IsStamped(user)) {
1781 /* Unauthenticated users might still have been stamped
1782 previously and could therefore have a hidden host;
1783 do not allow them to activate an account. */
1784 reply("NSMSG_STAMPED_RESETPASS");
1787 if (!(hi = get_handle_info(argv[1]))) {
1788 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1791 if (!hi->email_addr) {
1792 reply("MSG_SET_EMAIL_ADDR");
1795 cryptpass(argv[2], crypted);
1797 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1801 static NICKSERV_FUNC(cmd_cookie)
1803 struct handle_info *hi;
1806 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1809 NICKSERV_MIN_PARMS(3);
1810 if (!(hi = get_handle_info(argv[1]))) {
1811 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1817 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1818 reply("NSMSG_HANDLE_SUSPENDED");
1823 reply("NSMSG_NO_COOKIE");
1827 /* Check validity of operation before comparing cookie to
1828 * prohibit guessing by authed users. */
1829 if (user->handle_info
1830 && (hi->cookie->type != EMAIL_CHANGE)
1831 && (hi->cookie->type != PASSWORD_CHANGE)) {
1832 reply("NSMSG_CANNOT_COOKIE");
1836 if (strcmp(cookie, hi->cookie->cookie)) {
1837 reply("NSMSG_BAD_COOKIE");
1841 switch (hi->cookie->type) {
1843 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1844 set_user_handle_info(user, hi, 1);
1845 reply("NSMSG_HANDLE_ACTIVATED");
1847 case PASSWORD_CHANGE:
1848 set_user_handle_info(user, hi, 1);
1849 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
1850 reply("NSMSG_PASSWORD_CHANGED");
1853 nickserv_set_email_addr(hi, hi->cookie->data);
1854 reply("NSMSG_EMAIL_CHANGED");
1857 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1858 set_user_handle_info(user, hi, 1);
1859 nickserv_addmask(user, hi, mask);
1860 reply("NSMSG_AUTH_SUCCESS");
1865 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
1866 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
1870 nickserv_eat_cookie(hi->cookie);
1875 static NICKSERV_FUNC(cmd_oregnick) {
1877 struct handle_info *target;
1878 struct nick_info *ni;
1880 NICKSERV_MIN_PARMS(3);
1881 if (!(target = modcmd_get_handle_info(user, argv[1])))
1884 if (!is_registerable_nick(nick)) {
1885 reply("NSMSG_BAD_NICK", nick);
1888 ni = dict_find(nickserv_nick_dict, nick, NULL);
1890 reply("NSMSG_NICK_EXISTS", nick);
1893 register_nick(nick, target);
1894 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
1898 static NICKSERV_FUNC(cmd_regnick) {
1900 struct nick_info *ni;
1902 if (!is_registerable_nick(user->nick)) {
1903 reply("NSMSG_BAD_NICK", user->nick);
1906 /* count their nicks, see if it's too many */
1907 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
1908 if (n >= nickserv_conf.nicks_per_handle) {
1909 reply("NSMSG_TOO_MANY_NICKS");
1912 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
1914 reply("NSMSG_NICK_EXISTS", user->nick);
1917 register_nick(user->nick, user->handle_info);
1918 reply("NSMSG_REGNICK_SUCCESS", user->nick);
1922 static NICKSERV_FUNC(cmd_pass)
1924 struct handle_info *hi;
1925 const char *old_pass, *new_pass;
1927 NICKSERV_MIN_PARMS(3);
1928 hi = user->handle_info;
1932 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
1933 if (!checkpass(old_pass, hi->passwd)) {
1934 argv[1] = "BADPASS";
1935 reply("NSMSG_PASSWORD_INVALID");
1938 cryptpass(new_pass, hi->passwd);
1940 reply("NSMSG_PASS_SUCCESS");
1945 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
1948 char *new_mask = canonicalize_hostmask(strdup(mask));
1949 for (i=0; i<hi->masks->used; i++) {
1950 if (!irccasecmp(new_mask, hi->masks->list[i])) {
1951 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
1956 string_list_append(hi->masks, new_mask);
1957 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
1961 static NICKSERV_FUNC(cmd_addmask)
1964 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1965 int res = nickserv_addmask(user, user->handle_info, mask);
1969 if (!is_gline(argv[1])) {
1970 reply("NSMSG_MASK_INVALID", argv[1]);
1973 return nickserv_addmask(user, user->handle_info, argv[1]);
1977 static NICKSERV_FUNC(cmd_oaddmask)
1979 struct handle_info *hi;
1981 NICKSERV_MIN_PARMS(3);
1982 if (!(hi = get_victim_oper(user, argv[1])))
1984 return nickserv_addmask(user, hi, argv[2]);
1988 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask)
1991 for (i=0; i<hi->masks->used; i++) {
1992 if (!strcmp(del_mask, hi->masks->list[i])) {
1993 char *old_mask = hi->masks->list[i];
1994 if (hi->masks->used == 1) {
1995 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
1998 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
1999 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2004 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2008 static NICKSERV_FUNC(cmd_delmask)
2010 NICKSERV_MIN_PARMS(2);
2011 return nickserv_delmask(user, user->handle_info, argv[1]);
2014 static NICKSERV_FUNC(cmd_odelmask)
2016 struct handle_info *hi;
2017 NICKSERV_MIN_PARMS(3);
2018 if (!(hi = get_victim_oper(user, argv[1])))
2020 return nickserv_delmask(user, hi, argv[2]);
2024 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2025 unsigned int nn, add = 1, pos;
2026 unsigned long added, removed, flag;
2028 for (added=removed=nn=0; str[nn]; nn++) {
2030 case '+': add = 1; break;
2031 case '-': add = 0; break;
2033 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2034 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2037 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2038 /* cheesy avoidance of looking up the flag name.. */
2039 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2042 flag = 1 << (pos - 1);
2044 added |= flag, removed &= ~flag;
2046 removed |= flag, added &= ~flag;
2051 *premoved = removed;
2056 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2058 unsigned long before, after, added, removed;
2059 struct userNode *uNode;
2061 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2062 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2064 hi->flags = (hi->flags | added) & ~removed;
2065 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2067 /* Strip helping flag if they're only a support helper and not
2068 * currently in #support. */
2069 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2070 struct channelList *schannels;
2072 schannels = chanserv_support_channels();
2073 for (uNode = hi->users; uNode; uNode = uNode->next_authed) {
2074 for (ii = 0; ii < schannels->used; ++ii)
2075 if (GetUserMode(schannels->list[ii], uNode))
2077 if (ii < schannels->used)
2081 HANDLE_CLEAR_FLAG(hi, HELPING);
2084 if (after && !before) {
2085 /* Add user to current helper list. */
2086 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2087 userList_append(&curr_helpers, uNode);
2088 } else if (!after && before) {
2089 /* Remove user from current helper list. */
2090 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2091 userList_remove(&curr_helpers, uNode);
2098 set_list(struct userNode *user, struct handle_info *hi, int override)
2102 char *set_display[] = {
2103 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2104 "EMAIL", "ANNOUNCEMENTS", "MAXLOGINS", "LANGUAGE"
2107 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2109 /* Do this so options are presented in a consistent order. */
2110 for (i = 0; i < ArrayLength(set_display); ++i)
2111 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2112 opt(user, hi, override, 0, NULL);
2115 static NICKSERV_FUNC(cmd_set)
2117 struct handle_info *hi;
2120 hi = user->handle_info;
2122 set_list(user, hi, 0);
2125 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2126 reply("NSMSG_INVALID_OPTION", argv[1]);
2129 return opt(user, hi, 0, argc-1, argv+1);
2132 static NICKSERV_FUNC(cmd_oset)
2134 struct handle_info *hi;
2137 NICKSERV_MIN_PARMS(2);
2139 if (!(hi = get_victim_oper(user, argv[1])))
2143 set_list(user, hi, 0);
2147 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2148 reply("NSMSG_INVALID_OPTION", argv[2]);
2152 return opt(user, hi, 1, argc-2, argv+2);
2155 static OPTION_FUNC(opt_info)
2159 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2161 hi->infoline = NULL;
2163 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2167 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2168 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2172 static OPTION_FUNC(opt_width)
2175 hi->screen_width = strtoul(argv[1], NULL, 0);
2177 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2178 hi->screen_width = MIN_LINE_SIZE;
2179 else if (hi->screen_width > MAX_LINE_SIZE)
2180 hi->screen_width = MAX_LINE_SIZE;
2182 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2186 static OPTION_FUNC(opt_tablewidth)
2189 hi->table_width = strtoul(argv[1], NULL, 0);
2191 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2192 hi->table_width = MIN_LINE_SIZE;
2193 else if (hi->screen_width > MAX_LINE_SIZE)
2194 hi->table_width = MAX_LINE_SIZE;
2196 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2200 static OPTION_FUNC(opt_color)
2203 if (enabled_string(argv[1]))
2204 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2205 else if (disabled_string(argv[1]))
2206 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2208 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2213 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2217 static OPTION_FUNC(opt_privmsg)
2220 if (enabled_string(argv[1]))
2221 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2222 else if (disabled_string(argv[1]))
2223 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2225 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2230 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2234 static OPTION_FUNC(opt_style)
2239 if (!irccasecmp(argv[1], "Zoot"))
2240 hi->userlist_style = HI_STYLE_ZOOT;
2241 else if (!irccasecmp(argv[1], "def"))
2242 hi->userlist_style = HI_STYLE_DEF;
2245 switch (hi->userlist_style) {
2254 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2258 static OPTION_FUNC(opt_announcements)
2263 if (enabled_string(argv[1]))
2264 hi->announcements = 'y';
2265 else if (disabled_string(argv[1]))
2266 hi->announcements = 'n';
2267 else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default"))
2268 hi->announcements = '?';
2270 send_message(user, nickserv, "NSMSG_INVALID_ANNOUNCE", argv[1]);
2275 switch (hi->announcements) {
2276 case 'y': choice = user_find_message(user, "MSG_ON"); break;
2277 case 'n': choice = user_find_message(user, "MSG_OFF"); break;
2278 case '?': choice = "default"; break;
2279 default: choice = "unknown"; break;
2281 send_message(user, nickserv, "NSMSG_SET_ANNOUNCEMENTS", choice);
2285 static OPTION_FUNC(opt_password)
2288 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2293 cryptpass(argv[1], hi->passwd);
2295 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2299 static OPTION_FUNC(opt_flags)
2302 unsigned int ii, flen;
2305 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2310 nickserv_apply_flags(user, hi, argv[1]);
2312 for (ii = flen = 0; handle_flags[ii]; ii++)
2313 if (hi->flags & (1 << ii))
2314 flags[flen++] = handle_flags[ii];
2317 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2319 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2323 static OPTION_FUNC(opt_email)
2327 if (!is_valid_email_addr(argv[1])) {
2328 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2331 if ((str = sendmail_prohibited_address(argv[1]))) {
2332 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2335 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2336 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2338 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2340 nickserv_set_email_addr(hi, argv[1]);
2342 nickserv_eat_cookie(hi->cookie);
2343 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2346 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2350 static OPTION_FUNC(opt_maxlogins)
2352 unsigned char maxlogins;
2354 maxlogins = strtoul(argv[1], NULL, 0);
2355 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2356 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2359 hi->maxlogins = maxlogins;
2361 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2362 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2366 static OPTION_FUNC(opt_language)
2368 struct language *lang;
2370 lang = language_find(argv[1]);
2371 if (irccasecmp(lang->name, argv[1]))
2372 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2373 hi->language = lang;
2375 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2380 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2381 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2383 if ((user->handle_info->opserv_level < target->opserv_level)
2384 || ((user->handle_info->opserv_level == target->opserv_level)
2385 && (user->handle_info->opserv_level < 1000))) {
2386 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2389 if ((user->handle_info->opserv_level < new_level)
2390 || ((user->handle_info->opserv_level == new_level)
2391 && (user->handle_info->opserv_level < 1000))) {
2392 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2395 if (user->handle_info == target) {
2396 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2399 if (target->opserv_level == new_level)
2401 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2402 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2403 target->opserv_level = new_level;
2407 static OPTION_FUNC(opt_level)
2412 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2416 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2417 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2421 static OPTION_FUNC(opt_epithet)
2424 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2428 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2429 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2432 if ((epithet[0] == '*') && !epithet[1])
2435 hi->epithet = strdup(epithet);
2439 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2441 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2445 static OPTION_FUNC(opt_title)
2450 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2454 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2456 if (strchr(title, '.')) {
2457 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2460 if ((strlen(user->handle_info->handle) + strlen(title) +
2461 strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) {
2462 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2467 if (!strcmp(title, "*")) {
2468 hi->fakehost = NULL;
2470 hi->fakehost = malloc(strlen(title)+2);
2471 hi->fakehost[0] = '.';
2472 strcpy(hi->fakehost+1, title);
2475 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2476 title = hi->fakehost + 1;
2480 title = user_find_message(user, "MSG_NONE");
2481 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2485 static OPTION_FUNC(opt_fakehost)
2490 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2494 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2496 if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) {
2497 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2501 if (!strcmp(fake, "*"))
2502 hi->fakehost = NULL;
2504 hi->fakehost = strdup(fake);
2505 fake = hi->fakehost;
2508 fake = generate_fakehost(hi);
2510 fake = user_find_message(user, "MSG_NONE");
2511 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake);
2515 static NICKSERV_FUNC(cmd_reclaim)
2517 struct handle_info *hi;
2518 struct nick_info *ni;
2519 struct userNode *victim;
2521 NICKSERV_MIN_PARMS(2);
2522 hi = user->handle_info;
2523 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2525 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2528 if (ni->owner != user->handle_info) {
2529 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2532 victim = GetUserH(ni->nick);
2534 reply("MSG_NICK_UNKNOWN", ni->nick);
2537 if (victim == user) {
2538 reply("NSMSG_NICK_USER_YOU");
2541 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2542 switch (nickserv_conf.reclaim_action) {
2543 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2544 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2545 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2546 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2551 static NICKSERV_FUNC(cmd_unregnick)
2554 struct handle_info *hi;
2555 struct nick_info *ni;
2557 hi = user->handle_info;
2558 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2559 ni = dict_find(nickserv_nick_dict, nick, NULL);
2561 reply("NSMSG_UNKNOWN_NICK", nick);
2564 if (hi != ni->owner) {
2565 reply("NSMSG_NOT_YOUR_NICK", nick);
2568 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2573 static NICKSERV_FUNC(cmd_ounregnick)
2575 struct nick_info *ni;
2577 NICKSERV_MIN_PARMS(2);
2578 if (!(ni = get_nick_info(argv[1]))) {
2579 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2582 if (!oper_outranks(user, ni->owner))
2584 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2589 static NICKSERV_FUNC(cmd_unregister)
2591 struct handle_info *hi;
2594 NICKSERV_MIN_PARMS(2);
2595 hi = user->handle_info;
2598 if (checkpass(passwd, hi->passwd)) {
2599 nickserv_unregister_handle(hi, user);
2602 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2603 reply("NSMSG_PASSWORD_INVALID");
2608 static NICKSERV_FUNC(cmd_ounregister)
2610 struct handle_info *hi;
2611 char reason[MAXLEN];
2613 NICKSERV_MIN_PARMS(2);
2614 if (!(hi = get_victim_oper(user, argv[1])))
2616 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2617 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2618 nickserv_unregister_handle(hi, user);
2622 static NICKSERV_FUNC(cmd_status)
2624 if (nickserv_conf.disable_nicks) {
2625 reply("NSMSG_GLOBAL_STATS_NONICK",
2626 dict_size(nickserv_handle_dict));
2628 if (user->handle_info) {
2630 struct nick_info *ni;
2631 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2632 reply("NSMSG_HANDLE_STATS", cnt);
2634 reply("NSMSG_HANDLE_NONE");
2636 reply("NSMSG_GLOBAL_STATS",
2637 dict_size(nickserv_handle_dict),
2638 dict_size(nickserv_nick_dict));
2643 static NICKSERV_FUNC(cmd_ghost)
2645 struct userNode *target;
2646 char reason[MAXLEN];
2648 NICKSERV_MIN_PARMS(2);
2649 if (!(target = GetUserH(argv[1]))) {
2650 reply("MSG_NICK_UNKNOWN", argv[1]);
2653 if (target == user) {
2654 reply("NSMSG_CANNOT_GHOST_SELF");
2657 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2658 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2661 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2662 DelUser(target, nickserv, 1, reason);
2663 reply("NSMSG_GHOST_KILLED", argv[1]);
2667 static NICKSERV_FUNC(cmd_vacation)
2669 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2670 reply("NSMSG_ON_VACATION");
2675 nickserv_saxdb_write(struct saxdb_context *ctx) {
2677 struct handle_info *hi;
2680 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2682 #ifdef WITH_PROTOCOL_BAHAMUT
2685 saxdb_start_record(ctx, iter_key(it), 0);
2686 if (hi->announcements != '?') {
2687 flags[0] = hi->announcements;
2689 saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags);
2692 struct handle_cookie *cookie = hi->cookie;
2695 switch (cookie->type) {
2696 case ACTIVATION: type = KEY_ACTIVATION; break;
2697 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
2698 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
2699 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
2700 default: type = NULL; break;
2703 saxdb_start_record(ctx, KEY_COOKIE, 0);
2704 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
2705 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
2707 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
2708 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
2709 saxdb_end_record(ctx);
2713 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
2715 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
2717 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
2721 for (ii=flen=0; handle_flags[ii]; ++ii)
2722 if (hi->flags & (1 << ii))
2723 flags[flen++] = handle_flags[ii];
2725 saxdb_write_string(ctx, KEY_FLAGS, flags);
2727 #ifdef WITH_PROTOCOL_BAHAMUT
2728 saxdb_write_int(ctx, KEY_ID, hi->id);
2731 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
2732 if (hi->last_quit_host[0])
2733 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
2734 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
2735 if (hi->masks->used)
2736 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
2738 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
2740 struct string_list *slist;
2741 struct nick_info *ni;
2743 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
2744 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
2745 saxdb_write_string_list(ctx, KEY_NICKS, slist);
2749 if (hi->opserv_level)
2750 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
2751 if (hi->language != lang_C)
2752 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
2753 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
2754 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
2755 if (hi->screen_width)
2756 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
2757 if (hi->table_width)
2758 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
2759 flags[0] = hi->userlist_style;
2761 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
2762 saxdb_end_record(ctx);
2767 static handle_merge_func_t *handle_merge_func_list;
2768 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
2771 reg_handle_merge_func(handle_merge_func_t func)
2773 if (handle_merge_func_used == handle_merge_func_size) {
2774 if (handle_merge_func_size) {
2775 handle_merge_func_size <<= 1;
2776 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
2778 handle_merge_func_size = 8;
2779 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
2782 handle_merge_func_list[handle_merge_func_used++] = func;
2785 static NICKSERV_FUNC(cmd_merge)
2787 struct handle_info *hi_from, *hi_to;
2788 struct userNode *last_user;
2789 struct userData *cList, *cListNext;
2790 unsigned int ii, jj, n;
2791 char buffer[MAXLEN];
2793 NICKSERV_MIN_PARMS(3);
2795 if (!(hi_from = get_victim_oper(user, argv[1])))
2797 if (!(hi_to = get_victim_oper(user, argv[2])))
2799 if (hi_to == hi_from) {
2800 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
2804 for (n=0; n<handle_merge_func_used; n++)
2805 handle_merge_func_list[n](user, hi_to, hi_from);
2807 /* Append "from" handle's nicks to "to" handle's nick list. */
2809 struct nick_info *last_ni;
2810 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
2811 last_ni->next = hi_from->nicks;
2813 while (hi_from->nicks) {
2814 hi_from->nicks->owner = hi_to;
2815 hi_from->nicks = hi_from->nicks->next;
2818 /* Merge the hostmasks. */
2819 for (ii=0; ii<hi_from->masks->used; ii++) {
2820 char *mask = hi_from->masks->list[ii];
2821 for (jj=0; jj<hi_to->masks->used; jj++)
2822 if (match_ircglobs(hi_to->masks->list[jj], mask))
2824 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
2825 string_list_append(hi_to->masks, strdup(mask));
2828 /* Merge the lists of authed users. */
2830 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
2831 last_user->next_authed = hi_from->users;
2833 hi_to->users = hi_from->users;
2835 /* Repoint the old "from" handle's users. */
2836 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
2837 last_user->handle_info = hi_to;
2839 hi_from->users = NULL;
2841 /* Merge channel userlists. */
2842 for (cList=hi_from->channels; cList; cList=cListNext) {
2843 struct userData *cList2;
2844 cListNext = cList->u_next;
2845 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
2846 if (cList->channel == cList2->channel)
2848 if (cList2 && (cList2->access >= cList->access)) {
2849 log_module(NS_LOG, LOG_INFO, "Merge: %s had only %d access in %s (versus %d for %s)", hi_from->handle, cList->access, cList->channel->channel->name, cList2->access, hi_to->handle);
2850 /* keep cList2 in hi_to; remove cList from hi_from */
2851 del_channel_user(cList, 1);
2854 log_module(NS_LOG, LOG_INFO, "Merge: %s had only %d access in %s (versus %d for %s)", hi_to->handle, cList2->access, cList->channel->channel->name, cList->access, hi_from->handle);
2855 /* remove the lower-ranking cList2 from hi_to */
2856 del_channel_user(cList2, 1);
2858 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
2860 /* cList needs to be moved from hi_from to hi_to */
2861 cList->handle = hi_to;
2862 /* Remove from linked list for hi_from */
2863 assert(!cList->u_prev);
2864 hi_from->channels = cList->u_next;
2866 cList->u_next->u_prev = cList->u_prev;
2867 /* Add to linked list for hi_to */
2868 cList->u_prev = NULL;
2869 cList->u_next = hi_to->channels;
2870 if (hi_to->channels)
2871 hi_to->channels->u_prev = cList;
2872 hi_to->channels = cList;
2876 /* Do they get an OpServ level promotion? */
2877 if (hi_from->opserv_level > hi_to->opserv_level)
2878 hi_to->opserv_level = hi_from->opserv_level;
2880 /* What about last seen time? */
2881 if (hi_from->lastseen > hi_to->lastseen)
2882 hi_to->lastseen = hi_from->lastseen;
2884 /* Does a fakehost carry over? (This intentionally doesn't set it
2885 * for users previously attached to hi_to. They'll just have to
2888 if (hi_from->fakehost && !hi_to->fakehost)
2889 hi_to->fakehost = strdup(hi_from->fakehost);
2891 /* Notify of success. */
2892 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
2893 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
2894 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
2896 /* Unregister the "from" handle. */
2897 nickserv_unregister_handle(hi_from, NULL);
2902 struct nickserv_discrim {
2903 unsigned int limit, min_level, max_level;
2904 unsigned long flags_on, flags_off;
2905 time_t min_registered, max_registered;
2907 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
2908 const char *nickmask;
2909 const char *hostmask;
2910 const char *handlemask;
2911 const char *emailmask;
2914 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
2916 struct discrim_apply_info {
2917 struct nickserv_discrim *discrim;
2918 discrim_search_func func;
2919 struct userNode *source;
2920 unsigned int matched;
2923 static struct nickserv_discrim *
2924 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
2927 struct nickserv_discrim *discrim;
2929 discrim = malloc(sizeof(*discrim));
2930 memset(discrim, 0, sizeof(*discrim));
2931 discrim->min_level = 0;
2932 discrim->max_level = ~0;
2933 discrim->limit = 50;
2934 discrim->min_registered = 0;
2935 discrim->max_registered = INT_MAX;
2936 discrim->lastseen = now;
2938 for (i=0; i<argc; i++) {
2939 if (i == argc - 1) {
2940 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2943 if (!irccasecmp(argv[i], "limit")) {
2944 discrim->limit = strtoul(argv[++i], NULL, 0);
2945 } else if (!irccasecmp(argv[i], "flags")) {
2946 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
2947 } else if (!irccasecmp(argv[i], "registered")) {
2948 const char *cmp = argv[++i];
2949 if (cmp[0] == '<') {
2950 if (cmp[1] == '=') {
2951 discrim->min_registered = now - ParseInterval(cmp+2);
2953 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
2955 } else if (cmp[0] == '=') {
2956 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
2957 } else if (cmp[0] == '>') {
2958 if (cmp[1] == '=') {
2959 discrim->max_registered = now - ParseInterval(cmp+2);
2961 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
2964 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
2966 } else if (!irccasecmp(argv[i], "seen")) {
2967 discrim->lastseen = now - ParseInterval(argv[++i]);
2968 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
2969 discrim->nickmask = argv[++i];
2970 } else if (!irccasecmp(argv[i], "hostmask")) {
2972 if (!irccasecmp(argv[i], "exact")) {
2973 if (i == argc - 1) {
2974 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2977 discrim->hostmask_type = EXACT;
2978 } else if (!irccasecmp(argv[i], "subset")) {
2979 if (i == argc - 1) {
2980 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2983 discrim->hostmask_type = SUBSET;
2984 } else if (!irccasecmp(argv[i], "superset")) {
2985 if (i == argc - 1) {
2986 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2989 discrim->hostmask_type = SUPERSET;
2990 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
2991 if (i == argc - 1) {
2992 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
2995 discrim->hostmask_type = LASTQUIT;
2998 discrim->hostmask_type = SUPERSET;
3000 discrim->hostmask = argv[++i];
3001 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3002 if (!irccasecmp(argv[++i], "*")) {
3003 discrim->handlemask = 0;
3005 discrim->handlemask = argv[i];
3007 } else if (!irccasecmp(argv[i], "email")) {
3008 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3009 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3011 } else if (!irccasecmp(argv[++i], "*")) {
3012 discrim->emailmask = 0;
3014 discrim->emailmask = argv[i];
3016 } else if (!irccasecmp(argv[i], "access")) {
3017 const char *cmp = argv[++i];
3018 if (cmp[0] == '<') {
3019 if (discrim->min_level == 0) discrim->min_level = 1;
3020 if (cmp[1] == '=') {
3021 discrim->max_level = strtoul(cmp+2, NULL, 0);
3023 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3025 } else if (cmp[0] == '=') {
3026 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3027 } else if (cmp[0] == '>') {
3028 if (cmp[1] == '=') {
3029 discrim->min_level = strtoul(cmp+2, NULL, 0);
3031 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3034 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3037 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3048 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3050 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3051 || (discrim->flags_off & hi->flags)
3052 || (discrim->min_registered > hi->registered)
3053 || (discrim->max_registered < hi->registered)
3054 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3055 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3056 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3057 || (discrim->min_level > hi->opserv_level)
3058 || (discrim->max_level < hi->opserv_level)) {
3061 if (discrim->hostmask) {
3063 for (i=0; i<hi->masks->used; i++) {
3064 const char *mask = hi->masks->list[i];
3065 if ((discrim->hostmask_type == SUBSET)
3066 && (match_ircglobs(discrim->hostmask, mask))) break;
3067 else if ((discrim->hostmask_type == EXACT)
3068 && !irccasecmp(discrim->hostmask, mask)) break;
3069 else if ((discrim->hostmask_type == SUPERSET)
3070 && (match_ircglobs(mask, discrim->hostmask))) break;
3071 else if ((discrim->hostmask_type == LASTQUIT)
3072 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3074 if (i==hi->masks->used) return 0;
3076 if (discrim->nickmask) {
3077 struct nick_info *nick = hi->nicks;
3079 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3082 if (!nick) return 0;
3088 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3090 dict_iterator_t it, next;
3091 unsigned int matched;
3093 for (it = dict_first(nickserv_handle_dict), matched = 0;
3094 it && (matched < discrim->limit);
3096 next = iter_next(it);
3097 if (nickserv_discrim_match(discrim, iter_data(it))) {
3098 dsf(source, iter_data(it));
3106 search_print_func(struct userNode *source, struct handle_info *match)
3108 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3112 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3117 search_unregister_func (struct userNode *source, struct handle_info *match)
3119 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3120 nickserv_unregister_handle(match, source);
3124 nickserv_sort_accounts_by_access(const void *a, const void *b)
3126 const struct handle_info *hi_a = *(const struct handle_info**)a;
3127 const struct handle_info *hi_b = *(const struct handle_info**)b;
3128 if (hi_a->opserv_level != hi_b->opserv_level)
3129 return hi_b->opserv_level - hi_a->opserv_level;
3130 return irccasecmp(hi_a->handle, hi_b->handle);
3134 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3136 struct handle_info_list hil;
3137 struct helpfile_table tbl;
3142 memset(&hil, 0, sizeof(hil));
3143 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3144 struct handle_info *hi = iter_data(it);
3145 if (hi->opserv_level)
3146 handle_info_list_append(&hil, hi);
3148 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3149 tbl.length = hil.used + 1;
3151 tbl.flags = TABLE_NO_FREE;
3152 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3153 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3156 for (ii = 0; ii < hil.used; ) {
3157 ary = malloc(tbl.width * sizeof(ary[0]));
3158 ary[0] = hil.list[ii]->handle;
3159 ary[1] = strtab(hil.list[ii]->opserv_level);
3160 tbl.contents[++ii] = ary;
3162 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3163 reply("MSG_MATCH_COUNT", hil.used);
3164 for (ii = 0; ii < hil.used; ii++)
3165 free(tbl.contents[ii]);
3170 static NICKSERV_FUNC(cmd_search)
3172 struct nickserv_discrim *discrim;
3173 discrim_search_func action;
3174 struct svccmd *subcmd;
3175 unsigned int matches;
3178 NICKSERV_MIN_PARMS(3);
3179 sprintf(buf, "search %s", argv[1]);
3180 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3181 if (!irccasecmp(argv[1], "print"))
3182 action = search_print_func;
3183 else if (!irccasecmp(argv[1], "count"))
3184 action = search_count_func;
3185 else if (!irccasecmp(argv[1], "unregister"))
3186 action = search_unregister_func;
3188 reply("NSMSG_INVALID_ACTION", argv[1]);
3192 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3195 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3199 if (action == search_print_func)
3200 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3201 else if (action == search_count_func)
3202 discrim->limit = INT_MAX;
3204 matches = nickserv_discrim_search(discrim, action, user);
3207 reply("MSG_MATCH_COUNT", matches);
3209 reply("MSG_NO_MATCHES");
3215 static MODCMD_FUNC(cmd_checkpass)
3217 struct handle_info *hi;
3219 NICKSERV_MIN_PARMS(3);
3220 if (!(hi = get_handle_info(argv[1]))) {
3221 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3224 if (checkpass(argv[2], hi->passwd))
3225 reply("CHECKPASS_YES");
3227 reply("CHECKPASS_NO");
3233 nickserv_db_read_handle(const char *handle, dict_t obj)
3236 struct string_list *masks, *slist;
3237 struct handle_info *hi;
3238 struct userNode *authed_users;
3239 struct userData *channels;
3240 unsigned long int id;
3244 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3245 id = str ? strtoul(str, NULL, 0) : 0;
3246 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3248 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3251 if ((hi = get_handle_info(handle))) {
3252 authed_users = hi->users;
3253 channels = hi->channels;
3255 hi->channels = NULL;
3256 dict_remove(nickserv_handle_dict, hi->handle);
3258 authed_users = NULL;
3261 hi = register_handle(handle, str, id);
3263 hi->users = authed_users;
3264 while (authed_users) {
3265 authed_users->handle_info = hi;
3266 authed_users = authed_users->next_authed;
3269 hi->channels = channels;
3270 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3271 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3272 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3273 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3274 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3275 hi->language = language_find(str ? str : "C");
3276 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3277 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3278 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3280 hi->infoline = strdup(str);
3281 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3282 hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
3283 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3284 hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered;
3285 /* We want to read the nicks even if disable_nicks is set. This is so
3286 * that we don't lose the nick data entirely. */
3287 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3289 for (ii=0; ii<slist->used; ii++)
3290 register_nick(slist->list[ii], hi);
3292 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3294 for (ii=0; str[ii]; ii++)
3295 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3297 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3298 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3299 str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING);
3300 hi->announcements = str ? str[0] : '?';
3301 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3302 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3303 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3304 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3305 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3307 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3309 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3310 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3312 nickserv_set_email_addr(hi, str);
3313 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3315 hi->epithet = strdup(str);
3316 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3318 hi->fakehost = strdup(str);
3319 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3321 const char *data, *type, *expires, *cookie_str;
3322 struct handle_cookie *cookie;
3324 cookie = calloc(1, sizeof(*cookie));
3325 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3326 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3327 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3328 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3329 if (!type || !expires || !cookie_str) {
3330 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3333 if (!irccasecmp(type, KEY_ACTIVATION))
3334 cookie->type = ACTIVATION;
3335 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3336 cookie->type = PASSWORD_CHANGE;
3337 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3338 cookie->type = EMAIL_CHANGE;
3339 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3340 cookie->type = ALLOWAUTH;
3342 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3345 cookie->expires = strtoul(expires, NULL, 0);
3346 if (cookie->expires < now)
3349 cookie->data = strdup(data);
3350 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3354 nickserv_bake_cookie(cookie);
3356 nickserv_free_cookie(cookie);
3361 nickserv_saxdb_read(dict_t db) {
3363 struct record_data *rd;
3365 for (it=dict_first(db); it; it=iter_next(it)) {
3367 nickserv_db_read_handle(iter_key(it), rd->d.object);
3372 static NICKSERV_FUNC(cmd_mergedb)
3374 struct timeval start, stop;
3377 NICKSERV_MIN_PARMS(2);
3378 gettimeofday(&start, NULL);
3379 if (!(db = parse_database(argv[1]))) {
3380 reply("NSMSG_DB_UNREADABLE", argv[1]);
3383 nickserv_saxdb_read(db);
3385 gettimeofday(&stop, NULL);
3386 stop.tv_sec -= start.tv_sec;
3387 stop.tv_usec -= start.tv_usec;
3388 if (stop.tv_usec < 0) {
3390 stop.tv_usec += 1000000;
3392 reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000);
3397 expire_handles(UNUSED_ARG(void *data))
3399 dict_iterator_t it, next;
3401 struct handle_info *hi;
3403 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3404 next = iter_next(it);
3406 if ((hi->opserv_level > 0)
3408 || HANDLE_FLAGGED(hi, FROZEN)
3409 || HANDLE_FLAGGED(hi, NODELETE)) {
3412 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3413 if ((now - hi->lastseen) > expiry) {
3414 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3415 nickserv_unregister_handle(hi, NULL);
3419 if (nickserv_conf.handle_expire_frequency)
3420 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3424 nickserv_load_dict(const char *fname)
3428 if (!(file = fopen(fname, "r"))) {
3429 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3432 while (!feof(file)) {
3433 fgets(line, sizeof(line), file);
3436 if (line[strlen(line)-1] == '\n')
3437 line[strlen(line)-1] = 0;
3438 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3441 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3444 static enum reclaim_action
3445 reclaim_action_from_string(const char *str) {
3447 return RECLAIM_NONE;
3448 else if (!irccasecmp(str, "warn"))
3449 return RECLAIM_WARN;
3450 else if (!irccasecmp(str, "svsnick"))
3451 return RECLAIM_SVSNICK;
3452 else if (!irccasecmp(str, "kill"))
3453 return RECLAIM_KILL;
3455 return RECLAIM_NONE;
3459 nickserv_conf_read(void)
3461 dict_t conf_node, child;
3465 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3466 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3469 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3471 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3472 if (nickserv_conf.valid_handle_regex_set)
3473 regfree(&nickserv_conf.valid_handle_regex);
3475 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3476 nickserv_conf.valid_handle_regex_set = !err;
3477 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3479 nickserv_conf.valid_handle_regex_set = 0;
3481 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3482 if (nickserv_conf.valid_nick_regex_set)
3483 regfree(&nickserv_conf.valid_nick_regex);
3485 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3486 nickserv_conf.valid_nick_regex_set = !err;
3487 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3489 nickserv_conf.valid_nick_regex_set = 0;
3491 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
3493 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
3494 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
3495 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
3496 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
3497 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
3498 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
3499 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
3500 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
3501 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
3502 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
3503 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
3504 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
3505 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
3506 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
3507 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
3508 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
3509 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
3510 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
3511 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
3512 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
3513 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
3514 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
3515 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
3516 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
3517 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
3519 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
3520 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
3521 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3523 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3524 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
3525 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
3527 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
3528 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
3529 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
3530 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
3531 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
3532 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
3533 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
3534 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
3535 if (!nickserv_conf.disable_nicks) {
3536 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
3537 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3538 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
3539 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
3540 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
3541 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
3542 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
3543 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
3545 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
3546 for (it=dict_first(child); it; it=iter_next(it)) {
3547 const char *key = iter_key(it), *value;
3551 if (!strncasecmp(key, "uc_", 3))
3552 flag = toupper(key[3]);
3553 else if (!strncasecmp(key, "lc_", 3))
3554 flag = tolower(key[3]);
3558 if ((pos = handle_inverse_flags[flag])) {
3559 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
3560 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
3563 if (nickserv_conf.weak_password_dict)
3564 dict_delete(nickserv_conf.weak_password_dict);
3565 nickserv_conf.weak_password_dict = dict_new();
3566 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
3567 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
3568 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
3569 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
3571 nickserv_load_dict(str);
3572 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
3573 if (nickserv && str)
3574 NickChange(nickserv, str, 0);
3575 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
3576 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
3577 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
3578 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
3579 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
3580 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
3581 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
3582 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
3583 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
3584 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
3585 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
3586 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
3587 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
3588 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
3589 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
3590 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
3591 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
3592 nickserv_conf.titlehost_suffix = str ? str : "example.net";
3593 str = conf_get_data("server/network", RECDB_QSTRING);
3594 nickserv_conf.network_name = str ? str : "some IRC network";
3595 if (!nickserv_conf.auth_policer_params) {
3596 nickserv_conf.auth_policer_params = policer_params_new();
3597 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
3598 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
3600 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
3601 for (it=dict_first(child); it; it=iter_next(it))
3602 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
3606 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
3608 char newnick[NICKLEN+1];
3617 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3619 case RECLAIM_SVSNICK:
3621 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
3622 } while (GetUserH(newnick));
3623 irc_svsnick(nickserv, user, newnick);
3626 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
3627 DelUser(user, nickserv, 1, msg);
3633 nickserv_reclaim_p(void *data) {
3634 struct userNode *user = data;
3635 struct nick_info *ni = get_nick_info(user->nick);
3637 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3641 check_user_nick(struct userNode *user) {
3642 struct nick_info *ni;
3643 user->modes &= ~FLAGS_REGNICK;
3644 if (!(ni = get_nick_info(user->nick)))
3646 if (user->handle_info == ni->owner) {
3647 user->modes |= FLAGS_REGNICK;
3651 if (nickserv_conf.warn_nick_owned)
3652 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
3653 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
3655 if (nickserv_conf.auto_reclaim_delay)
3656 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
3658 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
3663 handle_new_user(struct userNode *user)
3665 return check_user_nick(user);
3669 handle_account(struct userNode *user, const char *stamp)
3671 struct handle_info *hi;
3673 #ifdef WITH_PROTOCOL_P10
3674 hi = dict_find(nickserv_handle_dict, stamp, NULL);
3676 hi = dict_find(nickserv_id_dict, stamp, NULL);
3680 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
3683 set_user_handle_info(user, hi, 0);
3685 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp);
3690 handle_nick_change(struct userNode *user, const char *old_nick)
3692 struct handle_info *hi;
3694 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
3695 dict_remove(nickserv_allow_auth_dict, old_nick);
3696 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
3698 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3699 check_user_nick(user);
3703 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
3705 dict_remove(nickserv_allow_auth_dict, user->nick);
3706 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
3707 set_user_handle_info(user, NULL, 0);
3710 static struct modcmd *
3711 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
3713 if (min_level > 0) {
3715 sprintf(buf, "%u", min_level);
3716 if (must_be_qualified) {
3717 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
3719 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
3721 } else if (min_level == 0) {
3722 if (must_be_qualified) {
3723 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3725 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
3728 if (must_be_qualified) {
3729 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
3731 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
3737 nickserv_db_cleanup(void)
3739 unreg_del_user_func(nickserv_remove_user);
3740 userList_clean(&curr_helpers);
3741 policer_params_delete(nickserv_conf.auth_policer_params);
3742 dict_delete(nickserv_handle_dict);
3743 dict_delete(nickserv_nick_dict);
3744 dict_delete(nickserv_opt_dict);
3745 dict_delete(nickserv_allow_auth_dict);
3746 dict_delete(nickserv_email_dict);
3747 dict_delete(nickserv_id_dict);
3748 dict_delete(nickserv_conf.weak_password_dict);
3749 free(auth_func_list);
3750 free(unreg_func_list);
3752 free(allowauth_func_list);
3753 free(handle_merge_func_list);
3754 free(failpw_func_list);
3755 if (nickserv_conf.valid_handle_regex_set)
3756 regfree(&nickserv_conf.valid_handle_regex);
3757 if (nickserv_conf.valid_nick_regex_set)
3758 regfree(&nickserv_conf.valid_nick_regex);
3762 init_nickserv(const char *nick)
3765 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
3766 reg_new_user_func(handle_new_user);
3767 reg_nick_change_func(handle_nick_change);
3768 reg_del_user_func(nickserv_remove_user);
3769 reg_account_func(handle_account);
3771 /* set up handle_inverse_flags */
3772 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
3773 for (i=0; handle_flags[i]; i++) {
3774 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
3775 flag_access_levels[i] = 0;
3778 conf_register_reload(nickserv_conf_read);
3779 nickserv_opt_dict = dict_new();
3780 nickserv_email_dict = dict_new();
3781 dict_set_free_keys(nickserv_email_dict, free);
3782 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
3784 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
3785 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
3786 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
3787 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
3788 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
3789 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
3790 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
3791 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
3792 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
3793 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
3794 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
3795 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
3796 nickserv_define_func("SET", cmd_set, -1, 1, 0);
3797 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
3798 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
3799 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
3800 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
3801 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
3802 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
3803 if (!nickserv_conf.disable_nicks) {
3804 /* nick management commands */
3805 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
3806 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
3807 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
3808 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
3809 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
3810 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
3812 if (nickserv_conf.email_enabled) {
3813 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
3814 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
3815 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
3816 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
3817 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
3819 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
3820 /* miscellaneous commands */
3821 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
3822 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
3823 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
3824 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
3825 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
3827 dict_insert(nickserv_opt_dict, "INFO", opt_info);
3828 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
3829 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
3830 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
3831 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
3832 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
3833 dict_insert(nickserv_opt_dict, "PASS", opt_password);
3834 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
3835 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
3836 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
3837 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
3838 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
3839 if (nickserv_conf.titlehost_suffix) {
3840 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
3841 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
3843 dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements);
3844 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
3845 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
3847 nickserv_handle_dict = dict_new();
3848 dict_set_free_keys(nickserv_handle_dict, free);
3849 dict_set_free_data(nickserv_handle_dict, free_handle_info);
3851 nickserv_id_dict = dict_new();
3852 dict_set_free_keys(nickserv_id_dict, free);
3854 nickserv_nick_dict = dict_new();
3855 dict_set_free_data(nickserv_nick_dict, free);
3857 nickserv_allow_auth_dict = dict_new();
3859 userList_init(&curr_helpers);
3862 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
3863 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
3864 nickserv_service = service_register(nickserv);
3866 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
3867 reg_exit_func(nickserv_db_cleanup);
3868 if(nickserv_conf.handle_expire_frequency)
3869 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3870 message_register_table(msgtab);