1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2008 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_SET_FAKEIDENT_LEVEL "set_fakeident_level"
55 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
56 #define KEY_FLAG_LEVELS "flag_levels"
57 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
58 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
59 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
60 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
61 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
62 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
63 #define KEY_DICT_FILE "dict_file"
64 #define KEY_NICK "nick"
65 #define KEY_LANGUAGE "language"
66 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
67 #define KEY_AUTOGAG_DURATION "autogag_duration"
68 #define KEY_AUTH_POLICER "auth_policer"
69 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
70 #define KEY_EMAIL_ENABLED "email_enabled"
71 #define KEY_EMAIL_REQUIRED "email_required"
72 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
73 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
74 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
75 #define KEY_OUNREGISTER_INACTIVE "ounregister_inactive"
76 #define KEY_OUNREGISTER_FLAGS "ounregister_flags"
77 #define KEY_HANDLE_TS_MODE "account_timestamp_mode"
80 #define KEY_PASSWD "passwd"
81 #define KEY_NICKS "nicks"
82 #define KEY_MASKS "masks"
83 #define KEY_OPSERV_LEVEL "opserv_level"
84 #define KEY_FLAGS "flags"
85 #define KEY_REGISTER_ON "register"
86 #define KEY_LAST_SEEN "lastseen"
87 #define KEY_INFO "info"
88 #define KEY_WEBSITE "website"
89 #define KEY_USERLIST_STYLE "user_style"
90 #define KEY_SCREEN_WIDTH "screen_width"
91 #define KEY_LAST_AUTHED_HOST "last_authed_host"
92 #define KEY_LAST_QUIT_HOST "last_quit_host"
93 #define KEY_EMAIL_ADDR "email_addr"
94 #define KEY_COOKIE "cookie"
95 #define KEY_COOKIE_DATA "data"
96 #define KEY_COOKIE_TYPE "type"
97 #define KEY_COOKIE_EXPIRES "expires"
98 #define KEY_ACTIVATION "activation"
99 #define KEY_PASSWORD_CHANGE "password change"
100 #define KEY_EMAIL_CHANGE "email change"
101 #define KEY_ALLOWAUTH "allowauth"
102 #define KEY_EPITHET "epithet"
103 #define KEY_TABLE_WIDTH "table_width"
104 #define KEY_MAXLOGINS "maxlogins"
105 #define KEY_FAKEHOST "fakehost"
106 #define KEY_FAKEIDENT "fakeident"
107 #define KEY_NOTES "notes"
108 #define KEY_NOTE_EXPIRES "expires"
109 #define KEY_NOTE_SET "set"
110 #define KEY_NOTE_SETTER "setter"
111 #define KEY_NOTE_NOTE "note"
112 #define KEY_KARMA "karma"
114 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
116 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
117 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
118 typedef OPTION_FUNC(option_func_t);
120 DEFINE_LIST(handle_info_list, struct handle_info*)
122 #define NICKSERV_MIN_PARMS(N) do { \
124 reply("MSG_MISSING_PARAMS", argv[0]); \
125 svccmd_send_help(user, nickserv, cmd); \
129 struct userNode *nickserv;
130 struct userList curr_helpers;
131 const char *handle_flags = HANDLE_FLAGS;
133 static struct module *nickserv_module;
134 static struct service *nickserv_service;
135 static struct log_type *NS_LOG;
136 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
137 static dict_t nickserv_id_dict; /* contains struct handle_info* */
138 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
139 static dict_t nickserv_opt_dict; /* contains option_func_t* */
140 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
141 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
142 static char handle_inverse_flags[256];
143 static unsigned int flag_access_levels[32];
144 static const struct message_entry msgtab[] = {
145 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
146 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
147 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
148 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
149 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
150 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
151 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
152 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
153 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
154 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
155 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
156 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
157 { "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." },
158 { "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." },
159 { "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." },
160 { "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." },
161 { "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." },
162 { "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." },
163 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
164 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
165 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
166 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
167 { "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." },
168 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
169 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
170 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
171 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
172 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
173 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
174 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
175 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
176 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
177 { "NSMSG_ATE_COOKIE_FOREIGN", "I ate the cookie for account $b%s$b. It may now have another." },
178 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
179 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
180 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
181 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
182 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
183 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
184 { "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)" },
185 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
186 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
187 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
188 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
189 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
190 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
191 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
192 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
193 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
194 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
195 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
196 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
197 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
198 { "NSMSG_TITLE_TRUNCATED_RENAME", "That account name combined with the user's title would result in a truncated host; please choose a shorter account name." },
199 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
200 { "NSMSG_FAKEIDENT_INVALID", "Fake idents must be shorter than %d characters." },
201 { "NSMSG_FAKEMASK_INVALID", "Fake ident@hosts must be shorter than %d characters." },
202 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
203 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
204 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
205 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
206 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
207 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
208 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
209 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
210 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
211 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
212 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
213 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
214 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
215 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
216 { "NSMSG_HANDLEINFO_WEBSITE", " Website: %s" },
217 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
218 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
219 { "NSMSG_HANDLEINFO_FAKEIDENT", " Fake ident: %s" },
220 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
221 { "NSMSG_HANDLEINFO_FAKEIDENTHOST", " Fake host: %s@%s" },
222 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
223 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
224 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
225 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
226 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
227 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
228 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
229 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
230 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
231 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
232 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
233 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
234 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
235 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
236 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
237 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
238 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
239 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
240 { "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)." },
241 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
242 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
243 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
244 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
245 { "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." },
246 { "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." },
247 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
248 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
249 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
250 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
251 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
252 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
253 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
254 { "NSMSG_PASS_SUCCESS", "Password changed." },
255 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
256 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
257 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
258 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
259 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
260 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
261 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
262 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
263 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
264 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
265 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
266 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
267 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
268 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
269 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
270 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
271 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
272 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
273 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
274 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
275 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
276 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
277 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
278 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
279 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
280 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
281 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
282 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
283 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
284 { "NSMSG_NO_ACCESS", "Access denied." },
285 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
286 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
287 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
288 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
289 { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
290 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
291 { "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." },
292 { "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." },
293 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
294 { "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." },
295 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
296 { "NSMSG_SEARCH_MATCH", "Match: %s" },
297 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
298 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
299 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
300 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
301 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
302 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
303 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
304 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
305 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
306 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
307 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
308 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
309 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
310 { "NSMSG_SET_WEBSITE", "$bWEBSITE: $b%s" },
311 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
312 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
313 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
314 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
315 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
316 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
317 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
318 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
319 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
320 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
321 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
322 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
323 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
324 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
325 { "NSMSG_SET_FAKEIDENT", "$bFAKEIDENT: $b%s" },
326 { "NSMSG_SET_FAKEIDENTHOST", "$bFAKEHOST: $b%s@%s" },
327 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
328 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
329 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
330 { "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." },
331 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
332 { "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." },
333 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
334 { "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." },
335 { "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." },
336 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
337 { "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." },
338 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
339 { "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." },
340 { "CHECKPASS_YES", "Yes." },
341 { "CHECKPASS_NO", "No." },
342 { "CHECKEMAIL_NOT_SET", "No email set." },
343 { "CHECKEMAIL_YES", "Yes." },
344 { "CHECKEMAIL_NO", "No." },
348 enum reclaim_action {
354 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
355 static void nickserv_reclaim_p(void *data);
356 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
358 enum handle_ts_mode {
364 unsigned int disable_nicks : 1;
365 unsigned int valid_handle_regex_set : 1;
366 unsigned int valid_nick_regex_set : 1;
367 unsigned int autogag_enabled : 1;
368 unsigned int email_enabled : 1;
369 unsigned int email_required : 1;
370 unsigned int default_hostmask : 1;
371 unsigned int warn_nick_owned : 1;
372 unsigned int warn_clone_auth : 1;
373 unsigned long nicks_per_handle;
374 unsigned long password_min_length;
375 unsigned long password_min_digits;
376 unsigned long password_min_upper;
377 unsigned long password_min_lower;
378 unsigned long db_backup_frequency;
379 unsigned long handle_expire_frequency;
380 unsigned long autogag_duration;
381 unsigned long email_visible_level;
382 unsigned long cookie_timeout;
383 unsigned long handle_expire_delay;
384 unsigned long nochan_handle_expire_delay;
385 unsigned long modoper_level;
386 unsigned long set_epithet_level;
387 unsigned long set_title_level;
388 unsigned long set_fakehost_level;
389 unsigned long set_fakeident_level;
390 unsigned long handles_per_email;
391 unsigned long email_search_level;
392 const char *network_name;
393 regex_t valid_handle_regex;
394 regex_t valid_nick_regex;
395 dict_t weak_password_dict;
396 struct policer_params *auth_policer_params;
397 enum reclaim_action reclaim_action;
398 enum reclaim_action auto_reclaim_action;
399 enum handle_ts_mode handle_ts_mode;
400 unsigned long auto_reclaim_delay;
401 unsigned char default_maxlogins;
402 unsigned char hard_maxlogins;
403 unsigned long ounregister_inactive;
404 unsigned long ounregister_flags;
407 const char *titlehost_suffix = NULL;
409 /* We have 2^32 unique account IDs to use. */
410 unsigned long int highest_id = 0;
412 #define WALK_NOTES(HANDLE, PREV, NOTE) \
413 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
414 if (NOTE->expires && NOTE->expires < now) { \
415 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
417 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
421 canonicalize_hostmask(char *mask)
423 char *out = mask, *temp;
424 if ((temp = strchr(mask, '!'))) {
426 while (*temp) *out++ = *temp++;
432 static struct handle_info *
433 register_handle(const char *handle, const char *passwd, unsigned long id)
435 struct handle_info *hi;
437 char id_base64[IDLEN + 1];
440 /* Assign a unique account ID to the account; note that 0 is
441 an invalid account ID. 1 is therefore the first account ID. */
443 id = 1 + highest_id++;
445 /* Note: highest_id is and must always be the highest ID. */
446 if (id > highest_id) {
450 inttobase64(id_base64, id, IDLEN);
452 /* Make sure an account with the same ID doesn't exist. If a
453 duplicate is found, log some details and assign a new one.
454 This should be impossible, but it never hurts to expect it. */
455 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
456 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
461 hi = calloc(1, sizeof(*hi));
462 hi->userlist_style = HI_DEFAULT_STYLE;
463 hi->handle = strdup(handle);
464 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
466 dict_insert(nickserv_handle_dict, hi->handle, hi);
470 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
476 register_nick(const char *nick, struct handle_info *owner)
478 struct nick_info *ni;
479 ni = malloc(sizeof(struct nick_info));
480 safestrncpy(ni->nick, nick, sizeof(ni->nick));
482 ni->next = owner->nicks;
484 dict_insert(nickserv_nick_dict, ni->nick, ni);
488 delete_nick(struct nick_info *ni)
490 struct nick_info *last, *next;
491 struct userNode *user;
492 /* Check to see if we should mark a user as unregistered. */
493 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
494 user->modes &= ~FLAGS_REGNICK;
497 /* Remove ni from the nick_info linked list. */
498 if (ni == ni->owner->nicks) {
499 ni->owner->nicks = ni->next;
501 last = ni->owner->nicks;
507 last->next = next->next;
509 dict_remove(nickserv_nick_dict, ni->nick);
512 static unreg_func_t *unreg_func_list;
513 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
516 reg_unreg_func(unreg_func_t func)
518 if (unreg_func_used == unreg_func_size) {
519 if (unreg_func_size) {
520 unreg_func_size <<= 1;
521 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
524 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
527 unreg_func_list[unreg_func_used++] = func;
531 nickserv_free_cookie(void *data)
533 struct handle_cookie *cookie = data;
534 if (cookie->hi) cookie->hi->cookie = NULL;
535 if (cookie->data) free(cookie->data);
540 free_handle_info(void *vhi)
542 struct handle_info *hi = vhi;
545 inttobase64(id, hi->id, IDLEN);
546 dict_remove(nickserv_id_dict, id);
548 free_string_list(hi->masks);
552 delete_nick(hi->nicks);
559 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
560 nickserv_free_cookie(hi->cookie);
563 struct handle_note *note = hi->notes;
564 hi->notes = note->next;
567 if (hi->email_addr) {
568 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
569 handle_info_list_remove(hil, hi);
571 dict_remove(nickserv_email_dict, hi->email_addr);
576 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
579 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
583 for (n=0; n<unreg_func_used; n++)
584 unreg_func_list[n](notify, hi);
586 set_user_handle_info(hi->users, NULL, 0);
588 if (nickserv_conf.disable_nicks)
589 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
591 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
593 dict_remove(nickserv_handle_dict, hi->handle);
597 get_handle_info(const char *handle)
599 return dict_find(nickserv_handle_dict, handle, 0);
603 get_nick_info(const char *nick)
605 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
609 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
614 for (nn=0; nn<channel->members.used; ++nn) {
615 mn = channel->members.list[nn];
616 if ((mn->user != except) && (mn->user->handle_info == handle))
623 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
624 if (!user->handle_info) {
626 send_message(user, bot, "MSG_AUTHENTICATE");
630 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
632 send_message(user, bot, "NSMSG_NO_ACCESS");
636 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
638 send_message(user, bot, "MSG_OPER_SUSPENDED");
642 if (user->handle_info->opserv_level < min_level) {
644 send_message(user, bot, "NSMSG_NO_ACCESS");
652 is_valid_handle(const char *handle)
654 struct userNode *user;
655 /* cant register a juped nick/service nick as handle, to prevent confusion */
656 user = GetUserH(handle);
657 if (user && IsLocal(user))
659 /* check against maximum length */
660 if (strlen(handle) > NICKSERV_HANDLE_LEN)
662 /* for consistency, only allow account names that could be nicks */
663 if (!is_valid_nick(handle))
665 /* disallow account names that look like bad words */
666 if (opserv_bad_channel(handle))
668 /* test either regex or containing all valid chars */
669 if (nickserv_conf.valid_handle_regex_set) {
670 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
673 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
674 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
678 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
683 is_registerable_nick(const char *nick)
685 /* make sure it could be used as an account name */
686 if (!is_valid_handle(nick))
689 if (strlen(nick) > NICKLEN)
691 /* test either regex or as valid handle */
692 if (nickserv_conf.valid_nick_regex_set) {
693 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
696 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
697 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
705 is_valid_email_addr(const char *email)
707 return strchr(email, '@') != NULL;
711 visible_email_addr(struct userNode *user, struct handle_info *hi)
713 if (hi->email_addr) {
714 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
715 return hi->email_addr;
725 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
727 struct handle_info *hi;
728 struct userNode *target;
732 if (!(hi = get_handle_info(++name))) {
733 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
738 if (!(target = GetUserH(name))) {
739 send_message(user, service, "MSG_NICK_UNKNOWN", name);
742 if (IsLocal(target)) {
743 if (IsService(target))
744 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
746 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
749 if (!(hi = target->handle_info)) {
750 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
758 oper_outranks(struct userNode *user, struct handle_info *hi) {
759 if (user->handle_info->opserv_level > hi->opserv_level)
761 if (user->handle_info->opserv_level == hi->opserv_level) {
762 if ((user->handle_info->opserv_level == 1000)
763 || (user->handle_info == hi)
764 || ((user->handle_info->opserv_level == 0)
765 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
766 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
770 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
774 static struct handle_info *
775 get_victim_oper(struct userNode *user, const char *target)
777 struct handle_info *hi;
778 if (!(hi = smart_get_handle_info(nickserv, user, target)))
780 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
781 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
784 return oper_outranks(user, hi) ? hi : NULL;
788 valid_user_for(struct userNode *user, struct handle_info *hi)
792 /* If no hostmasks on the account, allow it. */
793 if (!hi->masks->used)
795 /* If any hostmask matches, allow it. */
796 for (ii=0; ii<hi->masks->used; ii++)
797 if (user_matches_glob(user, hi->masks->list[ii], 0))
799 /* If they are allowauthed to this account, allow it (removing the aa). */
800 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
801 dict_remove(nickserv_allow_auth_dict, user->nick);
804 /* The user is not allowed to use this account. */
809 is_secure_password(const char *handle, const char *pass, struct userNode *user)
812 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
816 if (len < nickserv_conf.password_min_length) {
818 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
821 if (!irccasecmp(pass, handle)) {
823 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
826 dict_find(nickserv_conf.weak_password_dict, pass, &p);
829 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
832 for (i=0; i<len; i++) {
833 if (isdigit(pass[i]))
835 if (isupper(pass[i]))
837 if (islower(pass[i]))
840 if ((cnt_lower < nickserv_conf.password_min_lower)
841 || (cnt_upper < nickserv_conf.password_min_upper)
842 || (cnt_digits < nickserv_conf.password_min_digits)) {
844 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
850 static auth_func_t *auth_func_list;
851 static unsigned int auth_func_size = 0, auth_func_used = 0;
854 reg_auth_func(auth_func_t func)
856 if (auth_func_used == auth_func_size) {
857 if (auth_func_size) {
858 auth_func_size <<= 1;
859 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
862 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
865 auth_func_list[auth_func_used++] = func;
868 static handle_rename_func_t *rf_list;
869 static unsigned int rf_list_size, rf_list_used;
872 reg_handle_rename_func(handle_rename_func_t func)
874 if (rf_list_used == rf_list_size) {
877 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
880 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
883 rf_list[rf_list_used++] = func;
887 generate_fakehost(struct handle_info *handle)
889 extern const char *hidden_host_suffix;
890 static char buffer[HOSTLEN+1];
892 if (!handle->fakehost) {
893 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
895 } else if (handle->fakehost[0] == '.') {
896 /* A leading dot indicates the stored value is actually a title. */
897 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, titlehost_suffix);
899 } else if (handle->fakehost[0] == '$') {
900 /* A leading $ indicates the stored value begins with the user handle. */
901 snprintf(buffer, sizeof(buffer), "%s%s", handle->handle, handle->fakehost+1);
904 return handle->fakehost;
908 generate_fakeident(struct handle_info *handle, struct userNode *user)
910 static char buffer[USERLEN+1];
912 if (!handle->fakeident) {
915 safestrncpy(buffer, user->ident, sizeof(buffer));
918 return handle->fakeident;
922 apply_fakehost(struct handle_info *handle, struct userNode *user)
924 struct userNode *target;
925 char *fakehost, *fakeident;
930 fakehost = generate_fakehost(handle);
933 fakeident = generate_fakeident(handle, user);
934 assign_fakehost(user, fakehost, fakeident, 0, 1);
938 for (target = handle->users; target; target = target->next_authed) {
939 fakeident = generate_fakeident(handle, target);
940 assign_fakehost(target, fakehost, fakeident, 0, 1);
945 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
948 struct handle_info *old_info;
950 /* This can happen if somebody uses COOKIE while authed, or if
951 * they re-auth to their current handle (which is silly, but users
953 if (user->handle_info == hi)
956 if (user->handle_info) {
957 struct userNode *other;
960 userList_remove(&curr_helpers, user);
962 /* remove from next_authed linked list */
963 if (user->handle_info->users == user) {
964 user->handle_info->users = user->next_authed;
965 } else if (user->handle_info->users != NULL) {
966 for (other = user->handle_info->users;
967 other->next_authed != user;
968 other = other->next_authed) ;
969 other->next_authed = user->next_authed;
971 /* No users authed to the account - can happen if they get
972 * killed for authing. */
974 /* if nobody left on old handle, and they're not an oper, remove !god */
975 if (!user->handle_info->users && !user->handle_info->opserv_level)
976 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
977 /* record them as being last seen at this time */
978 user->handle_info->lastseen = now;
979 /* and record their hostmask */
980 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
982 old_info = user->handle_info;
983 user->handle_info = hi;
984 if (hi && !hi->users && !hi->opserv_level)
985 HANDLE_CLEAR_FLAG(hi, HELPING);
986 for (n=0; n<auth_func_used; n++) {
987 auth_func_list[n](user, old_info);
992 struct nick_info *ni;
994 HANDLE_CLEAR_FLAG(hi, FROZEN);
995 if (nickserv_conf.warn_clone_auth) {
996 struct userNode *other;
997 for (other = hi->users; other; other = other->next_authed)
998 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
1000 user->next_authed = hi->users;
1003 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
1004 userList_append(&curr_helpers, user);
1006 if (hi->fakehost || hi->fakeident || old_info)
1007 apply_fakehost(hi, user);
1010 if (!nickserv_conf.disable_nicks) {
1011 struct nick_info *ni2;
1012 for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
1013 if (!irccasecmp(user->nick, ni2->nick)) {
1014 user->modes |= FLAGS_REGNICK;
1019 StampUser(user, hi->handle, hi->registered, hi->id);
1022 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
1023 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
1025 /* We cannot clear the user's account ID, unfortunately. */
1026 user->next_authed = NULL;
1030 static struct handle_info*
1031 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
1033 struct handle_info *hi;
1034 struct nick_info *ni;
1035 char crypted[MD5_CRYPT_LENGTH];
1037 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
1038 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
1042 if (!is_secure_password(handle, passwd, user))
1045 cryptpass(passwd, crypted);
1046 hi = register_handle(handle, crypted, 0);
1047 hi->masks = alloc_string_list(1);
1049 hi->language = lang_C;
1050 hi->registered = now;
1052 hi->flags = HI_DEFAULT_FLAGS;
1053 if (settee && !no_auth)
1054 set_user_handle_info(settee, hi, 1);
1057 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1058 else if (nickserv_conf.disable_nicks)
1059 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1060 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1061 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1063 register_nick(user->nick, hi);
1064 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1066 if (settee && (user != settee))
1067 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1072 nickserv_bake_cookie(struct handle_cookie *cookie)
1074 cookie->hi->cookie = cookie;
1075 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1079 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1081 struct handle_cookie *cookie;
1082 char subject[128], body[4096], *misc;
1083 const char *netname, *fmt;
1087 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1091 cookie = calloc(1, sizeof(*cookie));
1093 cookie->type = type;
1094 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1095 cookie->expires = now + nickserv_conf.cookie_timeout;
1096 inttobase64(cookie->cookie, rand(), 5);
1097 inttobase64(cookie->cookie+5, rand(), 5);
1099 netname = nickserv_conf.network_name;
1102 switch (cookie->type) {
1104 hi->passwd[0] = 0; /* invalidate password */
1105 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1106 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1107 snprintf(subject, sizeof(subject), fmt, netname);
1108 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1109 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1112 case PASSWORD_CHANGE:
1113 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1114 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1115 snprintf(subject, sizeof(subject), fmt, netname);
1116 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1117 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1120 misc = hi->email_addr;
1121 hi->email_addr = cookie->data;
1123 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1124 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1125 snprintf(subject, sizeof(subject), fmt, netname);
1126 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1127 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1128 mail_send(nickserv, hi, subject, body, 1);
1129 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1130 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1132 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1133 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1134 snprintf(subject, sizeof(subject), fmt, netname);
1135 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1136 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1137 mail_send(nickserv, hi, subject, body, 1);
1140 hi->email_addr = misc;
1143 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1144 snprintf(subject, sizeof(subject), fmt, netname);
1145 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1146 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1147 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1150 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1154 mail_send(nickserv, hi, subject, body, first_time);
1155 nickserv_bake_cookie(cookie);
1159 nickserv_eat_cookie(struct handle_cookie *cookie)
1161 cookie->hi->cookie = NULL;
1162 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1163 nickserv_free_cookie(cookie);
1167 nickserv_free_email_addr(void *data)
1169 handle_info_list_clean(data);
1174 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1176 struct handle_info_list *hil;
1177 /* Remove from old handle_info_list ... */
1178 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1179 handle_info_list_remove(hil, hi);
1180 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1181 hi->email_addr = NULL;
1183 /* Add to the new list.. */
1184 if (new_email_addr) {
1185 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1186 hil = calloc(1, sizeof(*hil));
1187 hil->tag = strdup(new_email_addr);
1188 handle_info_list_init(hil);
1189 dict_insert(nickserv_email_dict, hil->tag, hil);
1191 handle_info_list_append(hil, hi);
1192 hi->email_addr = hil->tag;
1196 static NICKSERV_FUNC(cmd_register)
1199 struct handle_info *hi;
1200 const char *email_addr, *password;
1203 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1204 /* Require the first handle registered to belong to someone +o. */
1205 reply("NSMSG_REQUIRE_OPER");
1209 if (user->handle_info) {
1210 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1214 if (IsRegistering(user)) {
1215 reply("NSMSG_ALREADY_REGISTERING");
1219 if (IsStamped(user)) {
1220 /* Unauthenticated users might still have been stamped
1221 previously and could therefore have a hidden host;
1222 do not allow them to register a new account. */
1223 reply("NSMSG_STAMPED_REGISTER");
1227 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1229 if (!is_valid_handle(argv[1])) {
1230 reply("NSMSG_BAD_HANDLE", argv[1]);
1234 if ((argc >= 4) && nickserv_conf.email_enabled) {
1235 struct handle_info_list *hil;
1238 /* Remember email address. */
1239 email_addr = argv[3];
1241 /* Check that the email address looks valid.. */
1242 if (!is_valid_email_addr(email_addr)) {
1243 reply("NSMSG_BAD_EMAIL_ADDR");
1247 /* .. and that we are allowed to send to it. */
1248 if ((str = mail_prohibited_address(email_addr))) {
1249 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1253 /* If we do email verify, make sure we don't spam the address. */
1254 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1256 for (nn=0; nn<hil->used; nn++) {
1257 if (hil->list[nn]->cookie) {
1258 reply("NSMSG_EMAIL_UNACTIVATED");
1262 if (hil->used >= nickserv_conf.handles_per_email) {
1263 reply("NSMSG_EMAIL_OVERUSED");
1276 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1278 /* Add any masks they should get. */
1279 if (nickserv_conf.default_hostmask) {
1280 string_list_append(hi->masks, strdup("*@*"));
1282 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1283 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1284 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1287 /* If they're the first to register, give them level 1000. */
1288 if (dict_size(nickserv_handle_dict) == 1) {
1289 hi->opserv_level = 1000;
1290 reply("NSMSG_ROOT_HANDLE", argv[1]);
1293 /* Set their email address. */
1295 nickserv_set_email_addr(hi, email_addr);
1297 /* If they need to do email verification, tell them. */
1299 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1301 /* Set registering flag.. */
1302 user->modes |= FLAGS_REGISTERING;
1307 static NICKSERV_FUNC(cmd_oregister)
1310 struct userNode *settee;
1311 struct handle_info *hi;
1312 const char *pass, *email;
1314 NICKSERV_MIN_PARMS(3);
1319 if (!is_valid_handle(argv[1])) {
1320 reply("NSMSG_BAD_HANDLE", argv[1]);
1324 if (argc < 5 || !nickserv_conf.email_enabled) {
1329 if (!is_valid_email_addr(email)) {
1330 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
1333 if ((str = mail_prohibited_address(email))) {
1334 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", email, str);
1339 if (argc < 4 || !strcmp(argv[3], "*")) {
1342 } else if (strchr(argv[3], '@')) {
1343 mask = canonicalize_hostmask(strdup(argv[3]));
1345 settee = GetUserH(argv[4]);
1347 reply("MSG_NICK_UNKNOWN", argv[4]);
1354 } else if ((settee = GetUserH(argv[3]))) {
1355 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1357 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1360 if (settee && settee->handle_info) {
1361 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1365 if (!(hi = nickserv_register(user, settee, argv[1], pass, 0))) {
1370 string_list_append(hi->masks, mask);
1372 nickserv_set_email_addr(hi, email);
1376 static NICKSERV_FUNC(cmd_handleinfo)
1379 unsigned int i, pos=0, herelen;
1380 struct userNode *target, *next_un;
1381 struct handle_info *hi;
1382 const char *nsmsg_none;
1386 if (!(hi = user->handle_info)) {
1387 reply("NSMSG_MUST_AUTH");
1390 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1394 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1395 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1396 feh = hi->registered;
1397 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1400 intervalString(buff, now - hi->lastseen, user->handle_info);
1401 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1403 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1406 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1407 if (user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1408 reply("NSMSG_HANDLEINFO_WEBSITE", (hi->website ? hi->website : nsmsg_none));
1409 if (HANDLE_FLAGGED(hi, FROZEN))
1410 reply("NSMSG_HANDLEINFO_VACATION");
1412 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1413 struct do_not_register *dnr;
1414 if ((dnr = chanserv_is_dnr(NULL, hi)))
1415 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1416 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1418 } else if (hi != user->handle_info)
1422 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1424 if (nickserv_conf.email_enabled)
1425 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1429 switch (hi->cookie->type) {
1430 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1431 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1432 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1433 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1434 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1439 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1440 reply("NSMSG_HANDLEINFO_ID", hi->id);
1442 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1444 reply("NSMSG_HANDLEINFO_NO_NOTES");
1446 struct handle_note *prev, *note;
1448 WALK_NOTES(hi, prev, note) {
1449 char set_time[INTERVALLEN];
1450 intervalString(set_time, now - note->set, user->handle_info);
1451 if (note->expires) {
1452 char exp_time[INTERVALLEN];
1453 intervalString(exp_time, note->expires - now, user->handle_info);
1454 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1456 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1463 unsigned long flen = 1;
1464 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1466 for (i=0, flen=1; handle_flags[i]; i++)
1467 if (hi->flags & 1 << i)
1468 flags[flen++] = handle_flags[i];
1470 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1472 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1475 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1476 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1477 || (hi->opserv_level > 0)) {
1478 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1481 if (hi->fakeident && hi->fakehost)
1482 reply("NSMSG_HANDLEINFO_FAKEIDENTHOST", hi->fakeident, hi->fakehost);
1483 else if (hi->fakeident)
1484 reply("NSMSG_HANDLEINFO_FAKEIDENT", hi->fakeident);
1485 else if (hi->fakehost)
1486 reply("NSMSG_HANDLEINFO_FAKEHOST", hi->fakehost);
1488 if (hi->last_quit_host[0])
1489 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1491 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1493 if (nickserv_conf.disable_nicks) {
1494 /* nicks disabled; don't show anything about registered nicks */
1495 } else if (hi->nicks) {
1496 struct nick_info *ni, *next_ni;
1497 for (ni = hi->nicks; ni; ni = next_ni) {
1498 herelen = strlen(ni->nick);
1499 if (pos + herelen + 1 > ArrayLength(buff)) {
1501 goto print_nicks_buff;
1505 memcpy(buff+pos, ni->nick, herelen);
1506 pos += herelen; buff[pos++] = ' ';
1510 reply("NSMSG_HANDLEINFO_NICKS", buff);
1515 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1518 if (hi->masks->used) {
1519 for (i=0; i < hi->masks->used; i++) {
1520 herelen = strlen(hi->masks->list[i]);
1521 if (pos + herelen + 1 > ArrayLength(buff)) {
1523 goto print_mask_buff;
1525 memcpy(buff+pos, hi->masks->list[i], herelen);
1526 pos += herelen; buff[pos++] = ' ';
1527 if (i+1 == hi->masks->used) {
1530 reply("NSMSG_HANDLEINFO_MASKS", buff);
1535 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1539 struct userData *chan, *next;
1542 for (chan = hi->channels; chan; chan = next) {
1543 next = chan->u_next;
1544 name = chan->channel->channel->name;
1545 herelen = strlen(name);
1546 if (pos + herelen + 7 > ArrayLength(buff)) {
1548 goto print_chans_buff;
1550 if (IsUserSuspended(chan))
1552 pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
1556 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1561 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1564 for (target = hi->users; target; target = next_un) {
1565 herelen = strlen(target->nick);
1566 if (pos + herelen + 1 > ArrayLength(buff)) {
1568 goto print_cnick_buff;
1570 next_un = target->next_authed;
1572 memcpy(buff+pos, target->nick, herelen);
1573 pos += herelen; buff[pos++] = ' ';
1577 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1582 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1585 static NICKSERV_FUNC(cmd_userinfo)
1587 struct userNode *target;
1589 NICKSERV_MIN_PARMS(2);
1590 if (!(target = GetUserH(argv[1]))) {
1591 reply("MSG_NICK_UNKNOWN", argv[1]);
1594 if (target->handle_info)
1595 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1597 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1601 static NICKSERV_FUNC(cmd_nickinfo)
1603 struct nick_info *ni;
1605 NICKSERV_MIN_PARMS(2);
1606 if (!(ni = get_nick_info(argv[1]))) {
1607 reply("MSG_NICK_UNKNOWN", argv[1]);
1610 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1614 static NICKSERV_FUNC(cmd_notes)
1616 struct handle_info *hi;
1617 struct handle_note *prev, *note;
1620 NICKSERV_MIN_PARMS(2);
1621 if (!(hi = get_victim_oper(user, argv[1])))
1624 WALK_NOTES(hi, prev, note) {
1625 char set_time[INTERVALLEN];
1626 intervalString(set_time, now - note->set, user->handle_info);
1627 if (note->expires) {
1628 char exp_time[INTERVALLEN];
1629 intervalString(exp_time, note->expires - now, user->handle_info);
1630 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1632 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1636 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1640 static NICKSERV_FUNC(cmd_rename_handle)
1642 struct handle_info *hi;
1643 char msgbuf[MAXLEN], *old_handle;
1646 NICKSERV_MIN_PARMS(3);
1647 if (!(hi = get_victim_oper(user, argv[1])))
1649 if (!is_valid_handle(argv[2])) {
1650 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1653 if (get_handle_info(argv[2])) {
1654 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1657 if (hi->fakehost && hi->fakehost[0] == '.' &&
1658 (strlen(argv[2]) + strlen(hi->fakehost+1) +
1659 strlen(titlehost_suffix) + 2) > HOSTLEN) {
1660 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED_RENAME");
1664 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1665 hi->handle = strdup(argv[2]);
1666 dict_insert(nickserv_handle_dict, hi->handle, hi);
1667 for (nn=0; nn<rf_list_used; nn++)
1668 rf_list[nn](hi, old_handle);
1669 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1670 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1671 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1677 static failpw_func_t *failpw_func_list;
1678 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1681 reg_failpw_func(failpw_func_t func)
1683 if (failpw_func_used == failpw_func_size) {
1684 if (failpw_func_size) {
1685 failpw_func_size <<= 1;
1686 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1688 failpw_func_size = 8;
1689 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1692 failpw_func_list[failpw_func_used++] = func;
1695 static NICKSERV_FUNC(cmd_auth)
1697 int pw_arg, used, maxlogins;
1698 struct handle_info *hi;
1700 struct userNode *other;
1702 if (user->handle_info) {
1703 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1706 if (IsStamped(user)) {
1707 /* Unauthenticated users might still have been stamped
1708 previously and could therefore have a hidden host;
1709 do not allow them to authenticate. */
1710 reply("NSMSG_STAMPED_AUTH");
1714 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1716 } else if (argc == 2) {
1717 if (nickserv_conf.disable_nicks) {
1718 if (!(hi = get_handle_info(user->nick))) {
1719 reply("NSMSG_HANDLE_NOT_FOUND");
1723 /* try to look up their handle from their nick */
1724 struct nick_info *ni;
1725 ni = get_nick_info(user->nick);
1727 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1734 reply("MSG_MISSING_PARAMS", argv[0]);
1735 svccmd_send_help(user, nickserv, cmd);
1739 reply("NSMSG_HANDLE_NOT_FOUND");
1742 /* Responses from here on look up the language used by the handle they asked about. */
1743 passwd = argv[pw_arg];
1744 if (!valid_user_for(user, hi)) {
1745 if (hi->email_addr && nickserv_conf.email_enabled)
1746 send_message_type(4, user, cmd->parent->bot,
1747 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1750 send_message_type(4, user, cmd->parent->bot,
1751 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1753 argv[pw_arg] = "BADMASK";
1756 if (!checkpass(passwd, hi->passwd)) {
1758 send_message_type(4, user, cmd->parent->bot,
1759 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1760 argv[pw_arg] = "BADPASS";
1761 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1762 if (nickserv_conf.autogag_enabled) {
1763 if (!user->auth_policer.params) {
1764 user->auth_policer.last_req = now;
1765 user->auth_policer.params = nickserv_conf.auth_policer_params;
1767 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1769 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1770 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1771 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1773 argv[pw_arg] = "GAGGED";
1778 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1779 send_message_type(4, user, cmd->parent->bot,
1780 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1781 argv[pw_arg] = "SUSPENDED";
1784 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1785 for (used = 0, other = hi->users; other; other = other->next_authed) {
1786 if (++used >= maxlogins) {
1787 send_message_type(4, user, cmd->parent->bot,
1788 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1790 argv[pw_arg] = "MAXLOGINS";
1795 set_user_handle_info(user, hi, 1);
1796 if (nickserv_conf.email_required && !hi->email_addr)
1797 reply("NSMSG_PLEASE_SET_EMAIL");
1798 if (!is_secure_password(hi->handle, passwd, NULL))
1799 reply("NSMSG_WEAK_PASSWORD");
1800 if (hi->passwd[0] != '$')
1801 cryptpass(passwd, hi->passwd);
1802 if (!hi->masks->used) {
1804 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1805 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1806 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1808 argv[pw_arg] = "****";
1809 reply("NSMSG_AUTH_SUCCESS");
1813 static allowauth_func_t *allowauth_func_list;
1814 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1817 reg_allowauth_func(allowauth_func_t func)
1819 if (allowauth_func_used == allowauth_func_size) {
1820 if (allowauth_func_size) {
1821 allowauth_func_size <<= 1;
1822 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
1824 allowauth_func_size = 8;
1825 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
1828 allowauth_func_list[allowauth_func_used++] = func;
1831 static NICKSERV_FUNC(cmd_allowauth)
1833 struct userNode *target;
1834 struct handle_info *hi;
1837 NICKSERV_MIN_PARMS(2);
1838 if (!(target = GetUserH(argv[1]))) {
1839 reply("MSG_NICK_UNKNOWN", argv[1]);
1842 if (target->handle_info) {
1843 reply("NSMSG_USER_PREV_AUTH", target->nick);
1846 if (IsStamped(target)) {
1847 /* Unauthenticated users might still have been stamped
1848 previously and could therefore have a hidden host;
1849 do not allow them to authenticate to an account. */
1850 reply("NSMSG_USER_PREV_STAMP", target->nick);
1855 else if (!(hi = get_handle_info(argv[2]))) {
1856 reply("MSG_HANDLE_UNKNOWN", argv[2]);
1860 if (hi->opserv_level > user->handle_info->opserv_level) {
1861 reply("MSG_USER_OUTRANKED", hi->handle);
1864 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
1865 || (hi->opserv_level > 0))
1866 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
1867 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
1870 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
1871 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
1872 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
1873 if (nickserv_conf.email_enabled)
1874 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
1876 if (dict_remove(nickserv_allow_auth_dict, target->nick))
1877 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
1879 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
1881 for (n=0; n<allowauth_func_used; n++)
1882 allowauth_func_list[n](user, target, hi);
1886 static NICKSERV_FUNC(cmd_authcookie)
1888 struct handle_info *hi;
1890 NICKSERV_MIN_PARMS(2);
1891 if (user->handle_info) {
1892 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1895 if (IsStamped(user)) {
1896 /* Unauthenticated users might still have been stamped
1897 previously and could therefore have a hidden host;
1898 do not allow them to authenticate to an account. */
1899 reply("NSMSG_STAMPED_AUTHCOOKIE");
1902 if (!(hi = get_handle_info(argv[1]))) {
1903 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1906 if (!hi->email_addr) {
1907 reply("MSG_SET_EMAIL_ADDR");
1910 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
1914 static NICKSERV_FUNC(cmd_delcookie)
1916 struct handle_info *hi;
1918 hi = user->handle_info;
1920 reply("NSMSG_NO_COOKIE");
1923 switch (hi->cookie->type) {
1926 reply("NSMSG_MUST_TIME_OUT");
1929 nickserv_eat_cookie(hi->cookie);
1930 reply("NSMSG_ATE_COOKIE");
1936 static NICKSERV_FUNC(cmd_odelcookie)
1938 struct handle_info *hi;
1940 NICKSERV_MIN_PARMS(2);
1942 if (!(hi = get_victim_oper(user, argv[1])))
1946 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
1950 nickserv_eat_cookie(hi->cookie);
1951 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
1956 static NICKSERV_FUNC(cmd_resetpass)
1958 struct handle_info *hi;
1959 char crypted[MD5_CRYPT_LENGTH];
1961 NICKSERV_MIN_PARMS(3);
1962 if (user->handle_info) {
1963 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1966 if (IsStamped(user)) {
1967 /* Unauthenticated users might still have been stamped
1968 previously and could therefore have a hidden host;
1969 do not allow them to activate an account. */
1970 reply("NSMSG_STAMPED_RESETPASS");
1973 if (!(hi = get_handle_info(argv[1]))) {
1974 reply("MSG_HANDLE_UNKNOWN", argv[1]);
1977 if (!hi->email_addr) {
1978 reply("MSG_SET_EMAIL_ADDR");
1981 cryptpass(argv[2], crypted);
1983 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
1987 static NICKSERV_FUNC(cmd_cookie)
1989 struct handle_info *hi;
1992 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
1995 NICKSERV_MIN_PARMS(3);
1996 if (!(hi = get_handle_info(argv[1]))) {
1997 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2003 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2004 reply("NSMSG_HANDLE_SUSPENDED");
2009 reply("NSMSG_NO_COOKIE");
2013 /* Check validity of operation before comparing cookie to
2014 * prohibit guessing by authed users. */
2015 if (user->handle_info
2016 && (hi->cookie->type != EMAIL_CHANGE)
2017 && (hi->cookie->type != PASSWORD_CHANGE)) {
2018 reply("NSMSG_CANNOT_COOKIE");
2022 if (strcmp(cookie, hi->cookie->cookie)) {
2023 reply("NSMSG_BAD_COOKIE");
2027 switch (hi->cookie->type) {
2029 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2030 set_user_handle_info(user, hi, 1);
2031 reply("NSMSG_HANDLE_ACTIVATED");
2033 case PASSWORD_CHANGE:
2034 set_user_handle_info(user, hi, 1);
2035 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2036 reply("NSMSG_PASSWORD_CHANGED");
2039 nickserv_set_email_addr(hi, hi->cookie->data);
2040 reply("NSMSG_EMAIL_CHANGED");
2043 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2044 set_user_handle_info(user, hi, 1);
2045 nickserv_addmask(user, hi, mask);
2046 reply("NSMSG_AUTH_SUCCESS");
2051 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2052 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2056 nickserv_eat_cookie(hi->cookie);
2061 static NICKSERV_FUNC(cmd_oregnick) {
2063 struct handle_info *target;
2064 struct nick_info *ni;
2066 NICKSERV_MIN_PARMS(3);
2067 if (!(target = modcmd_get_handle_info(user, argv[1])))
2070 if (!is_registerable_nick(nick)) {
2071 reply("NSMSG_BAD_NICK", nick);
2074 ni = dict_find(nickserv_nick_dict, nick, NULL);
2076 reply("NSMSG_NICK_EXISTS", nick);
2079 register_nick(nick, target);
2080 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2084 static NICKSERV_FUNC(cmd_regnick) {
2086 struct nick_info *ni;
2088 if (!is_registerable_nick(user->nick)) {
2089 reply("NSMSG_BAD_NICK", user->nick);
2092 /* count their nicks, see if it's too many */
2093 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2094 if (n >= nickserv_conf.nicks_per_handle) {
2095 reply("NSMSG_TOO_MANY_NICKS");
2098 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2100 reply("NSMSG_NICK_EXISTS", user->nick);
2103 register_nick(user->nick, user->handle_info);
2104 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2108 static NICKSERV_FUNC(cmd_pass)
2110 struct handle_info *hi;
2111 const char *old_pass, *new_pass;
2113 NICKSERV_MIN_PARMS(3);
2114 hi = user->handle_info;
2118 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2119 if (!checkpass(old_pass, hi->passwd)) {
2120 argv[1] = "BADPASS";
2121 reply("NSMSG_PASSWORD_INVALID");
2124 cryptpass(new_pass, hi->passwd);
2126 reply("NSMSG_PASS_SUCCESS");
2131 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2134 char *new_mask = canonicalize_hostmask(strdup(mask));
2135 for (i=0; i<hi->masks->used; i++) {
2136 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2137 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2142 string_list_append(hi->masks, new_mask);
2143 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2147 static NICKSERV_FUNC(cmd_addmask)
2150 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2151 int res = nickserv_addmask(user, user->handle_info, mask);
2155 if (!is_gline(argv[1])) {
2156 reply("NSMSG_MASK_INVALID", argv[1]);
2159 return nickserv_addmask(user, user->handle_info, argv[1]);
2163 static NICKSERV_FUNC(cmd_oaddmask)
2165 struct handle_info *hi;
2167 NICKSERV_MIN_PARMS(3);
2168 if (!(hi = get_victim_oper(user, argv[1])))
2170 return nickserv_addmask(user, hi, argv[2]);
2174 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2177 for (i=0; i<hi->masks->used; i++) {
2178 if (!strcmp(del_mask, hi->masks->list[i])) {
2179 char *old_mask = hi->masks->list[i];
2180 if (hi->masks->used == 1 && !force) {
2181 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2184 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2185 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2190 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2194 static NICKSERV_FUNC(cmd_delmask)
2196 NICKSERV_MIN_PARMS(2);
2197 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2200 static NICKSERV_FUNC(cmd_odelmask)
2202 struct handle_info *hi;
2203 NICKSERV_MIN_PARMS(3);
2204 if (!(hi = get_victim_oper(user, argv[1])))
2206 return nickserv_delmask(user, hi, argv[2], 1);
2210 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2211 unsigned int nn, add = 1, pos;
2212 unsigned long added, removed, flag;
2214 for (added=removed=nn=0; str[nn]; nn++) {
2216 case '+': add = 1; break;
2217 case '-': add = 0; break;
2219 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2220 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2223 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2224 /* cheesy avoidance of looking up the flag name.. */
2225 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2228 flag = 1 << (pos - 1);
2230 added |= flag, removed &= ~flag;
2232 removed |= flag, added &= ~flag;
2237 *premoved = removed;
2242 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2244 unsigned long before, after, added, removed;
2245 struct userNode *uNode;
2247 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2248 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2250 hi->flags = (hi->flags | added) & ~removed;
2251 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2253 /* Strip helping flag if they're only a support helper and not
2254 * currently in #support. */
2255 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2256 struct channelList *schannels;
2258 schannels = chanserv_support_channels();
2259 for (ii = 0; ii < schannels->used; ++ii)
2260 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2262 if (ii == schannels->used)
2263 HANDLE_CLEAR_FLAG(hi, HELPING);
2266 if (after && !before) {
2267 /* Add user to current helper list. */
2268 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2269 userList_append(&curr_helpers, uNode);
2270 } else if (!after && before) {
2271 /* Remove user from current helper list. */
2272 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2273 userList_remove(&curr_helpers, uNode);
2280 set_list(struct userNode *user, struct handle_info *hi, int override)
2284 char *set_display[] = {
2285 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2286 "EMAIL", "MAXLOGINS", "LANGUAGE"
2289 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2291 /* Do this so options are presented in a consistent order. */
2292 for (i = 0; i < ArrayLength(set_display); ++i)
2293 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2294 opt(user, hi, override, 0, NULL);
2297 static NICKSERV_FUNC(cmd_set)
2299 struct handle_info *hi;
2302 hi = user->handle_info;
2304 set_list(user, hi, 0);
2307 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2308 reply("NSMSG_INVALID_OPTION", argv[1]);
2311 return opt(user, hi, 0, argc-1, argv+1);
2314 static NICKSERV_FUNC(cmd_oset)
2316 struct handle_info *hi;
2317 struct svccmd *subcmd;
2319 char cmdname[MAXLEN];
2321 NICKSERV_MIN_PARMS(2);
2323 if (!(hi = get_victim_oper(user, argv[1])))
2327 set_list(user, hi, 0);
2331 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2332 reply("NSMSG_INVALID_OPTION", argv[2]);
2336 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2337 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2338 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2341 return opt(user, hi, 1, argc-2, argv+2);
2344 static OPTION_FUNC(opt_info)
2348 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2350 hi->infoline = NULL;
2352 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2356 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2357 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2361 static OPTION_FUNC(opt_website)
2363 const char *website;
2366 if (!HANDLE_FLAGGED(user->handle_info, BOT)) {
2367 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2370 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2374 website = unsplit_string(argv+1, argc-1, NULL);
2375 hi->website = strdup(website);
2378 if (HANDLE_FLAGGED(user->handle_info, BOT)) {
2379 website = hi->website ? hi->website : user_find_message(user, "MSG_NONE");
2380 send_message(user, nickserv, "NSMSG_SET_WEBSITE", website);
2385 static OPTION_FUNC(opt_width)
2388 hi->screen_width = strtoul(argv[1], NULL, 0);
2390 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2391 hi->screen_width = MIN_LINE_SIZE;
2392 else if (hi->screen_width > MAX_LINE_SIZE)
2393 hi->screen_width = MAX_LINE_SIZE;
2395 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2399 static OPTION_FUNC(opt_tablewidth)
2402 hi->table_width = strtoul(argv[1], NULL, 0);
2404 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2405 hi->table_width = MIN_LINE_SIZE;
2406 else if (hi->screen_width > MAX_LINE_SIZE)
2407 hi->table_width = MAX_LINE_SIZE;
2409 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2413 static OPTION_FUNC(opt_color)
2416 if (enabled_string(argv[1]))
2417 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2418 else if (disabled_string(argv[1]))
2419 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2421 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2426 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2430 static OPTION_FUNC(opt_privmsg)
2433 if (enabled_string(argv[1]))
2434 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2435 else if (disabled_string(argv[1]))
2436 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2438 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2443 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2447 static OPTION_FUNC(opt_style)
2452 if (!irccasecmp(argv[1], "Zoot"))
2453 hi->userlist_style = HI_STYLE_ZOOT;
2454 else if (!irccasecmp(argv[1], "def"))
2455 hi->userlist_style = HI_STYLE_DEF;
2458 switch (hi->userlist_style) {
2467 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2471 static OPTION_FUNC(opt_password)
2474 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2479 cryptpass(argv[1], hi->passwd);
2481 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2485 static OPTION_FUNC(opt_flags)
2488 unsigned int ii, flen;
2491 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2496 nickserv_apply_flags(user, hi, argv[1]);
2498 for (ii = flen = 0; handle_flags[ii]; ii++)
2499 if (hi->flags & (1 << ii))
2500 flags[flen++] = handle_flags[ii];
2503 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2505 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2509 static OPTION_FUNC(opt_email)
2513 if (!is_valid_email_addr(argv[1])) {
2514 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2517 if ((str = mail_prohibited_address(argv[1]))) {
2518 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2521 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2522 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2524 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2526 nickserv_set_email_addr(hi, argv[1]);
2528 nickserv_eat_cookie(hi->cookie);
2529 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2532 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2536 static OPTION_FUNC(opt_maxlogins)
2538 unsigned char maxlogins;
2540 maxlogins = strtoul(argv[1], NULL, 0);
2541 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2542 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2545 hi->maxlogins = maxlogins;
2547 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2548 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2552 static OPTION_FUNC(opt_language)
2554 struct language *lang;
2556 lang = language_find(argv[1]);
2557 if (irccasecmp(lang->name, argv[1]))
2558 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2559 hi->language = lang;
2561 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2565 static OPTION_FUNC(opt_karma)
2568 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2573 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2574 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2575 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2576 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2578 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2582 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2587 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2588 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2590 if ((user->handle_info->opserv_level < target->opserv_level)
2591 || ((user->handle_info->opserv_level == target->opserv_level)
2592 && (user->handle_info->opserv_level < 1000))) {
2593 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2596 if ((user->handle_info->opserv_level < new_level)
2597 || ((user->handle_info->opserv_level == new_level)
2598 && (user->handle_info->opserv_level < 1000))) {
2599 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2602 if (user->handle_info == target) {
2603 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2606 if (target->opserv_level == new_level)
2608 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2609 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2610 target->opserv_level = new_level;
2614 static OPTION_FUNC(opt_level)
2619 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2623 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2624 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2628 static OPTION_FUNC(opt_epithet)
2631 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2635 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2636 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2639 if ((epithet[0] == '*') && !epithet[1])
2642 hi->epithet = strdup(epithet);
2646 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2648 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2652 static OPTION_FUNC(opt_title)
2657 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2661 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2663 if (strchr(title, '.')) {
2664 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2667 if ((strlen(user->handle_info->handle) + strlen(title) +
2668 strlen(titlehost_suffix) + 2) > HOSTLEN) {
2669 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2674 if (!strcmp(title, "*")) {
2675 hi->fakehost = NULL;
2677 hi->fakehost = malloc(strlen(title)+2);
2678 hi->fakehost[0] = '.';
2679 strcpy(hi->fakehost+1, title);
2681 apply_fakehost(hi, NULL);
2682 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2683 title = hi->fakehost + 1;
2687 title = user_find_message(user, "MSG_NONE");
2688 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
2692 static OPTION_FUNC(opt_fakehost)
2694 char mask[USERLEN + HOSTLEN + 2];
2698 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2702 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
2703 if(strlen(argv[1]) >= sizeof(mask)) {
2704 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
2708 safestrncpy(mask, argv[1], sizeof(mask));
2710 if ((host = strrchr(mask, '@')) && host != mask) {
2711 /* If ident@host was used and the user doesn't have access to set idents, do not change anything. */
2712 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2724 if (ident && strlen(ident) > USERLEN) {
2725 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2729 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
2730 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
2734 if (host && host[0]) {
2736 if (!strcmp(host, "*"))
2737 hi->fakehost = NULL;
2739 hi->fakehost = strdup(host);
2740 host = hi->fakehost;
2743 host = generate_fakehost(hi);
2746 free(hi->fakeident);
2747 if (!strcmp(ident, "*"))
2748 hi->fakeident = NULL;
2750 hi->fakeident = strdup(ident);
2751 ident = hi->fakeident;
2754 ident = generate_fakeident(hi, NULL);
2756 apply_fakehost(hi, NULL);
2758 host = generate_fakehost(hi);
2759 ident = generate_fakeident(hi, NULL);
2762 host = (char *) user_find_message(user, "MSG_NONE");
2764 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
2766 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
2770 static OPTION_FUNC(opt_fakeident)
2775 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2779 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
2781 if (strlen(ident) > USERLEN) {
2782 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
2785 free(hi->fakeident);
2786 if (!strcmp(ident, "*"))
2787 hi->fakeident = NULL;
2789 hi->fakeident = strdup(ident);
2790 ident = hi->fakeident;
2791 apply_fakehost(hi, NULL);
2793 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
2795 ident = user_find_message(user, "MSG_NONE");
2796 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
2800 static NICKSERV_FUNC(cmd_reclaim)
2802 struct handle_info *hi;
2803 struct nick_info *ni;
2804 struct userNode *victim;
2806 NICKSERV_MIN_PARMS(2);
2807 hi = user->handle_info;
2808 ni = dict_find(nickserv_nick_dict, argv[1], 0);
2810 reply("NSMSG_UNKNOWN_NICK", argv[1]);
2813 if (ni->owner != user->handle_info) {
2814 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
2817 victim = GetUserH(ni->nick);
2819 reply("MSG_NICK_UNKNOWN", ni->nick);
2822 if (victim == user) {
2823 reply("NSMSG_NICK_USER_YOU");
2826 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
2827 switch (nickserv_conf.reclaim_action) {
2828 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
2829 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
2830 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
2831 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
2836 static NICKSERV_FUNC(cmd_unregnick)
2839 struct handle_info *hi;
2840 struct nick_info *ni;
2842 hi = user->handle_info;
2843 nick = (argc < 2) ? user->nick : (const char*)argv[1];
2844 ni = dict_find(nickserv_nick_dict, nick, NULL);
2846 reply("NSMSG_UNKNOWN_NICK", nick);
2849 if (hi != ni->owner) {
2850 reply("NSMSG_NOT_YOUR_NICK", nick);
2853 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2858 static NICKSERV_FUNC(cmd_ounregnick)
2860 struct nick_info *ni;
2862 NICKSERV_MIN_PARMS(2);
2863 if (!(ni = get_nick_info(argv[1]))) {
2864 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
2867 if (!oper_outranks(user, ni->owner))
2869 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
2874 static NICKSERV_FUNC(cmd_unregister)
2876 struct handle_info *hi;
2879 NICKSERV_MIN_PARMS(2);
2880 hi = user->handle_info;
2883 if (checkpass(passwd, hi->passwd)) {
2884 nickserv_unregister_handle(hi, user);
2887 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
2888 reply("NSMSG_PASSWORD_INVALID");
2893 static NICKSERV_FUNC(cmd_ounregister)
2895 struct handle_info *hi;
2896 char reason[MAXLEN];
2899 NICKSERV_MIN_PARMS(2);
2900 if (!(hi = get_victim_oper(user, argv[1])))
2903 if (HANDLE_FLAGGED(hi, NODELETE)) {
2904 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
2908 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
2910 ((hi->flags & nickserv_conf.ounregister_flags)
2912 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
2913 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
2917 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
2918 global_message(MESSAGE_RECIPIENT_STAFF, reason);
2919 nickserv_unregister_handle(hi, user);
2923 static NICKSERV_FUNC(cmd_status)
2925 if (nickserv_conf.disable_nicks) {
2926 reply("NSMSG_GLOBAL_STATS_NONICK",
2927 dict_size(nickserv_handle_dict));
2929 if (user->handle_info) {
2931 struct nick_info *ni;
2932 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
2933 reply("NSMSG_HANDLE_STATS", cnt);
2935 reply("NSMSG_HANDLE_NONE");
2937 reply("NSMSG_GLOBAL_STATS",
2938 dict_size(nickserv_handle_dict),
2939 dict_size(nickserv_nick_dict));
2944 static NICKSERV_FUNC(cmd_ghost)
2946 struct userNode *target;
2947 char reason[MAXLEN];
2949 NICKSERV_MIN_PARMS(2);
2950 if (!(target = GetUserH(argv[1]))) {
2951 reply("MSG_NICK_UNKNOWN", argv[1]);
2954 if (target == user) {
2955 reply("NSMSG_CANNOT_GHOST_SELF");
2958 if (!target->handle_info || (target->handle_info != user->handle_info)) {
2959 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
2962 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
2963 DelUser(target, nickserv, 1, reason);
2964 reply("NSMSG_GHOST_KILLED", argv[1]);
2968 static NICKSERV_FUNC(cmd_vacation)
2970 HANDLE_SET_FLAG(user->handle_info, FROZEN);
2971 reply("NSMSG_ON_VACATION");
2975 static NICKSERV_FUNC(cmd_addnote)
2977 struct handle_info *hi;
2978 unsigned long duration;
2981 struct handle_note *prev;
2982 struct handle_note *note;
2984 /* Parse parameters and figure out values for note's fields. */
2985 NICKSERV_MIN_PARMS(4);
2986 hi = get_victim_oper(user, argv[1]);
2989 if(!strcmp(argv[2], "0"))
2991 else if(!(duration = ParseInterval(argv[2])))
2993 reply("MSG_INVALID_DURATION", argv[2]);
2996 if (duration > 2*365*86400) {
2997 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
3000 unsplit_string(argv + 3, argc - 3, text);
3001 WALK_NOTES(hi, prev, note) {}
3002 id = prev ? (prev->id + 1) : 1;
3004 /* Create the new note structure. */
3005 note = calloc(1, sizeof(*note) + strlen(text));
3007 note->expires = duration ? (now + duration) : 0;
3010 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
3011 strcpy(note->note, text);
3016 reply("NSMSG_NOTE_ADDED", id, hi->handle);
3020 static NICKSERV_FUNC(cmd_delnote)
3022 struct handle_info *hi;
3023 struct handle_note *prev;
3024 struct handle_note *note;
3027 NICKSERV_MIN_PARMS(3);
3028 hi = get_victim_oper(user, argv[1]);
3031 id = strtoul(argv[2], NULL, 10);
3032 WALK_NOTES(hi, prev, note) {
3033 if (id == note->id) {
3035 prev->next = note->next;
3037 hi->notes = note->next;
3039 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3043 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3048 nickserv_saxdb_write(struct saxdb_context *ctx) {
3050 struct handle_info *hi;
3053 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3055 assert(hi->id != 0);
3056 saxdb_start_record(ctx, iter_key(it), 0);
3058 struct handle_cookie *cookie = hi->cookie;
3061 switch (cookie->type) {
3062 case ACTIVATION: type = KEY_ACTIVATION; break;
3063 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3064 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3065 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3066 default: type = NULL; break;
3069 saxdb_start_record(ctx, KEY_COOKIE, 0);
3070 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3071 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3073 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3074 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3075 saxdb_end_record(ctx);
3079 struct handle_note *prev, *note;
3080 saxdb_start_record(ctx, KEY_NOTES, 0);
3081 WALK_NOTES(hi, prev, note) {
3082 snprintf(flags, sizeof(flags), "%d", note->id);
3083 saxdb_start_record(ctx, flags, 0);
3085 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3086 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3087 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3088 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3089 saxdb_end_record(ctx);
3091 saxdb_end_record(ctx);
3094 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3096 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3098 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3100 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3104 for (ii=flen=0; handle_flags[ii]; ++ii)
3105 if (hi->flags & (1 << ii))
3106 flags[flen++] = handle_flags[ii];
3108 saxdb_write_string(ctx, KEY_FLAGS, flags);
3110 saxdb_write_int(ctx, KEY_ID, hi->id);
3112 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3114 saxdb_write_string(ctx, KEY_WEBSITE, hi->website);
3115 if (hi->last_quit_host[0])
3116 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3117 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3119 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3120 if (hi->masks->used)
3121 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3123 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3125 struct string_list *slist;
3126 struct nick_info *ni;
3128 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3129 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3130 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3134 if (hi->opserv_level)
3135 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3136 if (hi->language != lang_C)
3137 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3138 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3139 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3140 if (hi->screen_width)
3141 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3142 if (hi->table_width)
3143 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3144 flags[0] = hi->userlist_style;
3146 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3147 saxdb_end_record(ctx);
3152 static handle_merge_func_t *handle_merge_func_list;
3153 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3156 reg_handle_merge_func(handle_merge_func_t func)
3158 if (handle_merge_func_used == handle_merge_func_size) {
3159 if (handle_merge_func_size) {
3160 handle_merge_func_size <<= 1;
3161 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3163 handle_merge_func_size = 8;
3164 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3167 handle_merge_func_list[handle_merge_func_used++] = func;
3170 static NICKSERV_FUNC(cmd_merge)
3172 struct handle_info *hi_from, *hi_to;
3173 struct userNode *last_user;
3174 struct userData *cList, *cListNext;
3175 unsigned int ii, jj, n;
3176 char buffer[MAXLEN];
3178 NICKSERV_MIN_PARMS(3);
3180 if (!(hi_from = get_victim_oper(user, argv[1])))
3182 if (!(hi_to = get_victim_oper(user, argv[2])))
3184 if (hi_to == hi_from) {
3185 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3189 for (n=0; n<handle_merge_func_used; n++)
3190 handle_merge_func_list[n](user, hi_to, hi_from);
3192 /* Append "from" handle's nicks to "to" handle's nick list. */
3194 struct nick_info *last_ni;
3195 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3196 last_ni->next = hi_from->nicks;
3198 while (hi_from->nicks) {
3199 hi_from->nicks->owner = hi_to;
3200 hi_from->nicks = hi_from->nicks->next;
3203 /* Merge the hostmasks. */
3204 for (ii=0; ii<hi_from->masks->used; ii++) {
3205 char *mask = hi_from->masks->list[ii];
3206 for (jj=0; jj<hi_to->masks->used; jj++)
3207 if (match_ircglobs(hi_to->masks->list[jj], mask))
3209 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3210 string_list_append(hi_to->masks, strdup(mask));
3213 /* Merge the lists of authed users. */
3215 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3216 last_user->next_authed = hi_from->users;
3218 hi_to->users = hi_from->users;
3220 /* Repoint the old "from" handle's users. */
3221 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3222 last_user->handle_info = hi_to;
3224 hi_from->users = NULL;
3226 /* Merge channel userlists. */
3227 for (cList=hi_from->channels; cList; cList=cListNext) {
3228 struct userData *cList2;
3229 cListNext = cList->u_next;
3230 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3231 if (cList->channel == cList2->channel)
3233 if (cList2 && (cList2->access >= cList->access)) {
3234 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);
3235 /* keep cList2 in hi_to; remove cList from hi_from */
3236 del_channel_user(cList, 1);
3239 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);
3240 /* remove the lower-ranking cList2 from hi_to */
3241 del_channel_user(cList2, 1);
3243 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3245 /* cList needs to be moved from hi_from to hi_to */
3246 cList->handle = hi_to;
3247 /* Remove from linked list for hi_from */
3248 assert(!cList->u_prev);
3249 hi_from->channels = cList->u_next;
3251 cList->u_next->u_prev = cList->u_prev;
3252 /* Add to linked list for hi_to */
3253 cList->u_prev = NULL;
3254 cList->u_next = hi_to->channels;
3255 if (hi_to->channels)
3256 hi_to->channels->u_prev = cList;
3257 hi_to->channels = cList;
3261 /* Do they get an OpServ level promotion? */
3262 if (hi_from->opserv_level > hi_to->opserv_level)
3263 hi_to->opserv_level = hi_from->opserv_level;
3265 /* What about last seen time? */
3266 if (hi_from->lastseen > hi_to->lastseen)
3267 hi_to->lastseen = hi_from->lastseen;
3269 /* New karma is the sum of the two original karmas. */
3270 hi_to->karma += hi_from->karma;
3272 /* Does a fakehost carry over? (This intentionally doesn't set it
3273 * for users previously attached to hi_to. They'll just have to
3276 if (hi_from->fakehost && !hi_to->fakehost)
3277 hi_to->fakehost = strdup(hi_from->fakehost);
3278 if (hi_from->fakeident && !hi_to->fakeident)
3279 hi_to->fakeident = strdup(hi_from->fakeident);
3281 /* Notify of success. */
3282 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3283 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3284 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3286 /* Unregister the "from" handle. */
3287 nickserv_unregister_handle(hi_from, NULL);
3292 struct nickserv_discrim {
3293 unsigned long flags_on, flags_off;
3294 unsigned long min_registered, max_registered;
3295 unsigned long lastseen;
3297 int min_level, max_level;
3298 int min_karma, max_karma;
3299 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3300 const char *nickmask;
3301 const char *hostmask;
3302 const char *fakehostmask;
3303 const char *fakeidentmask;
3304 const char *website;
3305 const char *handlemask;
3306 const char *emailmask;
3309 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi);
3311 struct discrim_apply_info {
3312 struct nickserv_discrim *discrim;
3313 discrim_search_func func;
3314 struct userNode *source;
3315 unsigned int matched;
3318 static struct nickserv_discrim *
3319 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3322 struct nickserv_discrim *discrim;
3324 discrim = malloc(sizeof(*discrim));
3325 memset(discrim, 0, sizeof(*discrim));
3326 discrim->min_level = 0;
3327 discrim->max_level = INT_MAX;
3328 discrim->limit = 50;
3329 discrim->min_registered = 0;
3330 discrim->max_registered = ULONG_MAX;
3331 discrim->lastseen = ULONG_MAX;
3332 discrim->min_karma = INT_MIN;
3333 discrim->max_karma = INT_MAX;
3335 for (i=0; i<argc; i++) {
3336 if (i == argc - 1) {
3337 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3340 if (!irccasecmp(argv[i], "limit")) {
3341 discrim->limit = strtoul(argv[++i], NULL, 0);
3342 } else if (!irccasecmp(argv[i], "flags")) {
3343 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3344 } else if (!irccasecmp(argv[i], "registered")) {
3345 const char *cmp = argv[++i];
3346 if (cmp[0] == '<') {
3347 if (cmp[1] == '=') {
3348 discrim->min_registered = now - ParseInterval(cmp+2);
3350 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3352 } else if (cmp[0] == '=') {
3353 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3354 } else if (cmp[0] == '>') {
3355 if (cmp[1] == '=') {
3356 discrim->max_registered = now - ParseInterval(cmp+2);
3358 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3361 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3363 } else if (!irccasecmp(argv[i], "seen")) {
3364 discrim->lastseen = now - ParseInterval(argv[++i]);
3365 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3366 discrim->nickmask = argv[++i];
3367 } else if (!irccasecmp(argv[i], "hostmask")) {
3369 if (!irccasecmp(argv[i], "exact")) {
3370 if (i == argc - 1) {
3371 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3374 discrim->hostmask_type = EXACT;
3375 } else if (!irccasecmp(argv[i], "subset")) {
3376 if (i == argc - 1) {
3377 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3380 discrim->hostmask_type = SUBSET;
3381 } else if (!irccasecmp(argv[i], "superset")) {
3382 if (i == argc - 1) {
3383 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3386 discrim->hostmask_type = SUPERSET;
3387 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3388 if (i == argc - 1) {
3389 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3392 discrim->hostmask_type = LASTQUIT;
3395 discrim->hostmask_type = SUPERSET;
3397 discrim->hostmask = argv[++i];
3398 } else if (!irccasecmp(argv[i], "fakehost")) {
3399 if (!irccasecmp(argv[++i], "*")) {
3400 discrim->fakehostmask = 0;
3402 discrim->fakehostmask = argv[i];
3404 } else if (!irccasecmp(argv[i], "fakeident")) {
3405 if (!irccasecmp(argv[++i], "*")) {
3406 discrim->fakeidentmask = 0;
3408 discrim->fakeidentmask = argv[i];
3410 } else if (!irccasecmp(argv[i], "website")) {
3411 if (!irccasecmp(argv[++i], "*")) {
3412 discrim->website = 0;
3414 discrim->website = argv[i];
3416 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3417 if (!irccasecmp(argv[++i], "*")) {
3418 discrim->handlemask = 0;
3420 discrim->handlemask = argv[i];
3422 } else if (!irccasecmp(argv[i], "email")) {
3423 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3424 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3426 } else if (!irccasecmp(argv[++i], "*")) {
3427 discrim->emailmask = 0;
3429 discrim->emailmask = argv[i];
3431 } else if (!irccasecmp(argv[i], "access")) {
3432 const char *cmp = argv[++i];
3433 if (cmp[0] == '<') {
3434 if (discrim->min_level == 0) discrim->min_level = 1;
3435 if (cmp[1] == '=') {
3436 discrim->max_level = strtoul(cmp+2, NULL, 0);
3438 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3440 } else if (cmp[0] == '=') {
3441 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3442 } else if (cmp[0] == '>') {
3443 if (cmp[1] == '=') {
3444 discrim->min_level = strtoul(cmp+2, NULL, 0);
3446 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3449 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3451 } else if (!irccasecmp(argv[i], "karma")) {
3452 const char *cmp = argv[++i];
3453 if (cmp[0] == '<') {
3454 if (cmp[1] == '=') {
3455 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3457 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3459 } else if (cmp[0] == '=') {
3460 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3461 } else if (cmp[0] == '>') {
3462 if (cmp[1] == '=') {
3463 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3465 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3468 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3471 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3482 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3484 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3485 || (discrim->flags_off & hi->flags)
3486 || (discrim->min_registered > hi->registered)
3487 || (discrim->max_registered < hi->registered)
3488 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3489 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3490 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3491 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3492 || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website)))
3493 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3494 || (discrim->min_level > hi->opserv_level)
3495 || (discrim->max_level < hi->opserv_level)
3496 || (discrim->min_karma > hi->karma)
3497 || (discrim->max_karma < hi->karma)
3501 if (discrim->hostmask) {
3503 for (i=0; i<hi->masks->used; i++) {
3504 const char *mask = hi->masks->list[i];
3505 if ((discrim->hostmask_type == SUBSET)
3506 && (match_ircglobs(discrim->hostmask, mask))) break;
3507 else if ((discrim->hostmask_type == EXACT)
3508 && !irccasecmp(discrim->hostmask, mask)) break;
3509 else if ((discrim->hostmask_type == SUPERSET)
3510 && (match_ircglobs(mask, discrim->hostmask))) break;
3511 else if ((discrim->hostmask_type == LASTQUIT)
3512 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3514 if (i==hi->masks->used) return 0;
3516 if (discrim->nickmask) {
3517 struct nick_info *nick = hi->nicks;
3519 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3522 if (!nick) return 0;
3528 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3530 dict_iterator_t it, next;
3531 unsigned int matched;
3533 for (it = dict_first(nickserv_handle_dict), matched = 0;
3534 it && (matched < discrim->limit);
3536 next = iter_next(it);
3537 if (nickserv_discrim_match(discrim, iter_data(it))) {
3538 dsf(source, iter_data(it));
3546 search_print_func(struct userNode *source, struct handle_info *match)
3548 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3552 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match))
3557 search_unregister_func (struct userNode *source, struct handle_info *match)
3559 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3560 nickserv_unregister_handle(match, source);
3564 nickserv_sort_accounts_by_access(const void *a, const void *b)
3566 const struct handle_info *hi_a = *(const struct handle_info**)a;
3567 const struct handle_info *hi_b = *(const struct handle_info**)b;
3568 if (hi_a->opserv_level != hi_b->opserv_level)
3569 return hi_b->opserv_level - hi_a->opserv_level;
3570 return irccasecmp(hi_a->handle, hi_b->handle);
3574 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3576 struct handle_info_list hil;
3577 struct helpfile_table tbl;
3582 memset(&hil, 0, sizeof(hil));
3583 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3584 struct handle_info *hi = iter_data(it);
3585 if (hi->opserv_level)
3586 handle_info_list_append(&hil, hi);
3588 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
3589 tbl.length = hil.used + 1;
3591 tbl.flags = TABLE_NO_FREE;
3592 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3593 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
3596 for (ii = 0; ii < hil.used; ) {
3597 ary = malloc(tbl.width * sizeof(ary[0]));
3598 ary[0] = hil.list[ii]->handle;
3599 ary[1] = strtab(hil.list[ii]->opserv_level);
3600 tbl.contents[++ii] = ary;
3602 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3603 reply("MSG_MATCH_COUNT", hil.used);
3604 for (ii = 0; ii < hil.used; ii++)
3605 free(tbl.contents[ii]);
3610 static NICKSERV_FUNC(cmd_search)
3612 struct nickserv_discrim *discrim;
3613 discrim_search_func action;
3614 struct svccmd *subcmd;
3615 unsigned int matches;
3618 NICKSERV_MIN_PARMS(3);
3619 sprintf(buf, "search %s", argv[1]);
3620 subcmd = dict_find(nickserv_service->commands, buf, NULL);
3621 if (!irccasecmp(argv[1], "print"))
3622 action = search_print_func;
3623 else if (!irccasecmp(argv[1], "count"))
3624 action = search_count_func;
3625 else if (!irccasecmp(argv[1], "unregister"))
3626 action = search_unregister_func;
3628 reply("NSMSG_INVALID_ACTION", argv[1]);
3632 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
3635 discrim = nickserv_discrim_create(user, argc-2, argv+2);
3639 if (action == search_print_func)
3640 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
3641 else if (action == search_count_func)
3642 discrim->limit = INT_MAX;
3644 matches = nickserv_discrim_search(discrim, action, user);
3647 reply("MSG_MATCH_COUNT", matches);
3649 reply("MSG_NO_MATCHES");
3655 static MODCMD_FUNC(cmd_checkpass)
3657 struct handle_info *hi;
3659 NICKSERV_MIN_PARMS(3);
3660 if (!(hi = get_handle_info(argv[1]))) {
3661 reply("MSG_HANDLE_UNKNOWN", argv[1]);
3664 if (checkpass(argv[2], hi->passwd))
3665 reply("CHECKPASS_YES");
3667 reply("CHECKPASS_NO");
3672 static MODCMD_FUNC(cmd_checkemail)
3674 struct handle_info *hi;
3676 NICKSERV_MIN_PARMS(3);
3677 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
3680 if (!hi->email_addr)
3681 reply("CHECKEMAIL_NOT_SET");
3682 else if (!irccasecmp(argv[2], hi->email_addr))
3683 reply("CHECKEMAIL_YES");
3685 reply("CHECKEMAIL_NO");
3691 nickserv_db_read_handle(const char *handle, dict_t obj)
3694 struct string_list *masks, *slist;
3695 struct handle_info *hi;
3696 struct userNode *authed_users;
3697 struct userData *channel_list;
3702 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
3703 id = str ? strtoul(str, NULL, 0) : 0;
3704 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
3706 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
3709 if ((hi = get_handle_info(handle))) {
3710 authed_users = hi->users;
3711 channel_list = hi->channels;
3713 hi->channels = NULL;
3714 dict_remove(nickserv_handle_dict, hi->handle);
3716 authed_users = NULL;
3717 channel_list = NULL;
3719 hi = register_handle(handle, str, id);
3721 hi->users = authed_users;
3722 while (authed_users) {
3723 authed_users->handle_info = hi;
3724 authed_users = authed_users->next_authed;
3727 hi->channels = channel_list;
3728 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
3729 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
3730 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
3731 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
3732 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
3733 hi->language = language_find(str ? str : "C");
3734 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
3735 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
3736 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
3738 hi->infoline = strdup(str);
3739 str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING);
3741 hi->website = strdup(str);
3742 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
3743 hi->registered = str ? strtoul(str, NULL, 0) : now;
3744 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
3745 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
3746 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
3747 hi->karma = str ? strtoul(str, NULL, 0) : 0;
3748 /* We want to read the nicks even if disable_nicks is set. This is so
3749 * that we don't lose the nick data entirely. */
3750 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
3752 for (ii=0; ii<slist->used; ii++)
3753 register_nick(slist->list[ii], hi);
3755 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
3757 for (ii=0; str[ii]; ii++)
3758 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
3760 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
3761 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
3762 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
3763 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
3764 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
3765 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
3766 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
3768 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
3770 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
3771 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
3773 nickserv_set_email_addr(hi, str);
3774 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
3776 hi->epithet = strdup(str);
3777 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
3779 hi->fakehost = strdup(str);
3780 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
3782 hi->fakeident = strdup(str);
3783 /* Read the "cookie" sub-database (if it exists). */
3784 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
3786 const char *data, *type, *expires, *cookie_str;
3787 struct handle_cookie *cookie;
3789 cookie = calloc(1, sizeof(*cookie));
3790 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
3791 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
3792 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
3793 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
3794 if (!type || !expires || !cookie_str) {
3795 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
3798 if (!irccasecmp(type, KEY_ACTIVATION))
3799 cookie->type = ACTIVATION;
3800 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
3801 cookie->type = PASSWORD_CHANGE;
3802 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
3803 cookie->type = EMAIL_CHANGE;
3804 else if (!irccasecmp(type, KEY_ALLOWAUTH))
3805 cookie->type = ALLOWAUTH;
3807 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
3810 cookie->expires = strtoul(expires, NULL, 0);
3811 if (cookie->expires < now)
3814 cookie->data = strdup(data);
3815 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
3819 nickserv_bake_cookie(cookie);
3821 nickserv_free_cookie(cookie);
3823 /* Read the "notes" sub-database (if it exists). */
3824 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
3827 struct handle_note *last_note;
3828 struct handle_note *note;
3831 for (it = dict_first(subdb); it; it = iter_next(it)) {
3832 const char *expires;
3836 const char *note_id;
3839 note_id = iter_key(it);
3840 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
3842 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
3845 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
3846 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
3847 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
3848 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
3849 if (!setter || !text || !set) {
3850 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
3853 note = calloc(1, sizeof(*note) + strlen(text));
3855 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
3856 note->set = strtoul(set, NULL, 10);
3857 note->id = strtoul(note_id, NULL, 10);
3858 safestrncpy(note->setter, setter, sizeof(note->setter));
3859 strcpy(note->note, text);
3861 last_note->next = note;
3870 nickserv_saxdb_read(dict_t db) {
3872 struct record_data *rd;
3874 for (it=dict_first(db); it; it=iter_next(it)) {
3876 nickserv_db_read_handle(iter_key(it), rd->d.object);
3881 static NICKSERV_FUNC(cmd_mergedb)
3883 struct timeval start, stop;
3886 NICKSERV_MIN_PARMS(2);
3887 gettimeofday(&start, NULL);
3888 if (!(db = parse_database(argv[1]))) {
3889 reply("NSMSG_DB_UNREADABLE", argv[1]);
3892 nickserv_saxdb_read(db);
3894 gettimeofday(&stop, NULL);
3895 stop.tv_sec -= start.tv_sec;
3896 stop.tv_usec -= start.tv_usec;
3897 if (stop.tv_usec < 0) {
3899 stop.tv_usec += 1000000;
3901 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
3906 expire_handles(UNUSED_ARG(void *data))
3908 dict_iterator_t it, next;
3909 unsigned long expiry;
3910 struct handle_info *hi;
3912 for (it=dict_first(nickserv_handle_dict); it; it=next) {
3913 next = iter_next(it);
3915 if ((hi->opserv_level > 0)
3917 || HANDLE_FLAGGED(hi, FROZEN)
3918 || HANDLE_FLAGGED(hi, NODELETE)) {
3921 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
3922 if ((now - hi->lastseen) > expiry) {
3923 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
3924 nickserv_unregister_handle(hi, NULL);
3928 if (nickserv_conf.handle_expire_frequency)
3929 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
3933 nickserv_load_dict(const char *fname)
3937 if (!(file = fopen(fname, "r"))) {
3938 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
3941 while (fgets(line, sizeof(line), file)) {
3944 if (line[strlen(line)-1] == '\n')
3945 line[strlen(line)-1] = 0;
3946 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
3949 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
3952 static enum reclaim_action
3953 reclaim_action_from_string(const char *str) {
3955 return RECLAIM_NONE;
3956 else if (!irccasecmp(str, "warn"))
3957 return RECLAIM_WARN;
3958 else if (!irccasecmp(str, "svsnick"))
3959 return RECLAIM_SVSNICK;
3960 else if (!irccasecmp(str, "kill"))
3961 return RECLAIM_KILL;
3963 return RECLAIM_NONE;
3967 nickserv_conf_read(void)
3969 dict_t conf_node, child;
3973 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
3974 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
3977 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
3979 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
3980 if (nickserv_conf.valid_handle_regex_set)
3981 regfree(&nickserv_conf.valid_handle_regex);
3983 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3984 nickserv_conf.valid_handle_regex_set = !err;
3985 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
3987 nickserv_conf.valid_handle_regex_set = 0;
3989 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
3990 if (nickserv_conf.valid_nick_regex_set)
3991 regfree(&nickserv_conf.valid_nick_regex);
3993 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
3994 nickserv_conf.valid_nick_regex_set = !err;
3995 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
3997 nickserv_conf.valid_nick_regex_set = 0;
3999 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4001 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4002 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4003 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4004 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4005 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4006 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4007 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4008 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4009 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4010 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4011 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4012 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4013 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4014 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4015 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4016 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4017 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4018 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4019 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4020 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4021 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4022 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4023 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4024 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4025 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
4026 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
4027 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4029 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4030 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4031 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4033 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4034 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4035 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4037 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4038 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4039 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4040 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4041 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4042 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4043 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4044 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4045 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4046 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4047 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4050 nickserv_conf.ounregister_flags = 0;
4052 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4055 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4057 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4059 nickserv_conf.handle_ts_mode = TS_IGNORE;
4060 else if (!irccasecmp(str, "ircu"))
4061 nickserv_conf.handle_ts_mode = TS_IRCU;
4063 nickserv_conf.handle_ts_mode = TS_IGNORE;
4064 if (!nickserv_conf.disable_nicks) {
4065 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4066 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4067 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4068 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4069 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4070 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4071 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4072 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4074 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4075 for (it=dict_first(child); it; it=iter_next(it)) {
4076 const char *key = iter_key(it), *value;
4080 if (!strncasecmp(key, "uc_", 3))
4081 flag = toupper(key[3]);
4082 else if (!strncasecmp(key, "lc_", 3))
4083 flag = tolower(key[3]);
4087 if ((pos = handle_inverse_flags[flag])) {
4088 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4089 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4092 if (nickserv_conf.weak_password_dict)
4093 dict_delete(nickserv_conf.weak_password_dict);
4094 nickserv_conf.weak_password_dict = dict_new();
4095 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4096 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4097 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4098 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4100 nickserv_load_dict(str);
4101 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4102 if (nickserv && str)
4103 NickChange(nickserv, str, 0);
4104 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4105 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4106 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4107 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4108 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4109 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4110 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4111 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4112 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4113 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4114 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4115 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4116 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4117 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4118 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4119 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4120 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4121 titlehost_suffix = str ? str : "example.net";
4122 str = conf_get_data("server/network", RECDB_QSTRING);
4123 nickserv_conf.network_name = str ? str : "some IRC network";
4124 if (!nickserv_conf.auth_policer_params) {
4125 nickserv_conf.auth_policer_params = policer_params_new();
4126 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4127 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4129 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4130 for (it=dict_first(child); it; it=iter_next(it))
4131 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4135 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4137 char newnick[NICKLEN+1];
4146 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4148 case RECLAIM_SVSNICK:
4150 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4151 } while (GetUserH(newnick));
4152 irc_svsnick(nickserv, user, newnick);
4155 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4156 DelUser(user, nickserv, 1, msg);
4162 nickserv_reclaim_p(void *data) {
4163 struct userNode *user = data;
4164 struct nick_info *ni = get_nick_info(user->nick);
4166 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4170 check_user_nick(struct userNode *user) {
4171 struct nick_info *ni;
4172 user->modes &= ~FLAGS_REGNICK;
4173 if (!(ni = get_nick_info(user->nick)))
4175 if (user->handle_info == ni->owner) {
4176 user->modes |= FLAGS_REGNICK;
4180 if (nickserv_conf.warn_nick_owned)
4181 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4182 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4184 if (nickserv_conf.auto_reclaim_delay)
4185 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4187 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4191 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4193 struct handle_info *hi = NULL;
4196 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4197 if ((hi == NULL) && (serial != 0)) {
4199 inttobase64(id, serial, IDLEN);
4200 hi = dict_find(nickserv_id_dict, id, NULL);
4204 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4205 && (timestamp != hi->registered)) {
4208 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4211 set_user_handle_info(user, hi, 0);
4213 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4218 handle_nick_change(struct userNode *user, const char *old_nick)
4220 struct handle_info *hi;
4222 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4223 dict_remove(nickserv_allow_auth_dict, old_nick);
4224 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4226 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4227 check_user_nick(user);
4231 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
4233 dict_remove(nickserv_allow_auth_dict, user->nick);
4234 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4235 set_user_handle_info(user, NULL, 0);
4238 static struct modcmd *
4239 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4241 if (min_level > 0) {
4243 sprintf(buf, "%u", min_level);
4244 if (must_be_qualified) {
4245 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4247 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4249 } else if (min_level == 0) {
4250 if (must_be_qualified) {
4251 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4253 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4256 if (must_be_qualified) {
4257 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4259 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4265 nickserv_db_cleanup(void)
4267 unreg_del_user_func(nickserv_remove_user);
4268 userList_clean(&curr_helpers);
4269 policer_params_delete(nickserv_conf.auth_policer_params);
4270 dict_delete(nickserv_handle_dict);
4271 dict_delete(nickserv_nick_dict);
4272 dict_delete(nickserv_opt_dict);
4273 dict_delete(nickserv_allow_auth_dict);
4274 dict_delete(nickserv_email_dict);
4275 dict_delete(nickserv_id_dict);
4276 dict_delete(nickserv_conf.weak_password_dict);
4277 free(auth_func_list);
4278 free(unreg_func_list);
4280 free(allowauth_func_list);
4281 free(handle_merge_func_list);
4282 free(failpw_func_list);
4283 if (nickserv_conf.valid_handle_regex_set)
4284 regfree(&nickserv_conf.valid_handle_regex);
4285 if (nickserv_conf.valid_nick_regex_set)
4286 regfree(&nickserv_conf.valid_nick_regex);
4290 init_nickserv(const char *nick)
4293 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4294 reg_new_user_func(check_user_nick);
4295 reg_nick_change_func(handle_nick_change);
4296 reg_del_user_func(nickserv_remove_user);
4297 reg_account_func(handle_account);
4299 /* set up handle_inverse_flags */
4300 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4301 for (i=0; handle_flags[i]; i++) {
4302 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4303 flag_access_levels[i] = 0;
4306 conf_register_reload(nickserv_conf_read);
4307 nickserv_opt_dict = dict_new();
4308 nickserv_email_dict = dict_new();
4309 dict_set_free_keys(nickserv_email_dict, free);
4310 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4312 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4313 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4314 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4315 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4316 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4317 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4318 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4319 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4320 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4321 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4322 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4323 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4324 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4325 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4326 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4327 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4328 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4329 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4330 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4331 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4332 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4333 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4334 if (!nickserv_conf.disable_nicks) {
4335 /* nick management commands */
4336 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4337 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4338 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4339 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4340 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4341 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4343 if (nickserv_conf.email_enabled) {
4344 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4345 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4346 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4347 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4348 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4349 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4351 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4352 /* miscellaneous commands */
4353 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4354 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4355 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4356 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4357 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4358 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4360 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4361 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4362 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4363 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4364 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4365 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4366 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4367 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4368 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4369 dict_insert(nickserv_opt_dict, "WEBSITE", opt_website);
4370 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4371 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4372 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4373 if (titlehost_suffix) {
4374 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4375 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4376 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4378 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4379 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4380 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4381 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4383 nickserv_handle_dict = dict_new();
4384 dict_set_free_keys(nickserv_handle_dict, free);
4385 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4387 nickserv_id_dict = dict_new();
4388 dict_set_free_keys(nickserv_id_dict, free);
4390 nickserv_nick_dict = dict_new();
4391 dict_set_free_data(nickserv_nick_dict, free);
4393 nickserv_allow_auth_dict = dict_new();
4395 userList_init(&curr_helpers);
4398 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4399 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4400 nickserv_service = service_register(nickserv);
4402 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4403 reg_exit_func(nickserv_db_cleanup);
4404 if(nickserv_conf.handle_expire_frequency)
4405 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4406 message_register_table(msgtab);