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_MODSTAFF_LEVEL "modstaff_level"
52 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
53 #define KEY_SET_TITLE_LEVEL "set_title_level"
54 #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
55 #define KEY_SET_FAKEIDENT_LEVEL "set_fakeident_level"
56 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
57 #define KEY_FLAG_LEVELS "flag_levels"
58 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
59 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
60 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
61 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
62 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
63 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
64 #define KEY_DICT_FILE "dict_file"
65 #define KEY_NICK "nick"
66 #define KEY_LANGUAGE "language"
67 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
68 #define KEY_AUTOGAG_DURATION "autogag_duration"
69 #define KEY_AUTH_POLICER "auth_policer"
70 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
71 #define KEY_EMAIL_ENABLED "email_enabled"
72 #define KEY_EMAIL_REQUIRED "email_required"
73 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
74 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
75 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
76 #define KEY_OUNREGISTER_INACTIVE "ounregister_inactive"
77 #define KEY_OUNREGISTER_FLAGS "ounregister_flags"
78 #define KEY_HANDLE_TS_MODE "account_timestamp_mode"
79 #define KEY_MAX_AUTHLOG_LEN "max_authlog_len"
82 #define KEY_PASSWD "passwd"
83 #define KEY_NICKS "nicks"
84 #define KEY_MASKS "masks"
85 #define KEY_OPSERV_LEVEL "opserv_level"
86 #define KEY_STAFF_LEVEL "staff_level"
87 #define KEY_FLAGS "flags"
88 #define KEY_REGISTER_ON "register"
89 #define KEY_LAST_SEEN "lastseen"
90 #define KEY_INFO "info"
91 #define KEY_DEVNULL "devnull"
92 #define KEY_WEBSITE "website"
93 #define KEY_USERLIST_STYLE "user_style"
94 #define KEY_SCREEN_WIDTH "screen_width"
95 #define KEY_LAST_AUTHED_HOST "last_authed_host"
96 #define KEY_LAST_QUIT_HOST "last_quit_host"
97 #define KEY_EMAIL_ADDR "email_addr"
98 #define KEY_COOKIE "cookie"
99 #define KEY_COOKIE_DATA "data"
100 #define KEY_COOKIE_TYPE "type"
101 #define KEY_COOKIE_EXPIRES "expires"
102 #define KEY_ACTIVATION "activation"
103 #define KEY_PASSWORD_CHANGE "password change"
104 #define KEY_EMAIL_CHANGE "email change"
105 #define KEY_ALLOWAUTH "allowauth"
106 #define KEY_EPITHET "epithet"
107 #define KEY_TABLE_WIDTH "table_width"
108 #define KEY_MAXLOGINS "maxlogins"
109 #define KEY_FAKEHOST "fakehost"
110 #define KEY_FAKEIDENT "fakeident"
111 #define KEY_NOTES "notes"
112 #define KEY_NOTE_EXPIRES "expires"
113 #define KEY_NOTE_SET "set"
114 #define KEY_NOTE_SETTER "setter"
115 #define KEY_NOTE_NOTE "note"
116 #define KEY_KARMA "karma"
117 #define KEY_AUTHLOG "authlog"
118 #define KEY_AUTHLOG_LOGIN_TIME "login_time"
119 #define KEY_AUTHLOG_LOGOUT_TIME "logout_time"
120 #define KEY_AUTHLOG_HOSTMASK "hostmask"
121 #define KEY_AUTHLOG_QUIT_REASON "quit_reason"
123 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
125 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
126 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
127 typedef OPTION_FUNC(option_func_t);
129 DEFINE_LIST(handle_info_list, struct handle_info*)
131 #define NICKSERV_MIN_PARMS(N) do { \
133 reply("MSG_MISSING_PARAMS", argv[0]); \
134 svccmd_send_help(user, nickserv, cmd); \
138 struct userNode *nickserv;
139 struct userList curr_helpers;
140 const char *handle_flags = HANDLE_FLAGS;
142 static struct module *nickserv_module;
143 static struct service *nickserv_service;
144 static struct log_type *NS_LOG;
145 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
146 static dict_t nickserv_id_dict; /* contains struct handle_info* */
147 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
148 static dict_t nickserv_opt_dict; /* contains option_func_t* */
149 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
150 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
151 static char handle_inverse_flags[256];
152 static unsigned int flag_access_levels[32];
153 static const struct message_entry msgtab[] = {
154 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
155 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
156 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
157 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
158 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
159 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
160 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
161 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
162 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
163 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
164 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
165 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
166 { "NSMSG_USE_COOKIE_REGISTER", "To activate your account, you must check your email for the \"cookie\" that has been mailed to it. When you have it, use the $bcookie$b command to complete registration." },
167 { "NSMSG_USE_COOKIE_RESETPASS", "A cookie has been mailed to your account's email address. You must check your email and use the $bcookie$b command to confirm the password change." },
168 { "NSMSG_USE_COOKIE_EMAIL_1", "A cookie has been mailed to the new address you requested. To finish setting your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
169 { "NSMSG_USE_COOKIE_EMAIL_2", "A cookie has been generated, and half mailed to each your old and new addresses. To finish changing your email address, please check your email for the cookie and use the $bcookie$b command to verify." },
170 { "NSMSG_USE_COOKIE_AUTH", "A cookie has been generated and sent to your email address. Once you have checked your email and received the cookie, auth using the $bcookie$b command." },
171 { "NSMSG_COOKIE_LIVE", "Account $b%s$b already has a cookie active. Please either finish using that cookie, wait for it to expire, or auth to the account and use the $bdelcookie$b command." },
172 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
173 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
174 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
175 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
176 { "NSMSG_BAD_COOKIE", "That cookie is not the right one. Please make sure you are copying it EXACTLY from the email; it is case-sensitive, so $bABC$b is different from $babc$b." },
177 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
178 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
179 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
180 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
181 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
182 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
183 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
184 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
185 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
186 { "NSMSG_ATE_COOKIE_FOREIGN", "I ate the cookie for account $b%s$b. It may now have another." },
187 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
188 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
189 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
190 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
191 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
192 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
193 { "NSMSG_USE_AUTHCOOKIE", "Your hostmask is not valid for account $b%1$s$b. Please use the $bauthcookie$b command to grant yourself access. (/msg $S authcookie %1$s)" },
194 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
195 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
196 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
197 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
198 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
199 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
200 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
201 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
202 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
203 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
204 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
205 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
206 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
207 { "NSMSG_TITLE_TRUNCATED_RENAME", "That account name combined with the user's title would result in a truncated host; please choose a shorter account name." },
208 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
209 { "NSMSG_FAKEIDENT_INVALID", "Fake idents must be shorter than %d characters." },
210 { "NSMSG_FAKEMASK_INVALID", "Fake ident@hosts must be shorter than %d characters." },
211 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
212 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
213 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
214 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
215 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
216 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
217 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
218 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
219 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
220 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
221 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
222 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
223 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
224 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
225 { "NSMSG_HANDLEINFO_DEVNULL", " DevNull Class: %s" },
226 { "NSMSG_HANDLEINFO_WEBSITE", " Website: %s" },
227 { "NSMSG_HANDLEINFO_ACCESS", " Access: %i" },
228 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
229 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
230 { "NSMSG_HANDLEINFO_FAKEIDENT", " Fake ident: %s" },
231 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
232 { "NSMSG_HANDLEINFO_FAKEIDENTHOST", " Fake host: %s@%s" },
233 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
234 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
235 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
236 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
237 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
238 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
239 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
240 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
241 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
242 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
243 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
244 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
245 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
246 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
247 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
248 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
249 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
250 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
251 { "NSMSG_WEAK_PASSWORD", "WARNING: You are using a password that is considered weak (easy to guess). It is STRONGLY recommended you change it (now, if not sooner) by typing \"/msg $S@$s PASS oldpass newpass\" (with your current password and a new password)." },
252 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
253 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
254 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
255 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
256 { "NSMSG_AUTH_ALLOWED_MSG", "You may now authenticate to account $b%s$b by typing $b/msg $N@$s auth %s password$b (using your password). If you will be using this computer regularly, please type $b/msg $N addmask$b (AFTER you auth) to permanently add your hostmask." },
257 { "NSMSG_AUTH_ALLOWED_EMAIL", "You may also (after you auth) type $b/msg $N set email user@your.isp$b to set an email address. This will let you use the $bauthcookie$b command to be authenticated in the future." },
258 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
259 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
260 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
261 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
262 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
263 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
264 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
265 { "NSMSG_PASS_SUCCESS", "Password changed." },
266 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
267 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
268 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
269 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
270 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
271 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
272 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
273 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
274 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
275 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
276 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
277 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
278 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
279 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
280 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
281 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
282 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
283 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
284 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
285 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
286 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
287 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
288 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
289 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
290 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
291 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
292 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
293 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
294 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
295 { "NSMSG_NO_ACCESS", "Access denied." },
296 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
297 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
298 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
299 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
300 { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
301 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
302 { "NSMSG_BAD_HANDLE", "Account $b%s$b not registered because it is in use by a network service, is too long, or contains invalid characters." },
303 { "NSMSG_BAD_NICK", "Nickname $b%s$b not registered because it is in use by a network service, is too long, or contains invalid characters." },
304 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
305 { "NSMSG_FAIL_RENAME", "Account $b%s$b not renamed to $b%s$b because it is in use by a network services, or contains invalid characters." },
306 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
307 { "NSMSG_SEARCH_MATCH", "Match: %s" },
308 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
309 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
310 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
311 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
312 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
313 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
314 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
315 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
316 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
317 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
318 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
319 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
320 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
321 { "NSMSG_SET_DEVNULL", "$bDEVNULL: $b%s" },
322 { "NSMSG_SET_AUTOHIDE", "$bAUTOHIDE: $b%s" },
323 { "NSMSG_SET_WEBSITE", "$bWEBSITE: $b%s" },
324 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
325 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
326 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
327 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
328 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
329 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
330 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
331 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
332 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
333 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
334 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
335 { "NSMSG_SET_STAFFLEVEL", "$bSTAFF_LEVEL: $b%d" },
336 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
337 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
338 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
339 { "NSMSG_SET_FAKEIDENT", "$bFAKEIDENT: $b%s" },
340 { "NSMSG_SET_FAKEIDENTHOST", "$bFAKEHOST: $b%s@%s" },
341 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
342 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
343 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
344 { "NSEMAIL_ACTIVATION_BODY", "This email has been sent to verify that this email address belongs to the person who tried to register an account on %1$s. Your cookie is:\n %2$s\nTo verify your email address and complete the account registration, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nThis command is only used once to complete your account registration, and never again. Once you have run this command, you will need to authenticate everytime you reconnect to the network. To do this, you will have to type this command every time you reconnect:\n /msg %3$s@%4$s AUTH %5$s your-password\n Please remember to fill in 'your-password' with the actual password you gave to us when you registered.\n\nIf you did NOT request this account, you do not need to do anything. Please contact the %1$s staff if you have questions, and be sure to check our website." },
345 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
346 { "NSEMAIL_PASSWORD_CHANGE_BODY", "This email has been sent to verify that you wish to change the password on your account %5$s. Your cookie is %2$s.\nTo complete the password change, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request your password to be changed, you do not need to do anything. Please contact the %1$s staff if you have questions." },
347 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
348 { "NSEMAIL_EMAIL_CHANGE_BODY_NEW", "This email has been sent to verify that your email address belongs to the same person as account %5$s on %1$s. The SECOND HALF of your cookie is %2$.*6$s.\nTo verify your address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s ?????%2$.*6$s\n(Replace the ????? with the FIRST HALF of the cookie, as sent to your OLD email address.)\nIf you did NOT request this email address to be associated with this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
349 { "NSEMAIL_EMAIL_CHANGE_BODY_OLD", "This email has been sent to verify that you want to change your email for account %5$s on %1$s from this address to %7$s. The FIRST HALF of your cookie is %2$.*6$s\nTo verify your new address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$.*6$s?????\n(Replace the ????? with the SECOND HALF of the cookie, as sent to your NEW email address.)\nIf you did NOT request this change of email address, you do not need to do anything. Please contact the %1$s staff if you have questions." },
350 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
351 { "NSEMAIL_EMAIL_VERIFY_BODY", "This email has been sent to verify that this address belongs to the same person as %5$s on %1$s. Your cookie is %2$s.\nTo verify your address as associated with this account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request this email address to be associated with this account, you do not need to do anything. Please contact the %1$s staff if you have questions." },
352 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
353 { "NSEMAIL_ALLOWAUTH_BODY", "This email has been sent to let you authenticate (auth) to account %5$s on %1$s. Your cookie is %2$s.\nTo auth to that account, log on to %1$s and type the following command:\n /msg %3$s@%4$s COOKIE %5$s %2$s\nIf you did NOT request this authorization, you do not need to do anything. Please contact the %1$s staff if you have questions." },
354 { "CHECKPASS_YES", "Yes." },
355 { "CHECKPASS_NO", "No." },
356 { "CHECKEMAIL_NOT_SET", "No email set." },
357 { "CHECKEMAIL_YES", "Yes." },
358 { "CHECKEMAIL_NO", "No." },
362 enum reclaim_action {
368 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
369 static void nickserv_reclaim_p(void *data);
370 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
372 enum handle_ts_mode {
378 unsigned int disable_nicks : 1;
379 unsigned int valid_handle_regex_set : 1;
380 unsigned int valid_nick_regex_set : 1;
381 unsigned int autogag_enabled : 1;
382 unsigned int email_enabled : 1;
383 unsigned int email_required : 1;
384 unsigned int default_hostmask : 1;
385 unsigned int warn_nick_owned : 1;
386 unsigned int warn_clone_auth : 1;
387 unsigned long nicks_per_handle;
388 unsigned long password_min_length;
389 unsigned long password_min_digits;
390 unsigned long password_min_upper;
391 unsigned long password_min_lower;
392 unsigned long db_backup_frequency;
393 unsigned long handle_expire_frequency;
394 unsigned long autogag_duration;
395 unsigned long email_visible_level;
396 unsigned long cookie_timeout;
397 unsigned long handle_expire_delay;
398 unsigned long nochan_handle_expire_delay;
399 unsigned long modoper_level;
400 unsigned long modstaff_level;
401 unsigned long set_epithet_level;
402 unsigned long set_title_level;
403 unsigned long set_fakehost_level;
404 unsigned long set_fakeident_level;
405 unsigned long handles_per_email;
406 unsigned long email_search_level;
407 const char *network_name;
408 regex_t valid_handle_regex;
409 regex_t valid_nick_regex;
410 dict_t weak_password_dict;
411 struct policer_params *auth_policer_params;
412 enum reclaim_action reclaim_action;
413 enum reclaim_action auto_reclaim_action;
414 enum handle_ts_mode handle_ts_mode;
415 unsigned long auto_reclaim_delay;
416 unsigned char default_maxlogins;
417 unsigned char hard_maxlogins;
418 unsigned long ounregister_inactive;
419 unsigned long ounregister_flags;
420 unsigned int max_authlog_len;
423 struct pendingLOCUser {
424 struct handle_info *handle_info;
426 struct authlogEntry *authlog;
427 struct pendingLOCUser *next;
430 const char *titlehost_suffix = NULL;
431 static struct pendingLOCUser *pendingLOCUsers = NULL;
433 /* We have 2^32 unique account IDs to use. */
434 unsigned long int highest_id = 0;
436 #define WALK_NOTES(HANDLE, PREV, NOTE) \
437 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
438 if (NOTE->expires && NOTE->expires < now) { \
439 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
441 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
445 canonicalize_hostmask(char *mask)
447 char *out = mask, *temp;
448 if ((temp = strchr(mask, '!'))) {
450 while (*temp) *out++ = *temp++;
456 static struct handle_info *
457 register_handle(const char *handle, const char *passwd, unsigned long id)
459 struct handle_info *hi;
461 char id_base64[IDLEN + 1];
464 /* Assign a unique account ID to the account; note that 0 is
465 an invalid account ID. 1 is therefore the first account ID. */
467 id = 1 + highest_id++;
469 /* Note: highest_id is and must always be the highest ID. */
470 if (id > highest_id) {
474 inttobase64(id_base64, id, IDLEN);
476 /* Make sure an account with the same ID doesn't exist. If a
477 duplicate is found, log some details and assign a new one.
478 This should be impossible, but it never hurts to expect it. */
479 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
480 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
485 hi = calloc(1, sizeof(*hi));
486 hi->userlist_style = HI_DEFAULT_STYLE;
487 hi->handle = strdup(handle);
488 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
490 dict_insert(nickserv_handle_dict, hi->handle, hi);
495 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
501 register_nick(const char *nick, struct handle_info *owner)
503 struct nick_info *ni;
504 ni = malloc(sizeof(struct nick_info));
505 safestrncpy(ni->nick, nick, sizeof(ni->nick));
507 ni->next = owner->nicks;
509 dict_insert(nickserv_nick_dict, ni->nick, ni);
513 delete_nick(struct nick_info *ni)
515 struct nick_info *last, *next;
516 struct userNode *user;
517 /* Check to see if we should mark a user as unregistered. */
518 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
519 user->modes &= ~FLAGS_REGNICK;
522 /* Remove ni from the nick_info linked list. */
523 if (ni == ni->owner->nicks) {
524 ni->owner->nicks = ni->next;
526 last = ni->owner->nicks;
532 last->next = next->next;
534 dict_remove(nickserv_nick_dict, ni->nick);
537 static unreg_func_t *unreg_func_list;
538 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
541 reg_unreg_func(unreg_func_t func)
543 if (unreg_func_used == unreg_func_size) {
544 if (unreg_func_size) {
545 unreg_func_size <<= 1;
546 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
549 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
552 unreg_func_list[unreg_func_used++] = func;
556 nickserv_free_cookie(void *data)
558 struct handle_cookie *cookie = data;
559 if (cookie->hi) cookie->hi->cookie = NULL;
560 if (cookie->data) free(cookie->data);
565 free_handle_info(void *vhi)
567 struct handle_info *hi = vhi;
570 inttobase64(id, hi->id, IDLEN);
571 dict_remove(nickserv_id_dict, id);
573 free_string_list(hi->masks);
577 delete_nick(hi->nicks);
584 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
585 nickserv_free_cookie(hi->cookie);
588 struct handle_note *note = hi->notes;
589 hi->notes = note->next;
592 if (hi->email_addr) {
593 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
594 handle_info_list_remove(hil, hi);
596 dict_remove(nickserv_email_dict, hi->email_addr);
598 struct authlogEntry *authlog, *next;
599 for(authlog = hi->authlog; authlog; authlog = next) {
600 next = authlog->next;
601 struct pendingLOCUser *pending, *prev_pending = NULL;
602 for(pending = pendingLOCUsers; pending; pending = pending->next) {
603 if(pending->authlog == authlog) {
605 prev_pending->next = pending->next;
607 pendingLOCUsers = pending->next;
611 prev_pending = pending;
613 free((char *) authlog->hostmask);
614 if(authlog->quit_reason)
615 free((char *) authlog->quit_reason);
621 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
624 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
628 for (n=0; n<unreg_func_used; n++)
629 unreg_func_list[n](notify, hi);
631 set_user_handle_info(hi->users, NULL, 0);
633 if (nickserv_conf.disable_nicks)
634 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
636 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
638 dict_remove(nickserv_handle_dict, hi->handle);
642 get_handle_info(const char *handle)
644 return dict_find(nickserv_handle_dict, handle, 0);
648 get_nick_info(const char *nick)
650 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
654 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
659 for (nn=0; nn<channel->members.used; ++nn) {
660 mn = channel->members.list[nn];
661 if ((mn->user != except) && (mn->user->handle_info == handle))
668 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
669 if (!user->handle_info) {
671 send_message(user, bot, "MSG_AUTHENTICATE");
675 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
677 send_message(user, bot, "NSMSG_NO_ACCESS");
681 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
683 send_message(user, bot, "MSG_OPER_SUSPENDED");
687 if (user->handle_info->opserv_level < min_level) {
689 send_message(user, bot, "NSMSG_NO_ACCESS");
697 staff_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
698 if (!user->handle_info) {
700 send_message(user, bot, "MSG_AUTHENTICATE");
704 if (user->handle_info->staff_level < min_level) {
706 send_message(user, bot, "NSMSG_NO_ACCESS");
714 is_valid_handle(const char *handle)
716 struct userNode *user;
717 /* cant register a juped nick/service nick as handle, to prevent confusion */
718 user = GetUserH(handle);
719 if (user && IsLocal(user))
721 /* check against maximum length */
722 if (strlen(handle) > NICKSERV_HANDLE_LEN)
724 /* for consistency, only allow account names that could be nicks */
725 if (!is_valid_nick(handle))
727 /* disallow account names that look like bad words */
728 if (opserv_bad_channel(handle))
730 /* test either regex or containing all valid chars */
731 if (nickserv_conf.valid_handle_regex_set) {
732 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
735 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
736 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
740 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
745 is_registerable_nick(const char *nick)
747 /* make sure it could be used as an account name */
748 if (!is_valid_handle(nick))
751 if (strlen(nick) > NICKLEN)
753 /* test either regex or as valid handle */
754 if (nickserv_conf.valid_nick_regex_set) {
755 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
758 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
759 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
767 is_valid_email_addr(const char *org_email)
769 char email[strlen(org_email)+1];
770 strcpy(email, org_email);
771 //validate email address
772 //1st check: there need to be one @
773 char *p1 = strchr(email, '@');
774 if(!p1 || strchr(p1+1, '@')) return 0;
776 //2nd check: username (bevore @) must be at least 1 char long and out of part_chars
777 char *part_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._%+-";
779 if(p1 - email == 0) return 0;
780 for(i = 0; i < (p1 - email); i++) {
781 if(!strchr(part_chars, email[i])) return 0;
783 //3rd check: there need to be at least 1 dot in the domain part and all characters out of part_chars
784 part_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-";
793 } else if(!strchr(part_chars, *p1))
800 //4th check: TLD must be <= 5 chars, no special chars
815 visible_email_addr(struct userNode *user, struct handle_info *hi)
817 if (hi->email_addr) {
818 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
819 return hi->email_addr;
829 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
831 struct handle_info *hi;
832 struct userNode *target;
836 if (!(hi = get_handle_info(++name))) {
837 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
842 if (!(target = GetUserH(name))) {
843 send_message(user, service, "MSG_NICK_UNKNOWN", name);
846 if (IsLocal(target)) {
847 if (IsService(target))
848 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
850 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
853 if (!(hi = target->handle_info)) {
854 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
862 oper_outranks(struct userNode *user, struct handle_info *hi) {
863 if (user->handle_info->opserv_level > hi->opserv_level)
865 if (user->handle_info->opserv_level == hi->opserv_level) {
866 if ((user->handle_info->opserv_level == 1000)
867 || (user->handle_info == hi)
868 || ((user->handle_info->opserv_level == 0)
869 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
870 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
874 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
878 static struct handle_info *
879 get_victim_oper(struct userNode *user, const char *target)
881 struct handle_info *hi;
882 if (!(hi = smart_get_handle_info(nickserv, user, target)))
884 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
885 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
888 return oper_outranks(user, hi) ? hi : NULL;
892 valid_user_for(struct userNode *user, struct handle_info *hi)
896 /* If no hostmasks on the account, allow it. */
897 if (!hi->masks->used || IsDummy(user))
899 /* If any hostmask matches, allow it. */
900 for (ii=0; ii<hi->masks->used; ii++)
901 if (user_matches_glob(user, hi->masks->list[ii], 0))
903 /* If they are allowauthed to this account, allow it (removing the aa). */
904 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
905 dict_remove(nickserv_allow_auth_dict, user->nick);
908 /* The user is not allowed to use this account. */
913 is_secure_password(const char *handle, const char *pass, struct userNode *user)
916 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
920 if (len < nickserv_conf.password_min_length) {
922 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
925 if (!irccasecmp(pass, handle)) {
927 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
930 dict_find(nickserv_conf.weak_password_dict, pass, &p);
933 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
936 for (i=0; i<len; i++) {
937 if (isdigit(pass[i]))
939 if (isupper(pass[i]))
941 if (islower(pass[i]))
944 if ((cnt_lower < nickserv_conf.password_min_lower)
945 || (cnt_upper < nickserv_conf.password_min_upper)
946 || (cnt_digits < nickserv_conf.password_min_digits)) {
948 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
954 static auth_func_t *auth_func_list;
955 static unsigned int auth_func_size = 0, auth_func_used = 0;
958 reg_auth_func(auth_func_t func)
960 if (auth_func_used == auth_func_size) {
961 if (auth_func_size) {
962 auth_func_size <<= 1;
963 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
966 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
969 auth_func_list[auth_func_used++] = func;
972 static handle_rename_func_t *rf_list;
973 static unsigned int rf_list_size, rf_list_used;
976 reg_handle_rename_func(handle_rename_func_t func)
978 if (rf_list_used == rf_list_size) {
981 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
984 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
987 rf_list[rf_list_used++] = func;
991 generate_fakehost(struct handle_info *handle)
993 extern const char *hidden_host_suffix;
994 static char buffer[HOSTLEN+1];
996 if (!handle->fakehost) {
997 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
999 } else if (handle->fakehost[0] == '.') {
1000 /* A leading dot indicates the stored value is actually a title. */
1001 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, titlehost_suffix);
1003 } else if (handle->fakehost[0] == '$') {
1004 /* A leading $ indicates the stored value begins with the user handle. */
1005 snprintf(buffer, sizeof(buffer), "%s%s", handle->handle, handle->fakehost+1);
1008 return handle->fakehost;
1012 generate_fakeident(struct handle_info *handle, struct userNode *user)
1014 static char buffer[USERLEN+1];
1016 if (!handle->fakeident) {
1019 safestrncpy(buffer, user->ident, sizeof(buffer));
1022 return handle->fakeident;
1026 apply_fakehost(struct handle_info *handle, struct userNode *user)
1028 struct userNode *target;
1029 char *fakehost, *fakeident;
1034 fakehost = generate_fakehost(handle);
1037 fakeident = generate_fakeident(handle, user);
1038 assign_fakehost(user, fakehost, fakeident, 0, 1);
1042 for (target = handle->users; target; target = target->next_authed) {
1043 fakeident = generate_fakeident(handle, target);
1044 assign_fakehost(target, fakehost, fakeident, 0, 1);
1049 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
1052 struct handle_info *old_info;
1054 /* This can happen if somebody uses COOKIE while authed, or if
1055 * they re-auth to their current handle (which is silly, but users
1056 * are like that). */
1057 if (user->handle_info == hi)
1060 if (user->handle_info) {
1061 struct userNode *other;
1064 userList_remove(&curr_helpers, user);
1066 /* remove from next_authed linked list */
1067 if (user->handle_info->users == user) {
1068 user->handle_info->users = user->next_authed;
1069 } else if (user->handle_info->users != NULL) {
1070 for (other = user->handle_info->users;
1071 other->next_authed != user;
1072 other = other->next_authed) ;
1073 other->next_authed = user->next_authed;
1075 /* No users authed to the account - can happen if they get
1076 * killed for authing. */
1078 /* if nobody left on old handle, and they're not an oper, remove !god */
1079 if (!user->handle_info->users && !user->handle_info->opserv_level)
1080 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
1081 /* record them as being last seen at this time */
1082 user->handle_info->lastseen = now;
1083 /* and record their hostmask */
1084 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
1086 old_info = user->handle_info;
1087 user->handle_info = hi;
1088 if (hi && !hi->users && !hi->opserv_level)
1089 HANDLE_CLEAR_FLAG(hi, HELPING);
1090 for (n=0; n<auth_func_used; n++) {
1091 auth_func_list[n](user, old_info);
1096 struct nick_info *ni;
1098 HANDLE_CLEAR_FLAG(hi, FROZEN);
1099 if (nickserv_conf.warn_clone_auth) {
1100 struct userNode *other;
1101 for (other = hi->users; other; other = other->next_authed)
1102 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
1104 user->next_authed = hi->users;
1107 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
1108 userList_append(&curr_helpers, user);
1110 if (hi->fakehost || hi->fakeident || old_info)
1111 apply_fakehost(hi, user);
1114 if (!nickserv_conf.disable_nicks) {
1115 struct nick_info *ni2;
1116 for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
1117 if (!irccasecmp(user->nick, ni2->nick)) {
1118 user->modes |= FLAGS_REGNICK;
1123 StampUser(user, hi->handle, hi->registered, hi->id);
1126 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
1127 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
1129 /* We cannot clear the user's account ID, unfortunately. */
1130 user->next_authed = NULL;
1134 static struct handle_info*
1135 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
1137 struct handle_info *hi;
1138 struct nick_info *ni;
1139 char crypted[MD5_CRYPT_LENGTH];
1141 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
1142 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
1146 if (!is_secure_password(handle, passwd, user))
1149 cryptpass(passwd, crypted);
1150 hi = register_handle(handle, crypted, 0);
1151 hi->masks = alloc_string_list(1);
1153 hi->language = lang_C;
1154 hi->registered = now;
1156 hi->flags = HI_DEFAULT_FLAGS;
1157 if (settee && !no_auth)
1158 set_user_handle_info(settee, hi, 1);
1161 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1162 else if (nickserv_conf.disable_nicks)
1163 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1164 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1165 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1167 register_nick(user->nick, hi);
1168 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1170 if (settee && (user != settee))
1171 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1176 nickserv_bake_cookie(struct handle_cookie *cookie)
1178 cookie->hi->cookie = cookie;
1179 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1183 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1185 struct handle_cookie *cookie;
1186 char subject[128], body[4096], *misc;
1187 const char *netname, *fmt;
1191 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1195 cookie = calloc(1, sizeof(*cookie));
1197 cookie->type = type;
1198 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1199 cookie->expires = now + nickserv_conf.cookie_timeout;
1200 inttobase64(cookie->cookie, rand(), 5);
1201 inttobase64(cookie->cookie+5, rand(), 5);
1203 netname = nickserv_conf.network_name;
1206 switch (cookie->type) {
1208 hi->passwd[0] = 0; /* invalidate password */
1209 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1210 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1211 snprintf(subject, sizeof(subject), fmt, netname);
1212 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1213 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1216 case PASSWORD_CHANGE:
1217 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1218 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1219 snprintf(subject, sizeof(subject), fmt, netname);
1220 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1221 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1224 misc = hi->email_addr;
1225 hi->email_addr = cookie->data;
1227 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1228 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1229 snprintf(subject, sizeof(subject), fmt, netname);
1230 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1231 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1232 mail_send(nickserv, hi, subject, body, 1);
1233 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1234 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1236 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1237 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1238 snprintf(subject, sizeof(subject), fmt, netname);
1239 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1240 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1241 mail_send(nickserv, hi, subject, body, 1);
1244 hi->email_addr = misc;
1247 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1248 snprintf(subject, sizeof(subject), fmt, netname);
1249 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1250 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1251 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1254 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1258 mail_send(nickserv, hi, subject, body, first_time);
1259 nickserv_bake_cookie(cookie);
1263 nickserv_eat_cookie(struct handle_cookie *cookie)
1265 cookie->hi->cookie = NULL;
1266 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1267 nickserv_free_cookie(cookie);
1271 nickserv_free_email_addr(void *data)
1273 handle_info_list_clean(data);
1278 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1280 struct handle_info_list *hil;
1281 /* Remove from old handle_info_list ... */
1282 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1283 handle_info_list_remove(hil, hi);
1284 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1285 hi->email_addr = NULL;
1287 /* Add to the new list.. */
1288 if (new_email_addr) {
1289 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1290 hil = calloc(1, sizeof(*hil));
1291 hil->tag = strdup(new_email_addr);
1292 handle_info_list_init(hil);
1293 dict_insert(nickserv_email_dict, hil->tag, hil);
1295 handle_info_list_append(hil, hi);
1296 hi->email_addr = hil->tag;
1300 static NICKSERV_FUNC(cmd_register)
1303 struct handle_info *hi;
1304 const char *email_addr, *password;
1307 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1308 /* Require the first handle registered to belong to someone +o. */
1309 reply("NSMSG_REQUIRE_OPER");
1313 if (user->handle_info) {
1314 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1318 if (IsRegistering(user)) {
1319 reply("NSMSG_ALREADY_REGISTERING");
1323 if (IsStamped(user)) {
1324 /* Unauthenticated users might still have been stamped
1325 previously and could therefore have a hidden host;
1326 do not allow them to register a new account. */
1327 reply("NSMSG_STAMPED_REGISTER");
1331 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1333 if (!is_valid_handle(argv[1])) {
1334 reply("NSMSG_BAD_HANDLE", argv[1]);
1338 if ((argc >= 4) && nickserv_conf.email_enabled) {
1339 struct handle_info_list *hil;
1342 /* Remember email address. */
1343 email_addr = argv[3];
1345 /* Check that the email address looks valid.. */
1346 if (!is_valid_email_addr(email_addr)) {
1347 reply("NSMSG_BAD_EMAIL_ADDR");
1351 /* .. and that we are allowed to send to it. */
1352 if ((str = mail_prohibited_address(email_addr))) {
1353 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1357 /* If we do email verify, make sure we don't spam the address. */
1358 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1360 for (nn=0; nn<hil->used; nn++) {
1361 if (hil->list[nn]->cookie) {
1362 reply("NSMSG_EMAIL_UNACTIVATED");
1366 if (hil->used >= nickserv_conf.handles_per_email) {
1367 reply("NSMSG_EMAIL_OVERUSED");
1380 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1382 /* Add any masks they should get. */
1383 if (nickserv_conf.default_hostmask) {
1384 nickserv_addmask(NULL, hi, strdup("*@*"));
1386 nickserv_addmask(NULL, hi, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1387 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1388 nickserv_addmask(NULL, hi, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1391 /* If they're the first to register, give them level 1000. */
1392 if (dict_size(nickserv_handle_dict) == 1) {
1393 hi->opserv_level = 1000;
1394 hi->staff_level = 1000;
1395 reply("NSMSG_ROOT_HANDLE", argv[1]);
1398 /* Set their email address. */
1400 nickserv_set_email_addr(hi, email_addr);
1402 /* If they need to do email verification, tell them. */
1404 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1406 /* Set registering flag.. */
1407 user->modes |= FLAGS_REGISTERING;
1412 static NICKSERV_FUNC(cmd_oregister)
1415 struct userNode *settee;
1416 struct handle_info *hi;
1417 const char *pass, *email;
1419 NICKSERV_MIN_PARMS(3);
1424 if (!is_valid_handle(argv[1])) {
1425 reply("NSMSG_BAD_HANDLE", argv[1]);
1429 if (argc < 5 || !nickserv_conf.email_enabled) {
1434 if (!is_valid_email_addr(email)) {
1435 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
1438 if ((str = mail_prohibited_address(email))) {
1439 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", email, str);
1444 if (argc < 4 || !strcmp(argv[3], "*")) {
1447 } else if (strchr(argv[3], '@')) {
1448 mask = canonicalize_hostmask(strdup(argv[3]));
1450 settee = GetUserH(argv[4]);
1452 reply("MSG_NICK_UNKNOWN", argv[4]);
1459 } else if ((settee = GetUserH(argv[3]))) {
1460 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1462 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1465 if (settee && settee->handle_info) {
1466 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1470 if (!(hi = nickserv_register(user, settee, argv[1], pass, 0))) {
1475 string_list_append(hi->masks, mask);
1477 nickserv_set_email_addr(hi, email);
1481 static NICKSERV_FUNC(cmd_handleinfo)
1484 unsigned int i, pos=0, herelen;
1485 struct userNode *target, *next_un;
1486 struct handle_info *hi;
1487 const char *nsmsg_none;
1491 if (!(hi = user->handle_info)) {
1492 reply("NSMSG_MUST_AUTH");
1495 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1499 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1500 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1501 feh = hi->registered;
1502 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1505 intervalString(buff, now - hi->lastseen, user->handle_info);
1506 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1508 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1511 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1512 if ((oper_has_access(user, cmd->parent->bot, 200, 1)) || IsNetworkHelper(user)) {
1513 struct devnull_class *devnull_c = (hi->devnull_id ? devnull_find_id(hi->devnull_id) : NULL);
1514 reply("NSMSG_HANDLEINFO_DEVNULL", (devnull_c ? devnull_c->name : nsmsg_none));
1516 if (user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1517 reply("NSMSG_HANDLEINFO_WEBSITE", (hi->website ? hi->website : nsmsg_none));
1518 if(hi->opserv_level > 0 && user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1519 reply("NSMSG_HANDLEINFO_ACCESS", hi->opserv_level);
1520 if (HANDLE_FLAGGED(hi, FROZEN))
1521 reply("NSMSG_HANDLEINFO_VACATION");
1523 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1524 struct do_not_register *dnr;
1525 if ((dnr = chanserv_is_dnr(NULL, hi)))
1526 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1527 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1529 } else if (hi != user->handle_info)
1533 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1535 if (nickserv_conf.email_enabled)
1536 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1540 switch (hi->cookie->type) {
1541 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1542 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1543 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1544 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1545 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1550 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1551 reply("NSMSG_HANDLEINFO_ID", hi->id);
1553 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1555 reply("NSMSG_HANDLEINFO_NO_NOTES");
1557 struct handle_note *prev, *note;
1559 WALK_NOTES(hi, prev, note) {
1560 char set_time[INTERVALLEN];
1561 intervalString(set_time, now - note->set, user->handle_info);
1562 if (note->expires) {
1563 char exp_time[INTERVALLEN];
1564 intervalString(exp_time, note->expires - now, user->handle_info);
1565 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1567 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1574 unsigned long flen = 1;
1575 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1577 for (i=0, flen=1; handle_flags[i]; i++)
1578 if (hi->flags & 1 << i)
1579 flags[flen++] = handle_flags[i];
1581 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1583 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1586 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1587 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1588 || (hi->opserv_level > 0)) {
1589 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1592 if (hi->fakeident && hi->fakehost)
1593 reply("NSMSG_HANDLEINFO_FAKEIDENTHOST", hi->fakeident, hi->fakehost);
1594 else if (hi->fakeident)
1595 reply("NSMSG_HANDLEINFO_FAKEIDENT", hi->fakeident);
1596 else if (hi->fakehost)
1597 reply("NSMSG_HANDLEINFO_FAKEHOST", hi->fakehost);
1599 if (hi->last_quit_host[0])
1600 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1602 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1604 if (nickserv_conf.disable_nicks) {
1605 /* nicks disabled; don't show anything about registered nicks */
1606 } else if (hi->nicks) {
1607 struct nick_info *ni, *next_ni;
1608 for (ni = hi->nicks; ni; ni = next_ni) {
1609 herelen = strlen(ni->nick);
1610 if (pos + herelen + 1 > ArrayLength(buff)) {
1612 goto print_nicks_buff;
1616 memcpy(buff+pos, ni->nick, herelen);
1617 pos += herelen; buff[pos++] = ' ';
1621 reply("NSMSG_HANDLEINFO_NICKS", buff);
1626 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1629 if (hi->masks->used) {
1630 for (i=0; i < hi->masks->used; i++) {
1631 herelen = strlen(hi->masks->list[i]);
1632 if (pos + herelen + 1 > ArrayLength(buff)) {
1634 goto print_mask_buff;
1636 memcpy(buff+pos, hi->masks->list[i], herelen);
1637 pos += herelen; buff[pos++] = ' ';
1638 if (i+1 == hi->masks->used) {
1641 reply("NSMSG_HANDLEINFO_MASKS", buff);
1646 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1650 struct userData *chan, *next;
1653 for (chan = hi->channels; chan; chan = next) {
1654 next = chan->u_next;
1655 name = chan->channel->channel->name;
1656 herelen = strlen(name);
1657 if (pos + herelen + 7 > ArrayLength(buff)) {
1659 goto print_chans_buff;
1661 if (IsUserSuspended(chan))
1663 pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
1667 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1672 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1675 for (target = hi->users; target; target = next_un) {
1676 herelen = strlen(target->nick);
1677 if (pos + herelen + 1 > ArrayLength(buff)) {
1679 goto print_cnick_buff;
1681 next_un = target->next_authed;
1683 memcpy(buff+pos, target->nick, herelen);
1684 pos += herelen; buff[pos++] = ' ';
1688 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1693 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1696 static NICKSERV_FUNC(cmd_userinfo)
1698 struct userNode *target;
1700 NICKSERV_MIN_PARMS(2);
1701 if (!(target = GetUserH(argv[1]))) {
1702 reply("MSG_NICK_UNKNOWN", argv[1]);
1705 if (target->handle_info)
1706 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1708 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1712 static NICKSERV_FUNC(cmd_nickinfo)
1714 struct nick_info *ni;
1716 NICKSERV_MIN_PARMS(2);
1717 if (!(ni = get_nick_info(argv[1]))) {
1718 reply("MSG_NICK_UNKNOWN", argv[1]);
1721 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1725 static NICKSERV_FUNC(cmd_notes)
1727 struct handle_info *hi;
1728 struct handle_note *prev, *note;
1731 NICKSERV_MIN_PARMS(2);
1732 if (!(hi = get_victim_oper(user, argv[1])))
1735 WALK_NOTES(hi, prev, note) {
1736 char set_time[INTERVALLEN];
1737 intervalString(set_time, now - note->set, user->handle_info);
1738 if (note->expires) {
1739 char exp_time[INTERVALLEN];
1740 intervalString(exp_time, note->expires - now, user->handle_info);
1741 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1743 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1747 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1751 static NICKSERV_FUNC(cmd_rename_handle)
1753 struct handle_info *hi;
1754 char msgbuf[MAXLEN], *old_handle;
1757 NICKSERV_MIN_PARMS(3);
1758 if (!(hi = get_victim_oper(user, argv[1])))
1760 if (!is_valid_handle(argv[2])) {
1761 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1764 if (get_handle_info(argv[2])) {
1765 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1768 if (hi->fakehost && hi->fakehost[0] == '.' &&
1769 (strlen(argv[2]) + strlen(hi->fakehost+1) +
1770 strlen(titlehost_suffix) + 2) > HOSTLEN) {
1771 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED_RENAME");
1775 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1776 hi->handle = strdup(argv[2]);
1777 dict_insert(nickserv_handle_dict, hi->handle, hi);
1778 for (nn=0; nn<rf_list_used; nn++)
1779 rf_list[nn](hi, old_handle);
1780 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1781 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1782 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1784 apply_fakehost(hi, NULL);
1788 static failpw_func_t *failpw_func_list;
1789 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1792 reg_failpw_func(failpw_func_t func)
1794 if (failpw_func_used == failpw_func_size) {
1795 if (failpw_func_size) {
1796 failpw_func_size <<= 1;
1797 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1799 failpw_func_size = 8;
1800 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1803 failpw_func_list[failpw_func_used++] = func;
1806 static struct authlogEntry *authlog_add(struct handle_info *hi, struct userNode *user, const char *mask) {
1807 if(!hi || (!user && !mask)) return NULL;
1809 mask = generate_hostmask(user, GENMASK_USENICK|GENMASK_STRICT_IDENT|GENMASK_NO_HIDING|GENMASK_STRICT_HOST);
1810 struct authlogEntry *authlog, *next, *prev = NULL;
1811 authlog = malloc(sizeof(*authlog));
1812 authlog->login_time = now;
1813 authlog->logout_time = 0;
1814 authlog->hostmask = mask;
1815 authlog->quit_reason = NULL;
1816 authlog->user = user;
1817 authlog->next = hi->authlog;
1818 hi->authlog = authlog;
1820 for(authlog = hi->authlog; authlog; authlog = next) {
1822 next = authlog->next;
1823 if(i > nickserv_conf.max_authlog_len) {
1824 struct pendingLOCUser *pending, *prev_pending = NULL;
1825 for(pending = pendingLOCUsers; pending; pending = pending->next) {
1826 if(pending->authlog == authlog) {
1828 prev_pending->next = pending->next;
1830 pendingLOCUsers = pending->next;
1834 prev_pending = pending;
1836 free((char *) authlog->hostmask);
1837 if(authlog->quit_reason)
1838 free((char *) authlog->quit_reason);
1840 prev->next = authlog->next;
1842 hi->authlog = authlog->next;
1850 static NICKSERV_FUNC(cmd_auth)
1852 int pw_arg, used, maxlogins;
1853 struct handle_info *hi;
1855 struct userNode *other;
1857 if (user->handle_info) {
1858 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1861 if (IsStamped(user)) {
1862 /* Unauthenticated users might still have been stamped
1863 previously and could therefore have a hidden host;
1864 do not allow them to authenticate. */
1865 reply("NSMSG_STAMPED_AUTH");
1869 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1871 } else if (argc == 2) {
1872 if (nickserv_conf.disable_nicks) {
1873 if (!(hi = get_handle_info(user->nick))) {
1874 reply("NSMSG_HANDLE_NOT_FOUND");
1878 /* try to look up their handle from their nick */
1879 struct nick_info *ni;
1880 ni = get_nick_info(user->nick);
1882 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1889 reply("MSG_MISSING_PARAMS", argv[0]);
1890 svccmd_send_help(user, nickserv, cmd);
1894 reply("NSMSG_HANDLE_NOT_FOUND");
1897 /* Responses from here on look up the language used by the handle they asked about. */
1898 passwd = argv[pw_arg];
1899 if (!valid_user_for(user, hi)) {
1900 if (hi->email_addr && nickserv_conf.email_enabled)
1901 send_message_type(4, user, cmd->parent->bot,
1902 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1905 send_message_type(4, user, cmd->parent->bot,
1906 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1908 argv[pw_arg] = "BADMASK";
1911 if (!checkpass(passwd, hi->passwd)) {
1913 send_message_type(4, user, cmd->parent->bot,
1914 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1915 argv[pw_arg] = "BADPASS";
1916 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1917 if (nickserv_conf.autogag_enabled) {
1918 if (!user->auth_policer.params) {
1919 user->auth_policer.last_req = now;
1920 user->auth_policer.params = nickserv_conf.auth_policer_params;
1922 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1924 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1925 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1926 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1928 argv[pw_arg] = "GAGGED";
1933 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1934 send_message_type(4, user, cmd->parent->bot,
1935 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1936 argv[pw_arg] = "SUSPENDED";
1939 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1940 for (used = 0, other = hi->users; other; other = other->next_authed) {
1941 if (++used >= maxlogins) {
1942 send_message_type(4, user, cmd->parent->bot,
1943 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1945 argv[pw_arg] = "MAXLOGINS";
1949 if (HANDLE_FLAGGED(hi, AUTOHIDE)) {
1950 //ok we have a fakehost set... but we need to set mode +x
1951 irc_svsmode(nickserv,user,"+x");
1954 set_user_handle_info(user, hi, 1);
1955 if (nickserv_conf.email_required && !hi->email_addr)
1956 reply("NSMSG_PLEASE_SET_EMAIL");
1957 if (!is_secure_password(hi->handle, passwd, NULL))
1958 reply("NSMSG_WEAK_PASSWORD");
1959 if (hi->passwd[0] != '$')
1960 cryptpass(passwd, hi->passwd);
1961 if (!hi->masks->used) {
1963 nickserv_addmask(NULL, hi, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1964 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1965 nickserv_addmask(NULL, hi, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1967 authlog_add(hi, user, NULL);
1968 argv[pw_arg] = "****";
1969 reply("NSMSG_AUTH_SUCCESS");
1973 struct handle_info *checklogin(const char *user, const char *pass, UNUSED_ARG(const char *numeric), const char *hostmask, const char *ipmask)
1975 struct handle_info *hi;
1976 unsigned int match = 0, ii = 0;
1977 hi = dict_find(nickserv_handle_dict, user, NULL);
1980 /* If no hostmasks on the account, allow it. */
1981 if (hi->masks->used) {
1982 /* If any hostmask matches, allow it. */
1983 for (ii=0; ii<hi->masks->used; ii++)
1984 if (match_ircglob(hostmask, hi->masks->list[ii]) || match_ircglob(ipmask, hi->masks->list[ii])) {
1991 if(!checkpass(pass, hi->passwd))
1993 if (HANDLE_FLAGGED(hi, SUSPENDED))
1995 char *ptr = malloc(strlen(hostmask)+1);
1996 strcpy(ptr, hostmask);
1997 struct authlogEntry *authlog = authlog_add(hi, NULL, ptr);
1998 struct pendingLOCUser *pending;
1999 if(authlog && (pending = malloc(sizeof(*pending)))) {
2000 pending->handle_info = hi;
2001 pending->time = now;
2002 pending->authlog = authlog;
2003 pending->next = pendingLOCUsers;
2004 pendingLOCUsers = pending;
2009 char *getfakehost(const char *user)
2011 struct handle_info *hi;
2012 hi = dict_find(nickserv_handle_dict, user, NULL);
2015 return generate_fakehost(hi);
2018 static allowauth_func_t *allowauth_func_list;
2019 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
2022 reg_allowauth_func(allowauth_func_t func)
2024 if (allowauth_func_used == allowauth_func_size) {
2025 if (allowauth_func_size) {
2026 allowauth_func_size <<= 1;
2027 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
2029 allowauth_func_size = 8;
2030 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
2033 allowauth_func_list[allowauth_func_used++] = func;
2036 static int cmd_authlog_func(struct userNode *user, struct svccmd *cmd, struct handle_info *hi);
2038 static MODCMD_FUNC(cmd_authlog)
2040 return cmd_authlog_func(user, cmd, user->handle_info);
2043 static MODCMD_FUNC(cmd_oauthlog) {
2044 struct handle_info *hi;
2046 NICKSERV_MIN_PARMS(2);
2048 if (!(hi = get_victim_oper(user, argv[1])))
2051 return cmd_authlog_func(user, cmd, hi);
2054 static int cmd_authlog_func(struct userNode *user, struct svccmd *cmd, struct handle_info *hi) {
2055 struct helpfile_table tbl;
2056 struct authlogEntry *authlog;
2059 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
2066 tbl.flags = TABLE_NO_FREE;
2067 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
2068 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
2069 tbl.contents[0][0] = "Hostmask";
2070 tbl.contents[0][1] = "Login";
2071 tbl.contents[0][2] = "Logout";
2072 tbl.contents[0][3] = "Quit Reason";
2075 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
2077 free(tbl.contents[0]);
2083 char intervalBuf[INTERVALLEN];
2085 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
2086 tbl.contents[++i] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
2087 tbl.contents[i][0] = authlog->hostmask;
2088 str = intervalString(intervalBuf, now - authlog->login_time, hi);
2089 ptr = malloc(strlen(str)+1);
2091 tbl.contents[i][1] = ptr;
2092 if(authlog->logout_time)
2093 str = intervalString(intervalBuf, now - authlog->logout_time, hi);
2094 else if(!authlog->user)
2097 sprintf(intervalBuf, "Never (%s)", authlog->user->nick);
2100 ptr = malloc(strlen(str)+1);
2102 tbl.contents[i][2] = ptr;
2103 tbl.contents[i][3] = (authlog->quit_reason ? authlog->quit_reason : "-");
2106 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
2107 for(i = 1; i < tbl.length; ++i)
2109 free((char *) tbl.contents[i][1]);
2110 free((char *) tbl.contents[i][2]);
2111 free(tbl.contents[i]);
2113 free(tbl.contents[0]);
2119 static NICKSERV_FUNC(cmd_allowauth)
2121 struct userNode *target;
2122 struct handle_info *hi;
2125 NICKSERV_MIN_PARMS(2);
2126 if (!(target = GetUserH(argv[1]))) {
2127 reply("MSG_NICK_UNKNOWN", argv[1]);
2130 if (target->handle_info) {
2131 reply("NSMSG_USER_PREV_AUTH", target->nick);
2134 if (IsStamped(target)) {
2135 /* Unauthenticated users might still have been stamped
2136 previously and could therefore have a hidden host;
2137 do not allow them to authenticate to an account. */
2138 reply("NSMSG_USER_PREV_STAMP", target->nick);
2143 else if (!(hi = get_handle_info(argv[2]))) {
2144 reply("MSG_HANDLE_UNKNOWN", argv[2]);
2148 if (hi->opserv_level > user->handle_info->opserv_level) {
2149 reply("MSG_USER_OUTRANKED", hi->handle);
2152 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
2153 || (hi->opserv_level > 0))
2154 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
2155 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
2158 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
2159 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
2160 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
2161 if (nickserv_conf.email_enabled)
2162 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
2164 if (dict_remove(nickserv_allow_auth_dict, target->nick))
2165 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
2167 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
2169 for (n=0; n<allowauth_func_used; n++)
2170 allowauth_func_list[n](user, target, hi);
2174 static NICKSERV_FUNC(cmd_authcookie)
2176 struct handle_info *hi;
2178 NICKSERV_MIN_PARMS(2);
2179 if (user->handle_info) {
2180 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2183 if (IsStamped(user)) {
2184 /* Unauthenticated users might still have been stamped
2185 previously and could therefore have a hidden host;
2186 do not allow them to authenticate to an account. */
2187 reply("NSMSG_STAMPED_AUTHCOOKIE");
2190 if (!(hi = get_handle_info(argv[1]))) {
2191 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2194 if (!hi->email_addr) {
2195 reply("MSG_SET_EMAIL_ADDR");
2198 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
2202 static NICKSERV_FUNC(cmd_delcookie)
2204 struct handle_info *hi;
2206 hi = user->handle_info;
2208 reply("NSMSG_NO_COOKIE");
2211 switch (hi->cookie->type) {
2214 reply("NSMSG_MUST_TIME_OUT");
2217 nickserv_eat_cookie(hi->cookie);
2218 reply("NSMSG_ATE_COOKIE");
2224 static NICKSERV_FUNC(cmd_odelcookie)
2226 struct handle_info *hi;
2228 NICKSERV_MIN_PARMS(2);
2230 if (!(hi = get_victim_oper(user, argv[1])))
2234 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
2238 nickserv_eat_cookie(hi->cookie);
2239 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
2244 static NICKSERV_FUNC(cmd_resetpass)
2246 struct handle_info *hi;
2247 char crypted[MD5_CRYPT_LENGTH];
2249 NICKSERV_MIN_PARMS(3);
2250 if (user->handle_info) {
2251 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2254 if (IsStamped(user)) {
2255 /* Unauthenticated users might still have been stamped
2256 previously and could therefore have a hidden host;
2257 do not allow them to activate an account. */
2258 reply("NSMSG_STAMPED_RESETPASS");
2261 if (!(hi = get_handle_info(argv[1]))) {
2262 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2265 if (!hi->email_addr) {
2266 reply("MSG_SET_EMAIL_ADDR");
2269 cryptpass(argv[2], crypted);
2271 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
2275 static NICKSERV_FUNC(cmd_cookie)
2277 struct handle_info *hi;
2280 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
2283 NICKSERV_MIN_PARMS(3);
2284 if (!(hi = get_handle_info(argv[1]))) {
2285 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2291 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2292 reply("NSMSG_HANDLE_SUSPENDED");
2297 reply("NSMSG_NO_COOKIE");
2301 /* Check validity of operation before comparing cookie to
2302 * prohibit guessing by authed users. */
2303 if (user->handle_info
2304 && (hi->cookie->type != EMAIL_CHANGE)
2305 && (hi->cookie->type != PASSWORD_CHANGE)) {
2306 reply("NSMSG_CANNOT_COOKIE");
2310 if (strcmp(cookie, hi->cookie->cookie)) {
2311 reply("NSMSG_BAD_COOKIE");
2315 switch (hi->cookie->type) {
2317 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2318 set_user_handle_info(user, hi, 1);
2319 reply("NSMSG_HANDLE_ACTIVATED");
2321 case PASSWORD_CHANGE:
2322 set_user_handle_info(user, hi, 1);
2323 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2324 reply("NSMSG_PASSWORD_CHANGED");
2327 nickserv_set_email_addr(hi, hi->cookie->data);
2328 reply("NSMSG_EMAIL_CHANGED");
2331 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2332 set_user_handle_info(user, hi, 1);
2333 nickserv_addmask(user, hi, mask);
2334 reply("NSMSG_AUTH_SUCCESS");
2339 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2340 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2344 nickserv_eat_cookie(hi->cookie);
2349 static NICKSERV_FUNC(cmd_oregnick) {
2351 struct handle_info *target;
2352 struct nick_info *ni;
2354 NICKSERV_MIN_PARMS(3);
2355 if (!(target = modcmd_get_handle_info(user, argv[1])))
2358 if (!is_registerable_nick(nick)) {
2359 reply("NSMSG_BAD_NICK", nick);
2362 ni = dict_find(nickserv_nick_dict, nick, NULL);
2364 reply("NSMSG_NICK_EXISTS", nick);
2367 register_nick(nick, target);
2368 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2372 static NICKSERV_FUNC(cmd_regnick) {
2374 struct nick_info *ni;
2376 if (!is_registerable_nick(user->nick)) {
2377 reply("NSMSG_BAD_NICK", user->nick);
2380 /* count their nicks, see if it's too many */
2381 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2382 if (n >= nickserv_conf.nicks_per_handle) {
2383 reply("NSMSG_TOO_MANY_NICKS");
2386 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2388 reply("NSMSG_NICK_EXISTS", user->nick);
2391 register_nick(user->nick, user->handle_info);
2392 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2396 static NICKSERV_FUNC(cmd_pass)
2398 struct handle_info *hi;
2399 const char *old_pass, *new_pass;
2401 NICKSERV_MIN_PARMS(3);
2402 hi = user->handle_info;
2406 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2407 if (!checkpass(old_pass, hi->passwd)) {
2408 argv[1] = "BADPASS";
2409 reply("NSMSG_PASSWORD_INVALID");
2412 cryptpass(new_pass, hi->passwd);
2414 reply("NSMSG_PASS_SUCCESS");
2419 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2422 char *new_mask = canonicalize_hostmask(strdup(mask));
2423 for (i=0; i<hi->masks->used; i++) {
2424 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2426 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2431 string_list_append(hi->masks, new_mask);
2433 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2437 static NICKSERV_FUNC(cmd_addmask)
2440 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2441 int res = nickserv_addmask(user, user->handle_info, mask);
2445 if (!is_gline(argv[1])) {
2446 reply("NSMSG_MASK_INVALID", argv[1]);
2449 return nickserv_addmask(user, user->handle_info, argv[1]);
2453 static NICKSERV_FUNC(cmd_oaddmask)
2455 struct handle_info *hi;
2457 NICKSERV_MIN_PARMS(3);
2458 if (!(hi = get_victim_oper(user, argv[1])))
2460 return nickserv_addmask(user, hi, argv[2]);
2464 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2467 for (i=0; i<hi->masks->used; i++) {
2468 if (!strcmp(del_mask, hi->masks->list[i])) {
2469 char *old_mask = hi->masks->list[i];
2470 if (hi->masks->used == 1 && !force) {
2471 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2474 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2475 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2480 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2484 static NICKSERV_FUNC(cmd_delmask)
2486 NICKSERV_MIN_PARMS(2);
2487 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2490 static NICKSERV_FUNC(cmd_odelmask)
2492 struct handle_info *hi;
2493 NICKSERV_MIN_PARMS(3);
2494 if (!(hi = get_victim_oper(user, argv[1])))
2496 return nickserv_delmask(user, hi, argv[2], 1);
2500 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2501 unsigned int nn, add = 1, pos;
2502 unsigned long added, removed, flag;
2504 for (added=removed=nn=0; str[nn]; nn++) {
2506 case '+': add = 1; break;
2507 case '-': add = 0; break;
2509 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2510 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2513 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2514 /* cheesy avoidance of looking up the flag name.. */
2515 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2518 flag = 1 << (pos - 1);
2520 added |= flag, removed &= ~flag;
2522 removed |= flag, added &= ~flag;
2527 *premoved = removed;
2532 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2534 unsigned long before, after, added, removed;
2535 struct userNode *uNode;
2537 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2538 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2540 hi->flags = (hi->flags | added) & ~removed;
2541 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2543 /* Strip helping flag if they're only a support helper and not
2544 * currently in #support. */
2545 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2546 struct channelList *schannels;
2548 schannels = chanserv_support_channels();
2549 for (ii = 0; ii < schannels->used; ++ii)
2550 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2552 if (ii == schannels->used)
2553 HANDLE_CLEAR_FLAG(hi, HELPING);
2556 if (after && !before) {
2557 /* Add user to current helper list. */
2558 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2559 userList_append(&curr_helpers, uNode);
2560 } else if (!after && before) {
2561 /* Remove user from current helper list. */
2562 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2563 userList_remove(&curr_helpers, uNode);
2570 set_list(struct userNode *user, struct handle_info *hi, int override)
2574 char *set_display[] = {
2575 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2576 "EMAIL", "MAXLOGINS", "LANGUAGE", "DEVNULL"
2579 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2581 /* Do this so options are presented in a consistent order. */
2582 for (i = 0; i < ArrayLength(set_display); ++i)
2583 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2584 opt(user, hi, override, 0, NULL);
2587 static NICKSERV_FUNC(cmd_set)
2589 struct handle_info *hi;
2592 hi = user->handle_info;
2594 set_list(user, hi, 0);
2597 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2598 reply("NSMSG_INVALID_OPTION", argv[1]);
2601 return opt(user, hi, 0, argc-1, argv+1);
2604 static NICKSERV_FUNC(cmd_oset)
2606 struct handle_info *hi;
2607 struct svccmd *subcmd;
2609 char cmdname[MAXLEN];
2611 NICKSERV_MIN_PARMS(2);
2613 if (!(hi = get_victim_oper(user, argv[1])))
2617 set_list(user, hi, 0);
2621 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2622 reply("NSMSG_INVALID_OPTION", argv[2]);
2626 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2627 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2628 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2631 return opt(user, hi, 1, argc-2, argv+2);
2634 static OPTION_FUNC(opt_info)
2638 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2640 hi->infoline = NULL;
2642 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2646 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2647 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2651 static OPTION_FUNC(opt_devnull)
2653 const char *devnull_name;
2655 struct devnull_class *devnull_c;
2659 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2662 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2665 devnull_name = unsplit_string(argv+1, argc-1, NULL);
2666 devnull_c = devnull_find_name(devnull_name);
2668 hi->devnull_id = devnull_c->devnull_id;
2672 if(hi->devnull_id) {
2673 devnull_c = devnull_find_id(hi->devnull_id);
2675 devnull_name = devnull_c->name;
2677 devnull_name = user_find_message(user, "MSG_NONE");
2681 devnull_name = user_find_message(user, "MSG_NONE");
2682 send_message(user, nickserv, "NSMSG_SET_DEVNULL", devnull_name);
2686 void nickserv_devnull_delete(unsigned int devnull_id) {
2688 struct handle_info *hi;
2690 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2692 if (hi->devnull_id == devnull_id) {
2698 static OPTION_FUNC(opt_website)
2700 const char *website;
2703 if (!HANDLE_FLAGGED(user->handle_info, BOT)) {
2704 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2707 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2711 website = unsplit_string(argv+1, argc-1, NULL);
2712 hi->website = strdup(website);
2715 if (HANDLE_FLAGGED(user->handle_info, BOT)) {
2716 website = hi->website ? hi->website : user_find_message(user, "MSG_NONE");
2717 send_message(user, nickserv, "NSMSG_SET_WEBSITE", website);
2722 static OPTION_FUNC(opt_width)
2725 hi->screen_width = strtoul(argv[1], NULL, 0);
2727 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2728 hi->screen_width = MIN_LINE_SIZE;
2729 else if (hi->screen_width > MAX_LINE_SIZE)
2730 hi->screen_width = MAX_LINE_SIZE;
2732 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2736 static OPTION_FUNC(opt_tablewidth)
2739 hi->table_width = strtoul(argv[1], NULL, 0);
2741 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2742 hi->table_width = MIN_LINE_SIZE;
2743 else if (hi->screen_width > MAX_LINE_SIZE)
2744 hi->table_width = MAX_LINE_SIZE;
2746 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2750 static OPTION_FUNC(opt_color)
2753 if (enabled_string(argv[1]))
2754 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2755 else if (disabled_string(argv[1]))
2756 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2758 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2763 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2767 static OPTION_FUNC(opt_privmsg)
2770 if (enabled_string(argv[1]))
2771 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2772 else if (disabled_string(argv[1]))
2773 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2775 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2780 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2784 static OPTION_FUNC(opt_autohide)
2787 if (enabled_string(argv[1]))
2788 HANDLE_SET_FLAG(hi, AUTOHIDE);
2789 else if (disabled_string(argv[1]))
2790 HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
2792 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2797 send_message(user, nickserv, "NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
2801 static OPTION_FUNC(opt_style)
2806 if (!irccasecmp(argv[1], "Zoot"))
2807 hi->userlist_style = HI_STYLE_ZOOT;
2808 else if (!irccasecmp(argv[1], "def"))
2809 hi->userlist_style = HI_STYLE_DEF;
2812 switch (hi->userlist_style) {
2821 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2825 static OPTION_FUNC(opt_password)
2828 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2833 cryptpass(argv[1], hi->passwd);
2835 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2841 static OPTION_FUNC(opt_flags)
2844 unsigned int ii, flen;
2847 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2852 nickserv_apply_flags(user, hi, argv[1]);
2854 for (ii = flen = 0; handle_flags[ii]; ii++)
2855 if (hi->flags & (1 << ii))
2856 flags[flen++] = handle_flags[ii];
2859 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2861 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2865 static OPTION_FUNC(opt_email)
2869 if (!is_valid_email_addr(argv[1])) {
2870 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2873 if ((str = mail_prohibited_address(argv[1]))) {
2874 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2877 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2878 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2880 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2882 nickserv_set_email_addr(hi, argv[1]);
2884 nickserv_eat_cookie(hi->cookie);
2885 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2888 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2892 static OPTION_FUNC(opt_maxlogins)
2894 unsigned char maxlogins;
2896 maxlogins = strtoul(argv[1], NULL, 0);
2897 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2898 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2901 hi->maxlogins = maxlogins;
2903 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2904 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2908 static OPTION_FUNC(opt_language)
2910 struct language *lang;
2912 lang = language_find(argv[1]);
2913 if (irccasecmp(lang->name, argv[1]))
2914 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2915 hi->language = lang;
2917 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2921 static OPTION_FUNC(opt_karma)
2924 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2929 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2930 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2931 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2932 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2934 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2938 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2943 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2944 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2946 if ((user->handle_info->opserv_level < target->opserv_level)
2947 || ((user->handle_info->opserv_level == target->opserv_level)
2948 && (user->handle_info->opserv_level < 1000))) {
2949 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2952 if ((user->handle_info->opserv_level < new_level)
2953 || ((user->handle_info->opserv_level == new_level)
2954 && (user->handle_info->opserv_level < 1000))) {
2955 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2958 if (user->handle_info == target) {
2959 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2962 if (target->opserv_level == new_level)
2964 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2965 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2966 target->opserv_level = new_level;
2971 oper_try_set_staff_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2972 if (!oper_has_access(user, bot, nickserv_conf.modstaff_level, 0))
2974 if ((user->handle_info->opserv_level < target->opserv_level)
2975 || ((user->handle_info->opserv_level == target->opserv_level)
2976 && (user->handle_info->opserv_level < 1000))) {
2977 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2980 if (target->staff_level == new_level)
2982 log_module(NS_LOG, LOG_INFO, "Account %s setting staff level for account %s to %d (from %d).",
2983 user->handle_info->handle, target->handle, new_level, target->staff_level);
2984 target->staff_level = new_level;
2988 static OPTION_FUNC(opt_level)
2993 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2997 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2998 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
3002 static OPTION_FUNC(opt_staff_level)
3007 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3011 res = (argc > 1) ? oper_try_set_staff_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
3012 send_message(user, nickserv, "NSMSG_SET_STAFFLEVEL", hi->staff_level);
3016 static OPTION_FUNC(opt_epithet)
3019 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3023 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
3024 char *epithet = unsplit_string(argv+1, argc-1, NULL);
3027 if ((epithet[0] == '*') && !epithet[1])
3030 hi->epithet = strdup(epithet);
3034 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
3036 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
3040 static OPTION_FUNC(opt_title)
3045 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3049 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
3051 if (strchr(title, '.')) {
3052 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
3055 if ((strlen(user->handle_info->handle) + strlen(title) +
3056 strlen(titlehost_suffix) + 2) > HOSTLEN) {
3057 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
3062 if (!strcmp(title, "*")) {
3063 hi->fakehost = NULL;
3065 hi->fakehost = malloc(strlen(title)+2);
3066 hi->fakehost[0] = '.';
3067 strcpy(hi->fakehost+1, title);
3069 apply_fakehost(hi, NULL);
3070 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
3071 title = hi->fakehost + 1;
3075 title = user_find_message(user, "MSG_NONE");
3076 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
3080 static OPTION_FUNC(opt_fakehost)
3082 char mask[USERLEN + HOSTLEN + 2];
3086 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3090 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
3091 if(strlen(argv[1]) >= sizeof(mask)) {
3092 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
3096 safestrncpy(mask, argv[1], sizeof(mask));
3098 if ((host = strrchr(mask, '@')) && host != mask) {
3099 /* If ident@host was used and the user doesn't have access to set idents, do not change anything. */
3100 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3112 if (ident && strlen(ident) > USERLEN) {
3113 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3117 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
3118 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
3122 if (host && host[0]) {
3124 if (!strcmp(host, "*"))
3125 hi->fakehost = NULL;
3127 hi->fakehost = strdup(host);
3128 host = hi->fakehost;
3131 host = generate_fakehost(hi);
3134 free(hi->fakeident);
3135 if (!strcmp(ident, "*"))
3136 hi->fakeident = NULL;
3138 hi->fakeident = strdup(ident);
3139 ident = hi->fakeident;
3142 ident = generate_fakeident(hi, NULL);
3144 apply_fakehost(hi, NULL);
3146 host = generate_fakehost(hi);
3147 ident = generate_fakeident(hi, NULL);
3150 host = (char *) user_find_message(user, "MSG_NONE");
3152 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
3154 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
3158 static OPTION_FUNC(opt_fakeident)
3163 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3167 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3169 if (strlen(ident) > USERLEN) {
3170 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3173 free(hi->fakeident);
3174 if (!strcmp(ident, "*"))
3175 hi->fakeident = NULL;
3177 hi->fakeident = strdup(ident);
3178 ident = hi->fakeident;
3179 apply_fakehost(hi, NULL);
3181 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
3183 ident = user_find_message(user, "MSG_NONE");
3184 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
3188 static NICKSERV_FUNC(cmd_reclaim)
3190 struct nick_info *ni;
3191 struct userNode *victim;
3193 NICKSERV_MIN_PARMS(2);
3194 ni = dict_find(nickserv_nick_dict, argv[1], 0);
3196 reply("NSMSG_UNKNOWN_NICK", argv[1]);
3199 if (ni->owner != user->handle_info) {
3200 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
3203 victim = GetUserH(ni->nick);
3205 reply("MSG_NICK_UNKNOWN", ni->nick);
3208 if (victim == user) {
3209 reply("NSMSG_NICK_USER_YOU");
3212 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
3213 switch (nickserv_conf.reclaim_action) {
3214 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
3215 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
3216 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
3217 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
3222 static NICKSERV_FUNC(cmd_unregnick)
3225 struct handle_info *hi;
3226 struct nick_info *ni;
3228 hi = user->handle_info;
3229 nick = (argc < 2) ? user->nick : (const char*)argv[1];
3230 ni = dict_find(nickserv_nick_dict, nick, NULL);
3232 reply("NSMSG_UNKNOWN_NICK", nick);
3235 if (hi != ni->owner) {
3236 reply("NSMSG_NOT_YOUR_NICK", nick);
3239 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3244 static NICKSERV_FUNC(cmd_ounregnick)
3246 struct nick_info *ni;
3248 NICKSERV_MIN_PARMS(2);
3249 if (!(ni = get_nick_info(argv[1]))) {
3250 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
3253 if (!oper_outranks(user, ni->owner))
3255 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3260 static NICKSERV_FUNC(cmd_unregister)
3262 struct handle_info *hi;
3265 NICKSERV_MIN_PARMS(2);
3266 hi = user->handle_info;
3269 if (checkpass(passwd, hi->passwd)) {
3270 nickserv_unregister_handle(hi, user);
3273 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3274 reply("NSMSG_PASSWORD_INVALID");
3279 static NICKSERV_FUNC(cmd_ounregister)
3281 struct handle_info *hi;
3282 char reason[MAXLEN];
3285 NICKSERV_MIN_PARMS(2);
3286 if (!(hi = get_victim_oper(user, argv[1])))
3289 if (HANDLE_FLAGGED(hi, NODELETE)) {
3290 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
3294 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
3296 ((hi->flags & nickserv_conf.ounregister_flags)
3298 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
3299 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
3303 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
3304 global_message(MESSAGE_RECIPIENT_STAFF, reason);
3305 nickserv_unregister_handle(hi, user);
3309 static NICKSERV_FUNC(cmd_status)
3311 if (nickserv_conf.disable_nicks) {
3312 reply("NSMSG_GLOBAL_STATS_NONICK",
3313 dict_size(nickserv_handle_dict));
3315 if (user->handle_info) {
3317 struct nick_info *ni;
3318 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
3319 reply("NSMSG_HANDLE_STATS", cnt);
3321 reply("NSMSG_HANDLE_NONE");
3323 reply("NSMSG_GLOBAL_STATS",
3324 dict_size(nickserv_handle_dict),
3325 dict_size(nickserv_nick_dict));
3330 static NICKSERV_FUNC(cmd_ghost)
3332 struct userNode *target;
3333 char reason[MAXLEN];
3335 NICKSERV_MIN_PARMS(2);
3336 if (!(target = GetUserH(argv[1]))) {
3337 reply("MSG_NICK_UNKNOWN", argv[1]);
3340 if (target == user) {
3341 reply("NSMSG_CANNOT_GHOST_SELF");
3344 if (!target->handle_info || (target->handle_info != user->handle_info)) {
3345 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
3348 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
3349 DelUser(target, nickserv, 1, reason);
3350 reply("NSMSG_GHOST_KILLED", argv[1]);
3354 static NICKSERV_FUNC(cmd_vacation)
3356 HANDLE_SET_FLAG(user->handle_info, FROZEN);
3357 reply("NSMSG_ON_VACATION");
3361 static NICKSERV_FUNC(cmd_addnote)
3363 struct handle_info *hi;
3364 unsigned long duration;
3367 struct handle_note *prev;
3368 struct handle_note *note;
3370 /* Parse parameters and figure out values for note's fields. */
3371 NICKSERV_MIN_PARMS(4);
3372 hi = get_victim_oper(user, argv[1]);
3375 if(!strcmp(argv[2], "0"))
3377 else if(!(duration = ParseInterval(argv[2])))
3379 reply("MSG_INVALID_DURATION", argv[2]);
3382 if (duration > 2*365*86400) {
3383 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
3386 unsplit_string(argv + 3, argc - 3, text);
3387 WALK_NOTES(hi, prev, note) {}
3388 id = prev ? (prev->id + 1) : 1;
3390 /* Create the new note structure. */
3391 note = calloc(1, sizeof(*note) + strlen(text));
3393 note->expires = duration ? (now + duration) : 0;
3396 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
3397 strcpy(note->note, text);
3402 reply("NSMSG_NOTE_ADDED", id, hi->handle);
3406 static NICKSERV_FUNC(cmd_delnote)
3408 struct handle_info *hi;
3409 struct handle_note *prev;
3410 struct handle_note *note;
3413 NICKSERV_MIN_PARMS(3);
3414 hi = get_victim_oper(user, argv[1]);
3417 id = strtoul(argv[2], NULL, 10);
3418 WALK_NOTES(hi, prev, note) {
3419 if (id == note->id) {
3421 prev->next = note->next;
3423 hi->notes = note->next;
3425 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3429 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3434 nickserv_saxdb_write(struct saxdb_context *ctx) {
3436 struct handle_info *hi;
3439 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3441 assert(hi->id != 0);
3442 saxdb_start_record(ctx, iter_key(it), 0);
3444 struct handle_cookie *cookie = hi->cookie;
3447 switch (cookie->type) {
3448 case ACTIVATION: type = KEY_ACTIVATION; break;
3449 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3450 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3451 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3452 default: type = NULL; break;
3455 saxdb_start_record(ctx, KEY_COOKIE, 0);
3456 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3457 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3459 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3460 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3461 saxdb_end_record(ctx);
3465 struct handle_note *prev, *note;
3466 saxdb_start_record(ctx, KEY_NOTES, 0);
3467 WALK_NOTES(hi, prev, note) {
3468 snprintf(flags, sizeof(flags), "%d", note->id);
3469 saxdb_start_record(ctx, flags, 0);
3471 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3472 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3473 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3474 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3475 saxdb_end_record(ctx);
3477 saxdb_end_record(ctx);
3480 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3482 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3484 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3486 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3490 for (ii=flen=0; handle_flags[ii]; ++ii)
3491 if (hi->flags & (1 << ii))
3492 flags[flen++] = handle_flags[ii];
3494 saxdb_write_string(ctx, KEY_FLAGS, flags);
3496 saxdb_write_int(ctx, KEY_ID, hi->id);
3498 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3500 saxdb_write_int(ctx, KEY_DEVNULL, hi->devnull_id);
3502 saxdb_write_string(ctx, KEY_WEBSITE, hi->website);
3503 if (hi->last_quit_host[0])
3504 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3505 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3507 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3508 if (hi->masks->used)
3509 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3511 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3513 struct string_list *slist;
3514 struct nick_info *ni;
3516 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3517 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3518 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3522 if (hi->opserv_level)
3523 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3524 if (hi->staff_level)
3525 saxdb_write_int(ctx, KEY_STAFF_LEVEL, hi->staff_level);
3526 if (hi->language != lang_C)
3527 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3528 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3529 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3530 if (hi->screen_width)
3531 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3532 if (hi->table_width)
3533 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3534 flags[0] = hi->userlist_style;
3536 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3538 saxdb_start_record(ctx, KEY_AUTHLOG, 0);
3539 struct authlogEntry *authlog;
3541 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
3542 saxdb_start_record(ctx, strtab(++i), 0);
3543 saxdb_write_int(ctx, KEY_AUTHLOG_LOGIN_TIME, authlog->login_time);
3544 saxdb_write_int(ctx, KEY_AUTHLOG_LOGOUT_TIME, authlog->logout_time);
3545 saxdb_write_string(ctx, KEY_AUTHLOG_HOSTMASK, authlog->hostmask);
3546 if(authlog->quit_reason)
3547 saxdb_write_string(ctx, KEY_AUTHLOG_QUIT_REASON, authlog->quit_reason);
3548 saxdb_end_record(ctx);
3550 saxdb_end_record(ctx); //END KEY_AUTHLOG
3552 saxdb_end_record(ctx);
3557 static handle_merge_func_t *handle_merge_func_list;
3558 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3561 reg_handle_merge_func(handle_merge_func_t func)
3563 if (handle_merge_func_used == handle_merge_func_size) {
3564 if (handle_merge_func_size) {
3565 handle_merge_func_size <<= 1;
3566 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3568 handle_merge_func_size = 8;
3569 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3572 handle_merge_func_list[handle_merge_func_used++] = func;
3575 static NICKSERV_FUNC(cmd_merge)
3577 struct handle_info *hi_from, *hi_to;
3578 struct userNode *last_user;
3579 struct userData *cList, *cListNext;
3580 unsigned int ii, jj, n;
3581 char buffer[MAXLEN];
3583 NICKSERV_MIN_PARMS(3);
3585 if (!(hi_from = get_victim_oper(user, argv[1])))
3587 if (!(hi_to = get_victim_oper(user, argv[2])))
3589 if (hi_to == hi_from) {
3590 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3594 for (n=0; n<handle_merge_func_used; n++)
3595 handle_merge_func_list[n](user, hi_to, hi_from);
3597 /* Append "from" handle's nicks to "to" handle's nick list. */
3599 struct nick_info *last_ni;
3600 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3601 last_ni->next = hi_from->nicks;
3603 while (hi_from->nicks) {
3604 hi_from->nicks->owner = hi_to;
3605 hi_from->nicks = hi_from->nicks->next;
3608 /* Merge the hostmasks. */
3609 for (ii=0; ii<hi_from->masks->used; ii++) {
3610 char *mask = hi_from->masks->list[ii];
3611 for (jj=0; jj<hi_to->masks->used; jj++)
3612 if (match_ircglobs(hi_to->masks->list[jj], mask))
3614 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3615 string_list_append(hi_to->masks, strdup(mask));
3618 /* Merge the lists of authed users. */
3620 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3621 last_user->next_authed = hi_from->users;
3623 hi_to->users = hi_from->users;
3625 /* Repoint the old "from" handle's users. */
3626 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3627 last_user->handle_info = hi_to;
3629 hi_from->users = NULL;
3631 /* Merge channel userlists. */
3632 for (cList=hi_from->channels; cList; cList=cListNext) {
3633 struct userData *cList2;
3634 cListNext = cList->u_next;
3635 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3636 if (cList->channel == cList2->channel)
3638 if (cList2 && (cList2->access >= cList->access)) {
3639 log_module(NS_LOG, LOG_INFO, "Merge: %s had only %d access in %s (versus %d for %s)", hi_from->handle, cList->access, cList->channel->channel->name, cList2->access, hi_to->handle);
3640 /* keep cList2 in hi_to; remove cList from hi_from */
3641 del_channel_user(cList, 1);
3644 log_module(NS_LOG, LOG_INFO, "Merge: %s had only %d access in %s (versus %d for %s)", hi_to->handle, cList2->access, cList->channel->channel->name, cList->access, hi_from->handle);
3645 /* remove the lower-ranking cList2 from hi_to */
3646 del_channel_user(cList2, 1);
3648 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3650 /* cList needs to be moved from hi_from to hi_to */
3651 cList->handle = hi_to;
3652 /* Remove from linked list for hi_from */
3653 assert(!cList->u_prev);
3654 hi_from->channels = cList->u_next;
3656 cList->u_next->u_prev = cList->u_prev;
3657 /* Add to linked list for hi_to */
3658 cList->u_prev = NULL;
3659 cList->u_next = hi_to->channels;
3660 if (hi_to->channels)
3661 hi_to->channels->u_prev = cList;
3662 hi_to->channels = cList;
3666 /* Do they get an OpServ level promotion? */
3667 if (hi_from->opserv_level > hi_to->opserv_level)
3668 hi_to->opserv_level = hi_from->opserv_level;
3670 /* Do they get a staff level promotion? */
3671 if (hi_from->staff_level > hi_to->staff_level)
3672 hi_to->staff_level = hi_from->staff_level;
3674 /* What about last seen time? */
3675 if (hi_from->lastseen > hi_to->lastseen)
3676 hi_to->lastseen = hi_from->lastseen;
3678 /* New karma is the sum of the two original karmas. */
3679 hi_to->karma += hi_from->karma;
3681 /* Does a fakehost carry over? (This intentionally doesn't set it
3682 * for users previously attached to hi_to. They'll just have to
3685 if (hi_from->fakehost && !hi_to->fakehost)
3686 hi_to->fakehost = strdup(hi_from->fakehost);
3687 if (hi_from->fakeident && !hi_to->fakeident)
3688 hi_to->fakeident = strdup(hi_from->fakeident);
3690 /* Notify of success. */
3691 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3692 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3693 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3695 /* Unregister the "from" handle. */
3696 nickserv_unregister_handle(hi_from, NULL);
3701 #define NICKSERV_DISCRIM_FIELDS_AUTH 0x01
3702 #define NICKSERV_DISCRIM_FIELDS_EMAIL 0x02
3703 #define NICKSERV_DISCRIM_FIELDS_SEEN 0x04
3704 #define NICKSERV_DISCRIM_FIELDS_ACCESS 0x08
3705 #define NICKSERV_DISCRIM_FIELDS_FAKEHOST 0x10
3706 #define NICKSERV_DISCRIM_FIELDS_WEBSITE 0x20
3707 #define NICKSERV_DISCRIM_FIELDS_DEVNULL 0x40
3709 #define NICKSERV_DISCRIM_FIELD_COUNT 7
3711 struct nickserv_discrim {
3712 unsigned int show_fields;
3713 struct helpfile_table *output_table;
3714 int output_table_pos;
3715 unsigned int output_table_free_fields;
3717 unsigned long flags_on, flags_off;
3718 unsigned long min_registered, max_registered;
3719 unsigned long lastseen;
3721 int min_level, max_level;
3722 int min_karma, max_karma;
3723 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3724 const char *nickmask;
3725 const char *hostmask;
3726 const char *fakehostmask;
3727 const char *fakeidentmask;
3728 const char *website;
3729 const char *handlemask;
3730 const char *emailmask;
3731 const char *devnull_name;
3732 unsigned int devnull_id;
3735 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi, struct nickserv_discrim *discrim);
3737 struct discrim_apply_info {
3738 struct nickserv_discrim *discrim;
3739 discrim_search_func func;
3740 struct userNode *source;
3741 unsigned int matched;
3744 static struct nickserv_discrim *
3745 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3748 struct nickserv_discrim *discrim;
3750 discrim = malloc(sizeof(*discrim));
3751 memset(discrim, 0, sizeof(*discrim));
3752 discrim->min_level = 0;
3753 discrim->max_level = INT_MAX;
3754 discrim->limit = 50;
3755 discrim->min_registered = 0;
3756 discrim->max_registered = ULONG_MAX;
3757 discrim->lastseen = ULONG_MAX;
3758 discrim->min_karma = INT_MIN;
3759 discrim->max_karma = INT_MAX;
3761 for (i=0; i<argc; i++) {
3762 if (i == argc - 1) {
3763 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3766 if (!irccasecmp(argv[i], "limit")) {
3767 discrim->limit = strtoul(argv[++i], NULL, 0);
3768 } else if (!irccasecmp(argv[i], "flags")) {
3769 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3770 } else if (!irccasecmp(argv[i], "fields")) {
3771 char *fields = argv[++i];
3772 char *delimiter = strstr(fields, ",");
3776 if(!irccasecmp(fields, "auth"))
3777 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_AUTH;
3778 else if(!irccasecmp(fields, "email"))
3779 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_EMAIL;
3780 else if(!irccasecmp(fields, "seen"))
3781 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_SEEN;
3782 else if(!irccasecmp(fields, "access"))
3783 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_ACCESS;
3784 else if(!irccasecmp(fields, "fakehost"))
3785 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_FAKEHOST;
3786 else if(!irccasecmp(fields, "website") && IsBot(user))
3787 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_WEBSITE;
3788 else if(!irccasecmp(fields, "devnull"))
3789 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_DEVNULL;
3791 send_message(user, nickserv, "MSG_INVALID_FIELD", fields);
3796 fields = delimiter+1;
3798 delimiter = strstr(fields, ",");
3804 } else if (!irccasecmp(argv[i], "registered")) {
3805 const char *cmp = argv[++i];
3806 if (cmp[0] == '<') {
3807 if (cmp[1] == '=') {
3808 discrim->min_registered = now - ParseInterval(cmp+2);
3810 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3812 } else if (cmp[0] == '=') {
3813 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3814 } else if (cmp[0] == '>') {
3815 if (cmp[1] == '=') {
3816 discrim->max_registered = now - ParseInterval(cmp+2);
3818 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3821 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3823 } else if (!irccasecmp(argv[i], "seen")) {
3824 discrim->lastseen = now - ParseInterval(argv[++i]);
3825 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3826 discrim->nickmask = argv[++i];
3827 } else if (!irccasecmp(argv[i], "hostmask")) {
3829 if (!irccasecmp(argv[i], "exact")) {
3830 if (i == argc - 1) {
3831 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3834 discrim->hostmask_type = EXACT;
3835 } else if (!irccasecmp(argv[i], "subset")) {
3836 if (i == argc - 1) {
3837 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3840 discrim->hostmask_type = SUBSET;
3841 } else if (!irccasecmp(argv[i], "superset")) {
3842 if (i == argc - 1) {
3843 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3846 discrim->hostmask_type = SUPERSET;
3847 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3848 if (i == argc - 1) {
3849 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3852 discrim->hostmask_type = LASTQUIT;
3855 discrim->hostmask_type = SUPERSET;
3857 discrim->hostmask = argv[++i];
3858 } else if (!irccasecmp(argv[i], "fakehost")) {
3859 if (!irccasecmp(argv[++i], "*")) {
3860 discrim->fakehostmask = 0;
3862 discrim->fakehostmask = argv[i];
3864 } else if (!irccasecmp(argv[i], "fakeident")) {
3865 if (!irccasecmp(argv[++i], "*")) {
3866 discrim->fakeidentmask = 0;
3868 discrim->fakeidentmask = argv[i];
3870 } else if (!irccasecmp(argv[i], "website")) {
3871 if (!irccasecmp(argv[++i], "*")) {
3872 discrim->website = 0;
3874 discrim->website = argv[i];
3876 } else if (!irccasecmp(argv[i], "devnull")) {
3877 if (!irccasecmp(argv[++i], "*")) {
3878 discrim->devnull_id = 0;
3879 discrim->devnull_name = 0;
3881 struct devnull_class *th = devnull_find_name(argv[i]);
3883 send_message(user, nickserv, "OSMSG_DEVNULL_NOTFOUND", argv[i]);
3886 discrim->devnull_name = argv[i];
3887 discrim->devnull_id = th->id;
3889 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3890 if (!irccasecmp(argv[++i], "*")) {
3891 discrim->handlemask = 0;
3893 discrim->handlemask = argv[i];
3895 } else if (!irccasecmp(argv[i], "email")) {
3896 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3897 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3899 } else if (!irccasecmp(argv[++i], "*")) {
3900 discrim->emailmask = 0;
3902 discrim->emailmask = argv[i];
3904 } else if (!irccasecmp(argv[i], "access")) {
3905 const char *cmp = argv[++i];
3906 if (cmp[0] == '<') {
3907 if (discrim->min_level == 0) discrim->min_level = 1;
3908 if (cmp[1] == '=') {
3909 discrim->max_level = strtoul(cmp+2, NULL, 0);
3911 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3913 } else if (cmp[0] == '=') {
3914 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3915 } else if (cmp[0] == '>') {
3916 if (cmp[1] == '=') {
3917 discrim->min_level = strtoul(cmp+2, NULL, 0);
3919 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3922 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3924 } else if (!irccasecmp(argv[i], "karma")) {
3925 const char *cmp = argv[++i];
3926 if (cmp[0] == '<') {
3927 if (cmp[1] == '=') {
3928 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3930 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3932 } else if (cmp[0] == '=') {
3933 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3934 } else if (cmp[0] == '>') {
3935 if (cmp[1] == '=') {
3936 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3938 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3941 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3944 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3955 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3957 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3958 || (discrim->flags_off & hi->flags)
3959 || (discrim->min_registered > hi->registered)
3960 || (discrim->max_registered < hi->registered)
3961 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3962 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3963 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3964 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3965 || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website)))
3966 || (discrim->devnull_id && discrim->devnull_id != hi->devnull_id)
3967 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3968 || (discrim->min_level > hi->opserv_level)
3969 || (discrim->max_level < hi->opserv_level)
3970 || (discrim->min_karma > hi->karma)
3971 || (discrim->max_karma < hi->karma)
3975 if (discrim->hostmask) {
3977 for (i=0; i<hi->masks->used; i++) {
3978 const char *mask = hi->masks->list[i];
3979 if ((discrim->hostmask_type == SUBSET)
3980 && (match_ircglobs(discrim->hostmask, mask))) break;
3981 else if ((discrim->hostmask_type == EXACT)
3982 && !irccasecmp(discrim->hostmask, mask)) break;
3983 else if ((discrim->hostmask_type == SUPERSET)
3984 && (match_ircglobs(mask, discrim->hostmask))) break;
3985 else if ((discrim->hostmask_type == LASTQUIT)
3986 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3988 if (i==hi->masks->used) return 0;
3990 if (discrim->nickmask) {
3991 struct nick_info *nick = hi->nicks;
3993 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3996 if (!nick) return 0;
4002 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
4004 dict_iterator_t it, next;
4005 unsigned int matched;
4007 for (it = dict_first(nickserv_handle_dict), matched = 0;
4008 it && (matched < discrim->limit);
4010 next = iter_next(it);
4011 if (nickserv_discrim_match(discrim, iter_data(it))) {
4012 dsf(source, iter_data(it), discrim);
4020 search_print_func(struct userNode *source, struct handle_info *match, struct nickserv_discrim *discrim)
4022 if(discrim->show_fields) {
4024 if(discrim->output_table) {
4025 discrim->output_table->contents[++discrim->output_table_pos] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
4027 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
4028 discrim->output_table->contents[discrim->output_table_pos][i++] = match->handle;
4029 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
4030 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->email_addr ? match->email_addr : "*");
4031 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN) {
4033 char seenBuf[INTERVALLEN];
4036 } else if(match->lastseen == 0) {
4039 seen = intervalString(seenBuf, now - match->lastseen, source->handle_info);
4041 discrim->output_table_free_fields |= 1 << i;
4042 discrim->output_table->contents[discrim->output_table_pos][i++] = strdup(seen);
4044 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
4045 discrim->output_table->contents[discrim->output_table_pos][i++] = strtab(match->opserv_level);
4046 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
4047 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->fakehost ? match->fakehost : "*");
4048 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
4049 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->website ? match->website : "*");
4050 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL) {
4051 if(discrim->devnull_name)
4052 discrim->output_table->contents[discrim->output_table_pos][i++] = discrim->devnull_name;
4054 struct devnull_class *devnull = devnull_find_id(match->devnull_id);
4055 discrim->output_table->contents[discrim->output_table_pos][i++] = (devnull ? devnull->name : "*");
4060 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
4064 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match), UNUSED_ARG(struct nickserv_discrim *discrim))
4069 search_unregister_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
4071 if (oper_has_access(source, nickserv, match->opserv_level, 0))
4072 nickserv_unregister_handle(match, source);
4076 nickserv_sort_accounts_by_access(const void *a, const void *b)
4078 const struct handle_info *hi_a = *(const struct handle_info**)a;
4079 const struct handle_info *hi_b = *(const struct handle_info**)b;
4080 if (hi_a->opserv_level != hi_b->opserv_level)
4081 return hi_b->opserv_level - hi_a->opserv_level;
4082 return irccasecmp(hi_a->handle, hi_b->handle);
4086 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
4088 struct handle_info_list hil;
4089 struct helpfile_table tbl;
4094 memset(&hil, 0, sizeof(hil));
4095 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
4096 struct handle_info *hi = iter_data(it);
4097 if (hi->opserv_level)
4098 handle_info_list_append(&hil, hi);
4100 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
4101 tbl.length = hil.used + 1;
4103 tbl.flags = TABLE_NO_FREE;
4104 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4105 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
4108 for (ii = 0; ii < hil.used; ) {
4109 ary = malloc(tbl.width * sizeof(ary[0]));
4110 ary[0] = hil.list[ii]->handle;
4111 ary[1] = strtab(hil.list[ii]->opserv_level);
4112 tbl.contents[++ii] = ary;
4114 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4115 reply("MSG_MATCH_COUNT", hil.used);
4116 for (ii = 0; ii < hil.used; ii++)
4117 free(tbl.contents[ii]);
4122 static NICKSERV_FUNC(cmd_search)
4124 struct nickserv_discrim *discrim;
4125 discrim_search_func action;
4126 struct svccmd *subcmd;
4127 unsigned int matches;
4130 NICKSERV_MIN_PARMS(3);
4131 sprintf(buf, "search %s", argv[1]);
4132 subcmd = dict_find(nickserv_service->commands, buf, NULL);
4133 if (!irccasecmp(argv[1], "print"))
4134 action = search_print_func;
4135 else if (!irccasecmp(argv[1], "count"))
4136 action = search_count_func;
4137 else if (!irccasecmp(argv[1], "unregister"))
4138 action = search_unregister_func;
4140 reply("NSMSG_INVALID_ACTION", argv[1]);
4144 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
4147 discrim = nickserv_discrim_create(user, argc-2, argv+2);
4151 if (action == search_print_func)
4152 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
4153 else if (action == search_count_func)
4154 discrim->limit = INT_MAX;
4156 matches = nickserv_discrim_search(discrim, action, user);
4158 if(discrim->show_fields) {
4161 for(ii = 0; ii < NICKSERV_DISCRIM_FIELD_COUNT; ii++) {
4162 if(discrim->show_fields & (1 << ii)) width++;
4164 discrim->output_table = malloc(sizeof(discrim->output_table[0]));
4165 discrim->output_table->length = matches+1;
4166 discrim->output_table->width = width;
4167 discrim->output_table->flags = TABLE_NO_FREE;
4168 discrim->output_table->contents = malloc(discrim->output_table->length * sizeof(discrim->output_table->contents[0]));
4169 discrim->output_table->contents[0] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
4172 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
4173 discrim->output_table->contents[0][ii++] = "Auth";
4174 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
4175 discrim->output_table->contents[0][ii++] = "EMail";
4176 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN)
4177 discrim->output_table->contents[0][ii++] = "Seen";
4178 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
4179 discrim->output_table->contents[0][ii++] = "Access";
4180 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
4181 discrim->output_table->contents[0][ii++] = "Fakehost";
4182 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
4183 discrim->output_table->contents[0][ii++] = "Website";
4184 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
4185 discrim->output_table->contents[0][ii++] = "DevNull";
4187 nickserv_discrim_search(discrim, action, user);
4189 table_send(nickserv, user->nick, 0, NULL, *discrim->output_table);
4191 for(ii = 1; ii < discrim->output_table->length; ++ii) {
4193 for(ij = 0; ij < NICKSERV_DISCRIM_FIELD_COUNT; ij++) {
4194 if(discrim->output_table_free_fields & (1 << ij))
4195 free((char*)discrim->output_table->contents[ii][ij]);
4197 free(discrim->output_table->contents[ii]);
4199 free(discrim->output_table->contents[0]);
4200 free(discrim->output_table->contents);
4201 free(discrim->output_table);
4204 reply("MSG_MATCH_COUNT", matches);
4206 reply("MSG_NO_MATCHES");
4213 static MODCMD_FUNC(cmd_checkpass)
4215 struct handle_info *hi;
4217 NICKSERV_MIN_PARMS(3);
4218 if (!(hi = get_handle_info(argv[1]))) {
4219 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4222 if (checkpass(argv[2], hi->passwd))
4223 reply("CHECKPASS_YES");
4225 reply("CHECKPASS_NO");
4230 static MODCMD_FUNC(cmd_checkemail)
4232 struct handle_info *hi;
4234 NICKSERV_MIN_PARMS(3);
4235 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
4238 if (!hi->email_addr)
4239 reply("CHECKEMAIL_NOT_SET");
4240 else if (!irccasecmp(argv[2], hi->email_addr))
4241 reply("CHECKEMAIL_YES");
4243 reply("CHECKEMAIL_NO");
4248 nickserv_db_read_authlog(UNUSED_ARG(const char *key), void *data, void *extra)
4250 struct record_data *rd = data;
4251 struct handle_info *hi = extra;
4253 struct authlogEntry *authlog;
4254 authlog = malloc(sizeof(*authlog));
4256 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGIN_TIME, RECDB_QSTRING);
4257 authlog->login_time = str ? strtoul(str, NULL, 0) : 0;
4259 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGOUT_TIME, RECDB_QSTRING);
4260 authlog->logout_time = str ? strtoul(str, NULL, 0) : 0;
4262 str = database_get_data(rd->d.object, KEY_AUTHLOG_HOSTMASK, RECDB_QSTRING);
4263 authlog->hostmask = str ? strdup(str) : NULL;
4265 str = database_get_data(rd->d.object, KEY_AUTHLOG_QUIT_REASON, RECDB_QSTRING);
4266 authlog->quit_reason = str ? strdup(str) : NULL;
4268 authlog->user = NULL;
4270 authlog->next = NULL;
4272 //append it to the end of the list...
4273 struct authlogEntry *authlog_entry;
4275 hi->authlog = authlog;
4277 for(authlog_entry = hi->authlog; authlog_entry; authlog_entry = authlog_entry->next) {
4278 if(!authlog_entry->next) {
4279 authlog_entry->next = authlog;
4288 nickserv_db_read_handle(const char *handle, dict_t obj)
4291 struct string_list *masks, *slist;
4292 struct handle_info *hi;
4293 struct userNode *authed_users;
4294 struct userData *channel_list;
4299 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
4300 id = str ? strtoul(str, NULL, 0) : 0;
4301 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
4303 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
4306 if ((hi = get_handle_info(handle))) {
4307 authed_users = hi->users;
4308 channel_list = hi->channels;
4310 hi->channels = NULL;
4311 dict_remove(nickserv_handle_dict, hi->handle);
4313 authed_users = NULL;
4314 channel_list = NULL;
4316 hi = register_handle(handle, str, id);
4318 hi->users = authed_users;
4319 while (authed_users) {
4320 authed_users->handle_info = hi;
4321 authed_users = authed_users->next_authed;
4324 hi->channels = channel_list;
4325 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
4326 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
4327 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
4328 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
4329 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
4330 hi->language = language_find(str ? str : "C");
4331 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
4332 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
4333 str = database_get_data(obj, KEY_STAFF_LEVEL, RECDB_QSTRING);
4334 hi->staff_level = str ? strtoul(str, NULL, 0) : 0;
4335 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
4337 hi->infoline = strdup(str);
4338 str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING);
4340 hi->website = strdup(str);
4341 str = database_get_data(obj, KEY_DEVNULL, RECDB_QSTRING);
4342 hi->devnull_id = str ? strtoul(str, NULL, 0) : 0;
4343 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
4344 hi->registered = str ? strtoul(str, NULL, 0) : now;
4345 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
4346 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
4347 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
4348 hi->karma = str ? strtoul(str, NULL, 0) : 0;
4349 /* We want to read the nicks even if disable_nicks is set. This is so
4350 * that we don't lose the nick data entirely. */
4351 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
4353 for (ii=0; ii<slist->used; ii++)
4354 register_nick(slist->list[ii], hi);
4356 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
4358 for (ii=0; str[ii]; ii++)
4359 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
4361 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
4362 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
4363 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
4364 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
4365 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
4366 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
4367 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
4369 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
4371 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
4372 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
4374 nickserv_set_email_addr(hi, str);
4375 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
4377 hi->epithet = strdup(str);
4378 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
4380 hi->fakehost = strdup(str);
4381 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
4383 hi->fakeident = strdup(str);
4384 /* Read the "cookie" sub-database (if it exists). */
4385 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
4387 const char *data, *type, *expires, *cookie_str;
4388 struct handle_cookie *cookie;
4390 cookie = calloc(1, sizeof(*cookie));
4391 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
4392 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
4393 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
4394 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
4395 if (!type || !expires || !cookie_str) {
4396 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
4399 if (!irccasecmp(type, KEY_ACTIVATION))
4400 cookie->type = ACTIVATION;
4401 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
4402 cookie->type = PASSWORD_CHANGE;
4403 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
4404 cookie->type = EMAIL_CHANGE;
4405 else if (!irccasecmp(type, KEY_ALLOWAUTH))
4406 cookie->type = ALLOWAUTH;
4408 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
4411 cookie->expires = strtoul(expires, NULL, 0);
4412 if (cookie->expires < now)
4415 cookie->data = strdup(data);
4416 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
4420 nickserv_bake_cookie(cookie);
4422 nickserv_free_cookie(cookie);
4424 /* Read the "notes" sub-database (if it exists). */
4425 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
4428 struct handle_note *last_note;
4429 struct handle_note *note;
4432 for (it = dict_first(subdb); it; it = iter_next(it)) {
4433 const char *expires;
4437 const char *note_id;
4440 note_id = iter_key(it);
4441 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
4443 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
4446 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
4447 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
4448 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
4449 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
4450 if (!setter || !text || !set) {
4451 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
4454 note = calloc(1, sizeof(*note) + strlen(text));
4456 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
4457 note->set = strtoul(set, NULL, 10);
4458 note->id = strtoul(note_id, NULL, 10);
4459 safestrncpy(note->setter, setter, sizeof(note->setter));
4460 strcpy(note->note, text);
4462 last_note->next = note;
4468 if ((subdb = database_get_data(obj, KEY_AUTHLOG, RECDB_OBJECT)))
4469 dict_foreach(subdb, nickserv_db_read_authlog, hi);
4473 nickserv_saxdb_read(dict_t db) {
4475 struct record_data *rd;
4477 for (it=dict_first(db); it; it=iter_next(it)) {
4479 nickserv_db_read_handle(iter_key(it), rd->d.object);
4484 static NICKSERV_FUNC(cmd_mergedb)
4486 struct timeval start, stop;
4489 NICKSERV_MIN_PARMS(2);
4490 gettimeofday(&start, NULL);
4491 if (!(db = parse_database(argv[1]))) {
4492 reply("NSMSG_DB_UNREADABLE", argv[1]);
4495 nickserv_saxdb_read(db);
4497 gettimeofday(&stop, NULL);
4498 stop.tv_sec -= start.tv_sec;
4499 stop.tv_usec -= start.tv_usec;
4500 if (stop.tv_usec < 0) {
4502 stop.tv_usec += 1000000;
4504 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
4509 expire_handles(UNUSED_ARG(void *data))
4511 dict_iterator_t it, next;
4512 unsigned long expiry;
4513 struct handle_info *hi;
4515 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4516 next = iter_next(it);
4518 if ((hi->opserv_level > 0)
4520 || HANDLE_FLAGGED(hi, FROZEN)
4521 || HANDLE_FLAGGED(hi, NODELETE)) {
4524 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4525 if ((now - hi->lastseen) > expiry) {
4526 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
4527 nickserv_unregister_handle(hi, NULL);
4531 if (nickserv_conf.handle_expire_frequency)
4532 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4536 nickserv_load_dict(const char *fname)
4540 if (!(file = fopen(fname, "r"))) {
4541 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4544 while (fgets(line, sizeof(line), file)) {
4547 if (line[strlen(line)-1] == '\n')
4548 line[strlen(line)-1] = 0;
4549 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4552 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4555 static enum reclaim_action
4556 reclaim_action_from_string(const char *str) {
4558 return RECLAIM_NONE;
4559 else if (!irccasecmp(str, "warn"))
4560 return RECLAIM_WARN;
4561 else if (!irccasecmp(str, "svsnick"))
4562 return RECLAIM_SVSNICK;
4563 else if (!irccasecmp(str, "kill"))
4564 return RECLAIM_KILL;
4566 return RECLAIM_NONE;
4570 nickserv_conf_read(void)
4572 dict_t conf_node, child;
4576 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4577 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4580 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4582 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4583 if (nickserv_conf.valid_handle_regex_set)
4584 regfree(&nickserv_conf.valid_handle_regex);
4586 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4587 nickserv_conf.valid_handle_regex_set = !err;
4588 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4590 nickserv_conf.valid_handle_regex_set = 0;
4592 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4593 if (nickserv_conf.valid_nick_regex_set)
4594 regfree(&nickserv_conf.valid_nick_regex);
4596 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4597 nickserv_conf.valid_nick_regex_set = !err;
4598 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4600 nickserv_conf.valid_nick_regex_set = 0;
4602 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4604 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4605 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4606 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4607 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4608 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4609 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4610 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4611 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4612 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4613 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4614 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4615 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4616 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4617 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4618 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4619 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4620 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4621 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4622 str = database_get_data(conf_node, KEY_MODSTAFF_LEVEL, RECDB_QSTRING);
4623 nickserv_conf.modstaff_level = str ? strtoul(str, NULL, 0) : 800;
4624 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4625 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4626 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4627 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4628 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4629 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4630 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
4631 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
4632 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4634 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4635 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4636 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4638 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4639 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4640 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4642 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4643 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4644 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4645 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4646 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4647 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4648 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4649 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4650 str = database_get_data(conf_node, KEY_MAX_AUTHLOG_LEN, RECDB_QSTRING);
4651 nickserv_conf.max_authlog_len = str ? strtoul(str, NULL, 0) : 30;
4652 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4653 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4654 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4657 nickserv_conf.ounregister_flags = 0;
4659 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4662 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4664 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4666 nickserv_conf.handle_ts_mode = TS_IGNORE;
4667 else if (!irccasecmp(str, "ircu"))
4668 nickserv_conf.handle_ts_mode = TS_IRCU;
4670 nickserv_conf.handle_ts_mode = TS_IGNORE;
4671 if (!nickserv_conf.disable_nicks) {
4672 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4673 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4674 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4675 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4676 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4677 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4678 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4679 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4681 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4682 for (it=dict_first(child); it; it=iter_next(it)) {
4683 const char *key = iter_key(it), *value;
4687 if (!strncasecmp(key, "uc_", 3))
4688 flag = toupper(key[3]);
4689 else if (!strncasecmp(key, "lc_", 3))
4690 flag = tolower(key[3]);
4694 if ((pos = handle_inverse_flags[flag])) {
4695 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4696 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4699 if (nickserv_conf.weak_password_dict)
4700 dict_delete(nickserv_conf.weak_password_dict);
4701 nickserv_conf.weak_password_dict = dict_new();
4702 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4703 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4704 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4705 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4707 nickserv_load_dict(str);
4708 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4709 if (nickserv && str)
4710 NickChange(nickserv, str, 0);
4711 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4712 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4713 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4714 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4715 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4716 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4717 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4718 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4719 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4720 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4721 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4722 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4723 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4724 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4725 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4726 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4727 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4728 titlehost_suffix = str ? str : "example.net";
4729 str = conf_get_data("server/network", RECDB_QSTRING);
4730 nickserv_conf.network_name = str ? str : "some IRC network";
4731 if (!nickserv_conf.auth_policer_params) {
4732 nickserv_conf.auth_policer_params = policer_params_new();
4733 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4734 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4736 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4737 for (it=dict_first(child); it; it=iter_next(it))
4738 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4742 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4744 char newnick[NICKLEN+1];
4753 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4755 case RECLAIM_SVSNICK:
4757 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4758 } while (GetUserH(newnick));
4759 irc_svsnick(nickserv, user, newnick);
4762 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4763 DelUser(user, nickserv, 1, msg);
4769 nickserv_reclaim_p(void *data) {
4770 struct userNode *user = data;
4771 struct nick_info *ni = get_nick_info(user->nick);
4773 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4777 check_user_nick(struct userNode *user) {
4778 //check if this user is a pending LOC user
4779 if(pendingLOCUsers) {
4780 struct pendingLOCUser *pending, *next, *prev = NULL;
4782 for(pending = pendingLOCUsers; pending; pending = next) {
4783 next = pending->next;
4785 if(user->handle_info == pending->handle_info) {
4786 pending->authlog->user = user;
4787 free((char*) pending->authlog->hostmask);
4788 pending->authlog->hostmask = generate_hostmask(user, GENMASK_USENICK|GENMASK_STRICT_IDENT|GENMASK_NO_HIDING|GENMASK_STRICT_HOST);
4790 } else if(now - pending->time > 10)
4796 pendingLOCUsers = next;
4801 struct nick_info *ni;
4802 user->modes &= ~FLAGS_REGNICK;
4803 if (!(ni = get_nick_info(user->nick)))
4805 if (user->handle_info == ni->owner) {
4806 user->modes |= FLAGS_REGNICK;
4810 if (nickserv_conf.warn_nick_owned)
4811 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4812 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4814 if (nickserv_conf.auto_reclaim_delay)
4815 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4817 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4821 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4823 struct handle_info *hi = NULL;
4826 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4827 if ((hi == NULL) && (serial != 0)) {
4829 inttobase64(id, serial, IDLEN);
4830 hi = dict_find(nickserv_id_dict, id, NULL);
4834 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4835 && (timestamp != hi->registered)) {
4838 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4841 set_user_handle_info(user, hi, 0);
4843 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4848 handle_nick_change(struct userNode *user, const char *old_nick)
4850 struct handle_info *hi;
4852 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4853 dict_remove(nickserv_allow_auth_dict, old_nick);
4854 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4856 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4857 check_user_nick(user);
4861 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), const char *why)
4863 if(user->handle_info) {
4864 //check if theres an open authlog entry
4865 struct authlogEntry *authlog;
4866 for(authlog = user->handle_info->authlog; authlog; authlog = authlog->next) {
4867 if(authlog->user == user) {
4868 authlog->user = NULL;
4869 authlog->logout_time = now;
4870 authlog->quit_reason = strdup(why);
4875 dict_remove(nickserv_allow_auth_dict, user->nick);
4876 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4877 set_user_handle_info(user, NULL, 0);
4880 static struct modcmd *
4881 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4883 if (min_level > 0) {
4885 sprintf(buf, "%u", min_level);
4886 if (must_be_qualified) {
4887 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4889 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4891 } else if (min_level == 0) {
4892 if (must_be_qualified) {
4893 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4895 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4898 if (must_be_qualified) {
4899 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4901 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4907 nickserv_db_cleanup(void)
4909 unreg_del_user_func(nickserv_remove_user);
4910 userList_clean(&curr_helpers);
4911 policer_params_delete(nickserv_conf.auth_policer_params);
4912 dict_delete(nickserv_handle_dict);
4913 dict_delete(nickserv_nick_dict);
4914 dict_delete(nickserv_opt_dict);
4915 dict_delete(nickserv_allow_auth_dict);
4916 dict_delete(nickserv_email_dict);
4917 dict_delete(nickserv_id_dict);
4918 dict_delete(nickserv_conf.weak_password_dict);
4919 free(auth_func_list);
4920 free(unreg_func_list);
4922 free(allowauth_func_list);
4923 free(handle_merge_func_list);
4924 free(failpw_func_list);
4925 if (nickserv_conf.valid_handle_regex_set)
4926 regfree(&nickserv_conf.valid_handle_regex);
4927 if (nickserv_conf.valid_nick_regex_set)
4928 regfree(&nickserv_conf.valid_nick_regex);
4929 struct pendingLOCUser *pending, *next;
4930 for(pending = pendingLOCUsers; pending; pending = next) {
4931 next = pending->next;
4934 pendingLOCUsers = NULL;
4938 init_nickserv(const char *nick)
4941 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4942 reg_new_user_func(check_user_nick);
4943 reg_nick_change_func(handle_nick_change);
4944 reg_del_user_func(nickserv_remove_user);
4945 reg_account_func(handle_account);
4947 /* set up handle_inverse_flags */
4948 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4949 for (i=0; handle_flags[i]; i++) {
4950 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4951 flag_access_levels[i] = 0;
4954 conf_register_reload(nickserv_conf_read);
4955 nickserv_opt_dict = dict_new();
4956 nickserv_email_dict = dict_new();
4957 dict_set_free_keys(nickserv_email_dict, free);
4958 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4960 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4961 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4962 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4963 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4964 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4965 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4966 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4967 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4968 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4969 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4970 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4971 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4972 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4973 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4974 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4975 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4976 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4977 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4978 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4979 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4980 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4981 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4982 if (!nickserv_conf.disable_nicks) {
4983 /* nick management commands */
4984 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4985 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4986 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4987 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4988 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4989 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4991 if (nickserv_conf.email_enabled) {
4992 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4993 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4994 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4995 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4996 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4997 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4999 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
5000 /* miscellaneous commands */
5001 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
5002 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
5003 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
5004 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
5005 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
5006 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
5007 nickserv_define_func("AUTHLOG", cmd_authlog, -1, 1, 0);
5008 nickserv_define_func("OAUTHLOG", cmd_oauthlog, 0, 1, 0);
5010 dict_insert(nickserv_opt_dict, "INFO", opt_info);
5011 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
5012 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
5013 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
5014 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
5015 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
5016 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
5017 dict_insert(nickserv_opt_dict, "PASS", opt_password);
5018 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
5019 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
5020 dict_insert(nickserv_opt_dict, "WEBSITE", opt_website);
5021 dict_insert(nickserv_opt_dict, "DEVNULL", opt_devnull);
5022 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
5023 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
5024 dict_insert(nickserv_opt_dict, "STAFF", opt_staff_level);
5025 dict_insert(nickserv_opt_dict, "STAFF_LEVEL", opt_staff_level);
5026 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
5027 if (titlehost_suffix) {
5028 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
5029 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
5030 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
5032 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
5033 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
5034 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
5035 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
5037 nickserv_handle_dict = dict_new();
5038 dict_set_free_keys(nickserv_handle_dict, free);
5039 dict_set_free_data(nickserv_handle_dict, free_handle_info);
5041 nickserv_id_dict = dict_new();
5042 dict_set_free_keys(nickserv_id_dict, free);
5044 nickserv_nick_dict = dict_new();
5045 dict_set_free_data(nickserv_nick_dict, free);
5047 nickserv_allow_auth_dict = dict_new();
5049 userList_init(&curr_helpers);
5052 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
5053 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
5054 nickserv_service = service_register(nickserv);
5056 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
5057 reg_exit_func(nickserv_db_cleanup);
5058 if(nickserv_conf.handle_expire_frequency)
5059 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
5060 message_register_table(msgtab);