1 /* nickserv.c - Nick/authentication service
2 * Copyright 2000-2008 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for gag_create(), opserv_bad_channel() */
33 # include "rx/rxposix.h"
36 #define NICKSERV_CONF_NAME "services/nickserv"
38 #define KEY_DISABLE_NICKS "disable_nicks"
39 #define KEY_DEFAULT_HOSTMASK "default_hostmask"
40 #define KEY_NICKS_PER_HANDLE "nicks_per_handle"
41 #define KEY_NICKS_PER_ACCOUNT "nicks_per_account"
42 #define KEY_PASSWORD_MIN_LENGTH "password_min_length"
43 #define KEY_PASSWORD_MIN_DIGITS "password_min_digits"
44 #define KEY_PASSWORD_MIN_UPPER "password_min_upper"
45 #define KEY_PASSWORD_MIN_LOWER "password_min_lower"
46 #define KEY_VALID_HANDLE_REGEX "valid_handle_regex"
47 #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex"
48 #define KEY_VALID_NICK_REGEX "valid_nick_regex"
49 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
50 #define KEY_MODOPER_LEVEL "modoper_level"
51 #define KEY_SET_EPITHET_LEVEL "set_epithet_level"
52 #define KEY_SET_TITLE_LEVEL "set_title_level"
53 #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level"
54 #define KEY_SET_FAKEIDENT_LEVEL "set_fakeident_level"
55 #define KEY_TITLEHOST_SUFFIX "titlehost_suffix"
56 #define KEY_FLAG_LEVELS "flag_levels"
57 #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq"
58 #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq"
59 #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay"
60 #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay"
61 #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay"
62 #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay"
63 #define KEY_DICT_FILE "dict_file"
64 #define KEY_NICK "nick"
65 #define KEY_LANGUAGE "language"
66 #define KEY_AUTOGAG_ENABLED "autogag_enabled"
67 #define KEY_AUTOGAG_DURATION "autogag_duration"
68 #define KEY_AUTH_POLICER "auth_policer"
69 #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level"
70 #define KEY_EMAIL_ENABLED "email_enabled"
71 #define KEY_EMAIL_REQUIRED "email_required"
72 #define KEY_COOKIE_TIMEOUT "cookie_timeout"
73 #define KEY_ACCOUNTS_PER_EMAIL "accounts_per_email"
74 #define KEY_EMAIL_SEARCH_LEVEL "email_search_level"
75 #define KEY_OUNREGISTER_INACTIVE "ounregister_inactive"
76 #define KEY_OUNREGISTER_FLAGS "ounregister_flags"
77 #define KEY_HANDLE_TS_MODE "account_timestamp_mode"
78 #define KEY_MAX_AUTHLOG_LEN "max_authlog_len"
81 #define KEY_PASSWD "passwd"
82 #define KEY_NICKS "nicks"
83 #define KEY_MASKS "masks"
84 #define KEY_OPSERV_LEVEL "opserv_level"
85 #define KEY_FLAGS "flags"
86 #define KEY_REGISTER_ON "register"
87 #define KEY_LAST_SEEN "lastseen"
88 #define KEY_INFO "info"
89 #define KEY_DEVNULL "devnull"
90 #define KEY_WEBSITE "website"
91 #define KEY_USERLIST_STYLE "user_style"
92 #define KEY_SCREEN_WIDTH "screen_width"
93 #define KEY_LAST_AUTHED_HOST "last_authed_host"
94 #define KEY_LAST_QUIT_HOST "last_quit_host"
95 #define KEY_EMAIL_ADDR "email_addr"
96 #define KEY_COOKIE "cookie"
97 #define KEY_COOKIE_DATA "data"
98 #define KEY_COOKIE_TYPE "type"
99 #define KEY_COOKIE_EXPIRES "expires"
100 #define KEY_ACTIVATION "activation"
101 #define KEY_PASSWORD_CHANGE "password change"
102 #define KEY_EMAIL_CHANGE "email change"
103 #define KEY_ALLOWAUTH "allowauth"
104 #define KEY_EPITHET "epithet"
105 #define KEY_TABLE_WIDTH "table_width"
106 #define KEY_MAXLOGINS "maxlogins"
107 #define KEY_FAKEHOST "fakehost"
108 #define KEY_FAKEIDENT "fakeident"
109 #define KEY_NOTES "notes"
110 #define KEY_NOTE_EXPIRES "expires"
111 #define KEY_NOTE_SET "set"
112 #define KEY_NOTE_SETTER "setter"
113 #define KEY_NOTE_NOTE "note"
114 #define KEY_KARMA "karma"
115 #define KEY_AUTHLOG "authlog"
116 #define KEY_AUTHLOG_LOGIN_TIME "login_time"
117 #define KEY_AUTHLOG_LOGOUT_TIME "logout_time"
118 #define KEY_AUTHLOG_HOSTMASK "hostmask"
119 #define KEY_AUTHLOG_QUIT_REASON "quit_reason"
121 #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
123 #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME)
124 #define OPTION_FUNC(NAME) int NAME(struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[])
125 typedef OPTION_FUNC(option_func_t);
127 DEFINE_LIST(handle_info_list, struct handle_info*)
129 #define NICKSERV_MIN_PARMS(N) do { \
131 reply("MSG_MISSING_PARAMS", argv[0]); \
132 svccmd_send_help(user, nickserv, cmd); \
136 struct userNode *nickserv;
137 struct userList curr_helpers;
138 const char *handle_flags = HANDLE_FLAGS;
140 static struct module *nickserv_module;
141 static struct service *nickserv_service;
142 static struct log_type *NS_LOG;
143 static dict_t nickserv_handle_dict; /* contains struct handle_info* */
144 static dict_t nickserv_id_dict; /* contains struct handle_info* */
145 static dict_t nickserv_nick_dict; /* contains struct nick_info* */
146 static dict_t nickserv_opt_dict; /* contains option_func_t* */
147 static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */
148 static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */
149 static char handle_inverse_flags[256];
150 static unsigned int flag_access_levels[32];
151 static const struct message_entry msgtab[] = {
152 { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." },
153 { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." },
154 { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." },
155 { "NSMSG_PASSWORD_DICTIONARY", "Your password should not be the word \"password\", or any other dictionary word." },
156 { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." },
157 { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." },
158 { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." },
159 { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." },
160 { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." },
161 { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." },
162 { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." },
163 { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." },
164 { "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." },
165 { "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." },
166 { "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." },
167 { "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." },
168 { "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." },
169 { "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." },
170 { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." },
171 { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." },
172 { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." },
173 { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." },
174 { "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." },
175 { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." },
176 { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." },
177 { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" },
178 { "NSMSG_EMAIL_OVERUSED", "There are already the maximum number of accounts associated with that email address." },
179 { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." },
180 { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." },
181 { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." },
182 { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." },
183 { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." },
184 { "NSMSG_ATE_COOKIE_FOREIGN", "I ate the cookie for account $b%s$b. It may now have another." },
185 { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." },
186 { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." },
187 { "NSMSG_REGISTER_BAD_NICKMASK", "Could not recognize $b%s$b as either a current nick or a hostmask." },
188 { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." },
189 { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" },
190 { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." },
191 { "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)" },
192 { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." },
193 { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." },
194 { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." },
195 { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." },
196 { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." },
197 { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." },
198 { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." },
199 { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." },
200 { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." },
201 { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." },
202 { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." },
203 { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." },
204 { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." },
205 { "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." },
206 { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." },
207 { "NSMSG_FAKEIDENT_INVALID", "Fake idents must be shorter than %d characters." },
208 { "NSMSG_FAKEMASK_INVALID", "Fake ident@hosts must be shorter than %d characters." },
209 { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" },
210 { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" },
211 { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" },
212 { "NSMSG_HANDLEINFO_LASTSEEN", " Last seen: %s" },
213 { "NSMSG_HANDLEINFO_LASTSEEN_NOW", " Last seen: Right now!" },
214 { "NSMSG_HANDLEINFO_KARMA", " Karma: %d" },
215 { "NSMSG_HANDLEINFO_VACATION", " On vacation." },
216 { "NSMSG_HANDLEINFO_EMAIL_ADDR", " Email address: %s" },
217 { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", " Cookie: There is currently an activation cookie issued for this account" },
218 { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", " Cookie: There is currently a password change cookie issued for this account" },
219 { "NSMSG_HANDLEINFO_COOKIE_EMAIL", " Cookie: There is currently an email change cookie issued for this account" },
220 { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", " Cookie: There is currently an allowauth cookie issued for this account" },
221 { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", " Cookie: There is currently an unknown cookie issued for this account" },
222 { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" },
223 { "NSMSG_HANDLEINFO_DEVNULL", " DevNull Class: %s" },
224 { "NSMSG_HANDLEINFO_WEBSITE", " Website: %s" },
225 { "NSMSG_HANDLEINFO_ACCESS", " Access: %i" },
226 { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" },
227 { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" },
228 { "NSMSG_HANDLEINFO_FAKEIDENT", " Fake ident: %s" },
229 { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" },
230 { "NSMSG_HANDLEINFO_FAKEIDENTHOST", " Fake host: %s@%s" },
231 { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" },
232 { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" },
233 { "NSMSG_HANDLEINFO_NOTE_EXPIRES", " Note %d (%s ago by %s, expires %s): %s" },
234 { "NSMSG_HANDLEINFO_NOTE", " Note %d (%s ago by %s): %s" },
235 { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", " Last quit hostmask: Unknown" },
236 { "NSMSG_HANDLEINFO_NICKS", " Nickname(s): %s" },
237 { "NSMSG_HANDLEINFO_MASKS", " Hostmask(s): %s" },
238 { "NSMSG_HANDLEINFO_CHANNELS", " Channel(s): %s" },
239 { "NSMSG_HANDLEINFO_CURRENT", " Current nickname(s): %s" },
240 { "NSMSG_HANDLEINFO_DNR", " Do-not-register (by %s): %s" },
241 { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." },
242 { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." },
243 { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." },
244 { "NSMSG_NOTE_EXPIRES", "Note %d (%s ago by %s, expires %s): %s" },
245 { "NSMSG_NOTE", "Note %d (%s ago by %s): %s" },
246 { "NSMSG_NOTE_COUNT", "%u note(s) for %s." },
247 { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." },
248 { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" },
249 { "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)." },
250 { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." },
251 { "NSMSG_AUTH_SUCCESS", "I recognize you." },
252 { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." },
253 { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." },
254 { "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." },
255 { "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." },
256 { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." },
257 { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." },
258 { "NSMSG_MUST_AUTH", "You must be authenticated first." },
259 { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." },
260 { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." },
261 { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." },
262 { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." },
263 { "NSMSG_PASS_SUCCESS", "Password changed." },
264 { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." },
265 { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." },
266 { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." },
267 { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." },
268 { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." },
269 { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." },
270 { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." },
271 { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." },
272 { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." },
273 { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." },
274 { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." },
275 { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." },
276 { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." },
277 { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." },
278 { "NSMSG_UNREGISTER_MUST_FORCE", "Account $b%s$b is not inactive or has special flags set; use FORCE to unregister it." },
279 { "NSMSG_UNREGISTER_CANNOT_FORCE", "Account $b%s$b is not inactive or has special flags set; have an IRCOp use FORCE to unregister it." },
280 { "NSMSG_UNREGISTER_NODELETE", "Account $b%s$b is protected from unregistration." },
281 { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." },
282 { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." },
283 { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." },
284 { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." },
285 { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." },
286 { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." },
287 { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." },
288 { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." },
289 { "NSMSG_EXCESSIVE_DURATION", "$b%s$b is too long for this command." },
290 { "NSMSG_NOTE_ADDED", "Note $b%d$b added to $b%s$b." },
291 { "NSMSG_NOTE_REMOVED", "Note $b%d$b removed from $b%s$b." },
292 { "NSMSG_NO_SUCH_NOTE", "Account $b%s$b does not have a note with ID $b%d$b." },
293 { "NSMSG_NO_ACCESS", "Access denied." },
294 { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." },
295 { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." },
296 { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." },
297 { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." },
298 { "NSMSG_DB_MERGED", "$N merged DB from %s (in %lu.%03lu seconds)." },
299 { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." },
300 { "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." },
301 { "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." },
302 { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." },
303 { "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." },
304 { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" },
305 { "NSMSG_SEARCH_MATCH", "Match: %s" },
306 { "NSMSG_INVALID_ACTION", "%s is an invalid search action." },
307 { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." },
308 { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." },
309 { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." },
310 { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." },
311 { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." },
312 { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." },
313 { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." },
314 { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." },
315 { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." },
316 { "NSMSG_SETTING_LIST", "$b$N account settings:$b" },
317 { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." },
318 { "NSMSG_SET_INFO", "$bINFO: $b%s" },
319 { "NSMSG_SET_DEVNULL", "$bDEVNULL: $b%s" },
320 { "NSMSG_SET_AUTOHIDE", "$bAUTOHIDE: $b%s" },
321 { "NSMSG_SET_WEBSITE", "$bWEBSITE: $b%s" },
322 { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" },
323 { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" },
324 { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" },
325 { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" },
326 { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" },
327 { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" },
328 { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" },
329 { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" },
330 { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" },
331 { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" },
332 { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" },
333 { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" },
334 { "NSMSG_SET_TITLE", "$bTITLE: $b%s" },
335 { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" },
336 { "NSMSG_SET_FAKEIDENT", "$bFAKEIDENT: $b%s" },
337 { "NSMSG_SET_FAKEIDENTHOST", "$bFAKEHOST: $b%s@%s" },
338 { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." },
339 { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" },
340 { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" },
341 { "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." },
342 { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" },
343 { "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." },
344 { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" },
345 { "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." },
346 { "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." },
347 { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" },
348 { "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." },
349 { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" },
350 { "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." },
351 { "CHECKPASS_YES", "Yes." },
352 { "CHECKPASS_NO", "No." },
353 { "CHECKEMAIL_NOT_SET", "No email set." },
354 { "CHECKEMAIL_YES", "Yes." },
355 { "CHECKEMAIL_NO", "No." },
359 enum reclaim_action {
365 static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action);
366 static void nickserv_reclaim_p(void *data);
367 static int nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask);
369 enum handle_ts_mode {
375 unsigned int disable_nicks : 1;
376 unsigned int valid_handle_regex_set : 1;
377 unsigned int valid_nick_regex_set : 1;
378 unsigned int autogag_enabled : 1;
379 unsigned int email_enabled : 1;
380 unsigned int email_required : 1;
381 unsigned int default_hostmask : 1;
382 unsigned int warn_nick_owned : 1;
383 unsigned int warn_clone_auth : 1;
384 unsigned long nicks_per_handle;
385 unsigned long password_min_length;
386 unsigned long password_min_digits;
387 unsigned long password_min_upper;
388 unsigned long password_min_lower;
389 unsigned long db_backup_frequency;
390 unsigned long handle_expire_frequency;
391 unsigned long autogag_duration;
392 unsigned long email_visible_level;
393 unsigned long cookie_timeout;
394 unsigned long handle_expire_delay;
395 unsigned long nochan_handle_expire_delay;
396 unsigned long modoper_level;
397 unsigned long set_epithet_level;
398 unsigned long set_title_level;
399 unsigned long set_fakehost_level;
400 unsigned long set_fakeident_level;
401 unsigned long handles_per_email;
402 unsigned long email_search_level;
403 const char *network_name;
404 regex_t valid_handle_regex;
405 regex_t valid_nick_regex;
406 dict_t weak_password_dict;
407 struct policer_params *auth_policer_params;
408 enum reclaim_action reclaim_action;
409 enum reclaim_action auto_reclaim_action;
410 enum handle_ts_mode handle_ts_mode;
411 unsigned long auto_reclaim_delay;
412 unsigned char default_maxlogins;
413 unsigned char hard_maxlogins;
414 unsigned long ounregister_inactive;
415 unsigned long ounregister_flags;
416 unsigned int max_authlog_len;
419 struct pendingLOCUser {
420 struct handle_info *handle_info;
422 struct authlogEntry *authlog;
423 struct pendingLOCUser *next;
426 const char *titlehost_suffix = NULL;
427 static struct pendingLOCUser *pendingLOCUsers = NULL;
429 /* We have 2^32 unique account IDs to use. */
430 unsigned long int highest_id = 0;
432 #define WALK_NOTES(HANDLE, PREV, NOTE) \
433 for (PREV = NULL, NOTE = (HANDLE)->notes; NOTE != NULL; PREV = NOTE, NOTE = NOTE->next) \
434 if (NOTE->expires && NOTE->expires < now) { \
435 if (PREV) PREV->next = NOTE->next; else (HANDLE)->notes = NOTE->next; \
437 if (!(NOTE = PREV ? PREV : (HANDLE)->notes)) break; \
441 canonicalize_hostmask(char *mask)
443 char *out = mask, *temp;
444 if ((temp = strchr(mask, '!'))) {
446 while (*temp) *out++ = *temp++;
452 static struct handle_info *
453 register_handle(const char *handle, const char *passwd, unsigned long id)
455 struct handle_info *hi;
457 char id_base64[IDLEN + 1];
460 /* Assign a unique account ID to the account; note that 0 is
461 an invalid account ID. 1 is therefore the first account ID. */
463 id = 1 + highest_id++;
465 /* Note: highest_id is and must always be the highest ID. */
466 if (id > highest_id) {
470 inttobase64(id_base64, id, IDLEN);
472 /* Make sure an account with the same ID doesn't exist. If a
473 duplicate is found, log some details and assign a new one.
474 This should be impossible, but it never hurts to expect it. */
475 if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) {
476 log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle);
481 hi = calloc(1, sizeof(*hi));
482 hi->userlist_style = HI_DEFAULT_STYLE;
483 hi->handle = strdup(handle);
484 safestrncpy(hi->passwd, passwd, sizeof(hi->passwd));
486 dict_insert(nickserv_handle_dict, hi->handle, hi);
491 dict_insert(nickserv_id_dict, strdup(id_base64), hi);
497 register_nick(const char *nick, struct handle_info *owner)
499 struct nick_info *ni;
500 ni = malloc(sizeof(struct nick_info));
501 safestrncpy(ni->nick, nick, sizeof(ni->nick));
503 ni->next = owner->nicks;
505 dict_insert(nickserv_nick_dict, ni->nick, ni);
509 delete_nick(struct nick_info *ni)
511 struct nick_info *last, *next;
512 struct userNode *user;
513 /* Check to see if we should mark a user as unregistered. */
514 if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) {
515 user->modes &= ~FLAGS_REGNICK;
518 /* Remove ni from the nick_info linked list. */
519 if (ni == ni->owner->nicks) {
520 ni->owner->nicks = ni->next;
522 last = ni->owner->nicks;
528 last->next = next->next;
530 dict_remove(nickserv_nick_dict, ni->nick);
533 static unreg_func_t *unreg_func_list;
534 static unsigned int unreg_func_size = 0, unreg_func_used = 0;
537 reg_unreg_func(unreg_func_t func)
539 if (unreg_func_used == unreg_func_size) {
540 if (unreg_func_size) {
541 unreg_func_size <<= 1;
542 unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t));
545 unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t));
548 unreg_func_list[unreg_func_used++] = func;
552 nickserv_free_cookie(void *data)
554 struct handle_cookie *cookie = data;
555 if (cookie->hi) cookie->hi->cookie = NULL;
556 if (cookie->data) free(cookie->data);
561 free_handle_info(void *vhi)
563 struct handle_info *hi = vhi;
566 inttobase64(id, hi->id, IDLEN);
567 dict_remove(nickserv_id_dict, id);
569 free_string_list(hi->masks);
573 delete_nick(hi->nicks);
581 timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0);
582 nickserv_free_cookie(hi->cookie);
585 struct handle_note *note = hi->notes;
586 hi->notes = note->next;
589 if (hi->email_addr) {
590 struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL);
591 handle_info_list_remove(hil, hi);
593 dict_remove(nickserv_email_dict, hi->email_addr);
595 struct authlogEntry *authlog, *next;
596 for(authlog = hi->authlog; authlog; authlog = next) {
597 next = authlog->next;
598 struct pendingLOCUser *pending, *prev_pending = NULL;
599 for(pending = pendingLOCUsers; pending; pending = pending->next) {
600 if(pending->authlog == authlog) {
602 prev_pending->next = pending->next;
604 pendingLOCUsers = pending->next;
608 prev_pending = pending;
610 free((char *) authlog->hostmask);
611 if(authlog->quit_reason)
612 free((char *) authlog->quit_reason);
618 static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp);
621 nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify)
625 for (n=0; n<unreg_func_used; n++)
626 unreg_func_list[n](notify, hi);
628 set_user_handle_info(hi->users, NULL, 0);
630 if (nickserv_conf.disable_nicks)
631 send_message(notify, nickserv, "NSMSG_UNREGISTER_SUCCESS", hi->handle);
633 send_message(notify, nickserv, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle);
635 dict_remove(nickserv_handle_dict, hi->handle);
639 get_handle_info(const char *handle)
641 return dict_find(nickserv_handle_dict, handle, 0);
645 get_nick_info(const char *nick)
647 return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0);
651 find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except)
656 for (nn=0; nn<channel->members.used; ++nn) {
657 mn = channel->members.list[nn];
658 if ((mn->user != except) && (mn->user->handle_info == handle))
665 oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) {
666 if (!user->handle_info) {
668 send_message(user, bot, "MSG_AUTHENTICATE");
672 if (!IsOper(user) && (!IsHelping(user) || min_level)) {
674 send_message(user, bot, "NSMSG_NO_ACCESS");
678 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
680 send_message(user, bot, "MSG_OPER_SUSPENDED");
684 if (user->handle_info->opserv_level < min_level) {
686 send_message(user, bot, "NSMSG_NO_ACCESS");
694 is_valid_handle(const char *handle)
696 struct userNode *user;
697 /* cant register a juped nick/service nick as handle, to prevent confusion */
698 user = GetUserH(handle);
699 if (user && IsLocal(user))
701 /* check against maximum length */
702 if (strlen(handle) > NICKSERV_HANDLE_LEN)
704 /* for consistency, only allow account names that could be nicks */
705 if (!is_valid_nick(handle))
707 /* disallow account names that look like bad words */
708 if (opserv_bad_channel(handle))
710 /* test either regex or containing all valid chars */
711 if (nickserv_conf.valid_handle_regex_set) {
712 int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0);
715 buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0;
716 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
720 return !handle[strspn(handle, NICKSERV_VALID_CHARS)];
725 is_registerable_nick(const char *nick)
727 /* make sure it could be used as an account name */
728 if (!is_valid_handle(nick))
731 if (strlen(nick) > NICKLEN)
733 /* test either regex or as valid handle */
734 if (nickserv_conf.valid_nick_regex_set) {
735 int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0);
738 buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0;
739 log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
747 is_valid_email_addr(const char *org_email)
749 char email[strlen(org_email)+1];
750 strcpy(email, org_email);
751 //validate email address
752 //1st check: there need to be one @
753 char *p1 = strchr(email, '@');
754 if(!p1 || strchr(p1+1, '@')) return 0;
756 //2nd check: username (bevore @) must be at least 1 char long and out of part_chars
757 char *part_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._%+-";
759 if(p1 - email == 0) return 0;
760 for(i = 0; i < (p1 - email); i++) {
761 if(!strchr(part_chars, email[i])) return 0;
763 //3rd check: there need to be at least 1 dot in the domain part and all characters out of part_chars
764 part_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-";
773 } else if(!strchr(part_chars, *p1))
780 //4th check: TLD must be <= 5 chars, no special chars
795 visible_email_addr(struct userNode *user, struct handle_info *hi)
797 if (hi->email_addr) {
798 if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) {
799 return hi->email_addr;
809 smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name)
811 struct handle_info *hi;
812 struct userNode *target;
816 if (!(hi = get_handle_info(++name))) {
817 send_message(user, service, "MSG_HANDLE_UNKNOWN", name);
822 if (!(target = GetUserH(name))) {
823 send_message(user, service, "MSG_NICK_UNKNOWN", name);
826 if (IsLocal(target)) {
827 if (IsService(target))
828 send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick);
830 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
833 if (!(hi = target->handle_info)) {
834 send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick);
842 oper_outranks(struct userNode *user, struct handle_info *hi) {
843 if (user->handle_info->opserv_level > hi->opserv_level)
845 if (user->handle_info->opserv_level == hi->opserv_level) {
846 if ((user->handle_info->opserv_level == 1000)
847 || (user->handle_info == hi)
848 || ((user->handle_info->opserv_level == 0)
849 && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER))
850 && HANDLE_FLAGGED(user->handle_info, HELPING))) {
854 send_message(user, nickserv, "MSG_USER_OUTRANKED", hi->handle);
858 static struct handle_info *
859 get_victim_oper(struct userNode *user, const char *target)
861 struct handle_info *hi;
862 if (!(hi = smart_get_handle_info(nickserv, user, target)))
864 if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) {
865 send_message(user, nickserv, "MSG_OPER_SUSPENDED");
868 return oper_outranks(user, hi) ? hi : NULL;
872 valid_user_for(struct userNode *user, struct handle_info *hi)
876 /* If no hostmasks on the account, allow it. */
877 if (!hi->masks->used || IsDummy(user))
879 /* If any hostmask matches, allow it. */
880 for (ii=0; ii<hi->masks->used; ii++)
881 if (user_matches_glob(user, hi->masks->list[ii], 0))
883 /* If they are allowauthed to this account, allow it (removing the aa). */
884 if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) {
885 dict_remove(nickserv_allow_auth_dict, user->nick);
888 /* The user is not allowed to use this account. */
893 is_secure_password(const char *handle, const char *pass, struct userNode *user)
896 unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0;
900 if (len < nickserv_conf.password_min_length) {
902 send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length);
905 if (!irccasecmp(pass, handle)) {
907 send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT");
910 dict_find(nickserv_conf.weak_password_dict, pass, &p);
913 send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY");
916 for (i=0; i<len; i++) {
917 if (isdigit(pass[i]))
919 if (isupper(pass[i]))
921 if (islower(pass[i]))
924 if ((cnt_lower < nickserv_conf.password_min_lower)
925 || (cnt_upper < nickserv_conf.password_min_upper)
926 || (cnt_digits < nickserv_conf.password_min_digits)) {
928 send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower);
934 static auth_func_t *auth_func_list;
935 static unsigned int auth_func_size = 0, auth_func_used = 0;
938 reg_auth_func(auth_func_t func)
940 if (auth_func_used == auth_func_size) {
941 if (auth_func_size) {
942 auth_func_size <<= 1;
943 auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t));
946 auth_func_list = malloc(auth_func_size*sizeof(auth_func_t));
949 auth_func_list[auth_func_used++] = func;
952 static handle_rename_func_t *rf_list;
953 static unsigned int rf_list_size, rf_list_used;
956 reg_handle_rename_func(handle_rename_func_t func)
958 if (rf_list_used == rf_list_size) {
961 rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0]));
964 rf_list = malloc(rf_list_size*sizeof(rf_list[0]));
967 rf_list[rf_list_used++] = func;
971 generate_fakehost(struct handle_info *handle)
973 extern const char *hidden_host_suffix;
974 static char buffer[HOSTLEN+1];
976 if (!handle->fakehost) {
977 snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix);
979 } else if (handle->fakehost[0] == '.') {
980 /* A leading dot indicates the stored value is actually a title. */
981 snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, titlehost_suffix);
983 } else if (handle->fakehost[0] == '$') {
984 /* A leading $ indicates the stored value begins with the user handle. */
985 snprintf(buffer, sizeof(buffer), "%s%s", handle->handle, handle->fakehost+1);
988 return handle->fakehost;
992 generate_fakeident(struct handle_info *handle, struct userNode *user)
994 static char buffer[USERLEN+1];
996 if (!handle->fakeident) {
999 safestrncpy(buffer, user->ident, sizeof(buffer));
1002 return handle->fakeident;
1006 apply_fakehost(struct handle_info *handle, struct userNode *user)
1008 struct userNode *target;
1009 char *fakehost, *fakeident;
1014 fakehost = generate_fakehost(handle);
1017 fakeident = generate_fakeident(handle, user);
1018 assign_fakehost(user, fakehost, fakeident, 0, 1);
1022 for (target = handle->users; target; target = target->next_authed) {
1023 fakeident = generate_fakeident(handle, target);
1024 assign_fakehost(target, fakehost, fakeident, 0, 1);
1029 set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp)
1032 struct handle_info *old_info;
1034 /* This can happen if somebody uses COOKIE while authed, or if
1035 * they re-auth to their current handle (which is silly, but users
1036 * are like that). */
1037 if (user->handle_info == hi)
1040 if (user->handle_info) {
1041 struct userNode *other;
1044 userList_remove(&curr_helpers, user);
1046 /* remove from next_authed linked list */
1047 if (user->handle_info->users == user) {
1048 user->handle_info->users = user->next_authed;
1049 } else if (user->handle_info->users != NULL) {
1050 for (other = user->handle_info->users;
1051 other->next_authed != user;
1052 other = other->next_authed) ;
1053 other->next_authed = user->next_authed;
1055 /* No users authed to the account - can happen if they get
1056 * killed for authing. */
1058 /* if nobody left on old handle, and they're not an oper, remove !god */
1059 if (!user->handle_info->users && !user->handle_info->opserv_level)
1060 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
1061 /* record them as being last seen at this time */
1062 user->handle_info->lastseen = now;
1063 /* and record their hostmask */
1064 snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname);
1066 old_info = user->handle_info;
1067 user->handle_info = hi;
1068 if (hi && !hi->users && !hi->opserv_level)
1069 HANDLE_CLEAR_FLAG(hi, HELPING);
1070 for (n=0; n<auth_func_used; n++) {
1071 auth_func_list[n](user, old_info);
1076 struct nick_info *ni;
1078 HANDLE_CLEAR_FLAG(hi, FROZEN);
1079 if (nickserv_conf.warn_clone_auth) {
1080 struct userNode *other;
1081 for (other = hi->users; other; other = other->next_authed)
1082 send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname);
1084 user->next_authed = hi->users;
1087 if (IsHelper(user) && !userList_contains(&curr_helpers, user))
1088 userList_append(&curr_helpers, user);
1090 if (hi->fakehost || hi->fakeident || old_info)
1091 apply_fakehost(hi, user);
1094 if (!nickserv_conf.disable_nicks) {
1095 struct nick_info *ni2;
1096 for (ni2 = hi->nicks; ni2; ni2 = ni2->next) {
1097 if (!irccasecmp(user->nick, ni2->nick)) {
1098 user->modes |= FLAGS_REGNICK;
1103 StampUser(user, hi->handle, hi->registered, hi->id);
1106 if ((ni = get_nick_info(user->nick)) && (ni->owner == hi))
1107 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
1109 /* We cannot clear the user's account ID, unfortunately. */
1110 user->next_authed = NULL;
1114 static struct handle_info*
1115 nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth)
1117 struct handle_info *hi;
1118 struct nick_info *ni;
1119 char crypted[MD5_CRYPT_LENGTH];
1121 if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
1122 send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle);
1126 if (!is_secure_password(handle, passwd, user))
1129 cryptpass(passwd, crypted);
1130 hi = register_handle(handle, crypted, 0);
1131 hi->masks = alloc_string_list(1);
1133 hi->language = lang_C;
1134 hi->registered = now;
1136 hi->flags = HI_DEFAULT_FLAGS;
1137 if (settee && !no_auth)
1138 set_user_handle_info(settee, hi, 1);
1141 send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS");
1142 else if (nickserv_conf.disable_nicks)
1143 send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS");
1144 else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL)))
1145 send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER");
1147 register_nick(user->nick, hi);
1148 send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS");
1150 if (settee && (user != settee))
1151 send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle);
1156 nickserv_bake_cookie(struct handle_cookie *cookie)
1158 cookie->hi->cookie = cookie;
1159 timeq_add(cookie->expires, nickserv_free_cookie, cookie);
1163 nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data)
1165 struct handle_cookie *cookie;
1166 char subject[128], body[4096], *misc;
1167 const char *netname, *fmt;
1171 send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle);
1175 cookie = calloc(1, sizeof(*cookie));
1177 cookie->type = type;
1178 cookie->data = cookie_data ? strdup(cookie_data) : NULL;
1179 cookie->expires = now + nickserv_conf.cookie_timeout;
1180 inttobase64(cookie->cookie, rand(), 5);
1181 inttobase64(cookie->cookie+5, rand(), 5);
1183 netname = nickserv_conf.network_name;
1186 switch (cookie->type) {
1188 hi->passwd[0] = 0; /* invalidate password */
1189 send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER");
1190 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT");
1191 snprintf(subject, sizeof(subject), fmt, netname);
1192 fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY");
1193 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1196 case PASSWORD_CHANGE:
1197 send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS");
1198 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT");
1199 snprintf(subject, sizeof(subject), fmt, netname);
1200 fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY");
1201 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1204 misc = hi->email_addr;
1205 hi->email_addr = cookie->data;
1207 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2");
1208 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT");
1209 snprintf(subject, sizeof(subject), fmt, netname);
1210 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW");
1211 snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2);
1212 mail_send(nickserv, hi, subject, body, 1);
1213 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD");
1214 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr);
1216 send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1");
1217 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT");
1218 snprintf(subject, sizeof(subject), fmt, netname);
1219 fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY");
1220 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1221 mail_send(nickserv, hi, subject, body, 1);
1224 hi->email_addr = misc;
1227 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT");
1228 snprintf(subject, sizeof(subject), fmt, netname);
1229 fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY");
1230 snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle);
1231 send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH");
1234 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type);
1238 mail_send(nickserv, hi, subject, body, first_time);
1239 nickserv_bake_cookie(cookie);
1243 nickserv_eat_cookie(struct handle_cookie *cookie)
1245 cookie->hi->cookie = NULL;
1246 timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0);
1247 nickserv_free_cookie(cookie);
1251 nickserv_free_email_addr(void *data)
1253 handle_info_list_clean(data);
1258 nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
1260 struct handle_info_list *hil;
1261 /* Remove from old handle_info_list ... */
1262 if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) {
1263 handle_info_list_remove(hil, hi);
1264 if (!hil->used) dict_remove(nickserv_email_dict, hil->tag);
1265 hi->email_addr = NULL;
1267 /* Add to the new list.. */
1268 if (new_email_addr) {
1269 if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) {
1270 hil = calloc(1, sizeof(*hil));
1271 hil->tag = strdup(new_email_addr);
1272 handle_info_list_init(hil);
1273 dict_insert(nickserv_email_dict, hil->tag, hil);
1275 handle_info_list_append(hil, hi);
1276 hi->email_addr = hil->tag;
1280 static NICKSERV_FUNC(cmd_register)
1283 struct handle_info *hi;
1284 const char *email_addr, *password;
1287 if (!IsOper(user) && !dict_size(nickserv_handle_dict)) {
1288 /* Require the first handle registered to belong to someone +o. */
1289 reply("NSMSG_REQUIRE_OPER");
1293 if (user->handle_info) {
1294 reply("NSMSG_USE_RENAME", user->handle_info->handle);
1298 if (IsRegistering(user)) {
1299 reply("NSMSG_ALREADY_REGISTERING");
1303 if (IsStamped(user)) {
1304 /* Unauthenticated users might still have been stamped
1305 previously and could therefore have a hidden host;
1306 do not allow them to register a new account. */
1307 reply("NSMSG_STAMPED_REGISTER");
1311 NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required);
1313 if (!is_valid_handle(argv[1])) {
1314 reply("NSMSG_BAD_HANDLE", argv[1]);
1318 if ((argc >= 4) && nickserv_conf.email_enabled) {
1319 struct handle_info_list *hil;
1322 /* Remember email address. */
1323 email_addr = argv[3];
1325 /* Check that the email address looks valid.. */
1326 if (!is_valid_email_addr(email_addr)) {
1327 reply("NSMSG_BAD_EMAIL_ADDR");
1331 /* .. and that we are allowed to send to it. */
1332 if ((str = mail_prohibited_address(email_addr))) {
1333 reply("NSMSG_EMAIL_PROHIBITED", email_addr, str);
1337 /* If we do email verify, make sure we don't spam the address. */
1338 if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) {
1340 for (nn=0; nn<hil->used; nn++) {
1341 if (hil->list[nn]->cookie) {
1342 reply("NSMSG_EMAIL_UNACTIVATED");
1346 if (hil->used >= nickserv_conf.handles_per_email) {
1347 reply("NSMSG_EMAIL_OVERUSED");
1360 if (!(hi = nickserv_register(user, user, argv[1], password, no_auth)))
1362 /* Add any masks they should get. */
1363 if (nickserv_conf.default_hostmask) {
1364 string_list_append(hi->masks, strdup("*@*"));
1366 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1367 if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
1368 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1371 /* If they're the first to register, give them level 1000. */
1372 if (dict_size(nickserv_handle_dict) == 1) {
1373 hi->opserv_level = 1000;
1374 reply("NSMSG_ROOT_HANDLE", argv[1]);
1377 /* Set their email address. */
1379 nickserv_set_email_addr(hi, email_addr);
1381 /* If they need to do email verification, tell them. */
1383 nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd);
1385 /* Set registering flag.. */
1386 user->modes |= FLAGS_REGISTERING;
1391 static NICKSERV_FUNC(cmd_oregister)
1394 struct userNode *settee;
1395 struct handle_info *hi;
1396 const char *pass, *email;
1398 NICKSERV_MIN_PARMS(3);
1403 if (!is_valid_handle(argv[1])) {
1404 reply("NSMSG_BAD_HANDLE", argv[1]);
1408 if (argc < 5 || !nickserv_conf.email_enabled) {
1413 if (!is_valid_email_addr(email)) {
1414 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
1417 if ((str = mail_prohibited_address(email))) {
1418 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", email, str);
1423 if (argc < 4 || !strcmp(argv[3], "*")) {
1426 } else if (strchr(argv[3], '@')) {
1427 mask = canonicalize_hostmask(strdup(argv[3]));
1429 settee = GetUserH(argv[4]);
1431 reply("MSG_NICK_UNKNOWN", argv[4]);
1438 } else if ((settee = GetUserH(argv[3]))) {
1439 mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
1441 reply("NSMSG_REGISTER_BAD_NICKMASK", argv[3]);
1444 if (settee && settee->handle_info) {
1445 reply("NSMSG_USER_PREV_AUTH", settee->nick);
1449 if (!(hi = nickserv_register(user, settee, argv[1], pass, 0))) {
1454 string_list_append(hi->masks, mask);
1456 nickserv_set_email_addr(hi, email);
1460 static NICKSERV_FUNC(cmd_handleinfo)
1463 unsigned int i, pos=0, herelen;
1464 struct userNode *target, *next_un;
1465 struct handle_info *hi;
1466 const char *nsmsg_none;
1470 if (!(hi = user->handle_info)) {
1471 reply("NSMSG_MUST_AUTH");
1474 } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
1478 nsmsg_none = handle_find_message(hi, "MSG_NONE");
1479 reply("NSMSG_HANDLEINFO_ON", hi->handle);
1480 feh = hi->registered;
1481 reply("NSMSG_HANDLEINFO_REGGED", ctime(&feh));
1484 intervalString(buff, now - hi->lastseen, user->handle_info);
1485 reply("NSMSG_HANDLEINFO_LASTSEEN", buff);
1487 reply("NSMSG_HANDLEINFO_LASTSEEN_NOW");
1490 reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none));
1491 if ((oper_has_access(user, cmd->parent->bot, 200, 1)) || IsNetworkHelper(user))
1492 reply("NSMSG_HANDLEINFO_DEVNULL", (hi->devnull ? hi->devnull : nsmsg_none));
1493 if (user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1494 reply("NSMSG_HANDLEINFO_WEBSITE", (hi->website ? hi->website : nsmsg_none));
1495 if(hi->opserv_level > 0 && user->handle_info && HANDLE_FLAGGED(user->handle_info, BOT))
1496 reply("NSMSG_HANDLEINFO_ACCESS", hi->opserv_level);
1497 if (HANDLE_FLAGGED(hi, FROZEN))
1498 reply("NSMSG_HANDLEINFO_VACATION");
1500 if (oper_has_access(user, cmd->parent->bot, 0, 1)) {
1501 struct do_not_register *dnr;
1502 if ((dnr = chanserv_is_dnr(NULL, hi)))
1503 reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason);
1504 if ((user->handle_info->opserv_level < 900) && !oper_outranks(user, hi))
1506 } else if (hi != user->handle_info)
1510 reply("NSMSG_HANDLEINFO_KARMA", hi->karma);
1512 if (nickserv_conf.email_enabled)
1513 reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi));
1517 switch (hi->cookie->type) {
1518 case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break;
1519 case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break;
1520 case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break;
1521 case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break;
1522 default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
1527 if (oper_has_access(user, cmd->parent->bot, 601, 1))
1528 reply("NSMSG_HANDLEINFO_ID", hi->id);
1530 if (oper_has_access(user, cmd->parent->bot, 0, 1) || IsStaff(user)) {
1532 reply("NSMSG_HANDLEINFO_NO_NOTES");
1534 struct handle_note *prev, *note;
1536 WALK_NOTES(hi, prev, note) {
1537 char set_time[INTERVALLEN];
1538 intervalString(set_time, now - note->set, user->handle_info);
1539 if (note->expires) {
1540 char exp_time[INTERVALLEN];
1541 intervalString(exp_time, note->expires - now, user->handle_info);
1542 reply("NSMSG_HANDLEINFO_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1544 reply("NSMSG_HANDLEINFO_NOTE", note->id, set_time, note->setter, note->note);
1551 unsigned long flen = 1;
1552 char flags[34]; /* 32 bits possible plus '+' and '\0' */
1554 for (i=0, flen=1; handle_flags[i]; i++)
1555 if (hi->flags & 1 << i)
1556 flags[flen++] = handle_flags[i];
1558 reply("NSMSG_HANDLEINFO_FLAGS", flags);
1560 reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none);
1563 if (HANDLE_FLAGGED(hi, SUPPORT_HELPER)
1564 || HANDLE_FLAGGED(hi, NETWORK_HELPER)
1565 || (hi->opserv_level > 0)) {
1566 reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none));
1569 if (hi->fakeident && hi->fakehost)
1570 reply("NSMSG_HANDLEINFO_FAKEIDENTHOST", hi->fakeident, hi->fakehost);
1571 else if (hi->fakeident)
1572 reply("NSMSG_HANDLEINFO_FAKEIDENT", hi->fakeident);
1573 else if (hi->fakehost)
1574 reply("NSMSG_HANDLEINFO_FAKEHOST", hi->fakehost);
1576 if (hi->last_quit_host[0])
1577 reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host);
1579 reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN");
1581 if (nickserv_conf.disable_nicks) {
1582 /* nicks disabled; don't show anything about registered nicks */
1583 } else if (hi->nicks) {
1584 struct nick_info *ni, *next_ni;
1585 for (ni = hi->nicks; ni; ni = next_ni) {
1586 herelen = strlen(ni->nick);
1587 if (pos + herelen + 1 > ArrayLength(buff)) {
1589 goto print_nicks_buff;
1593 memcpy(buff+pos, ni->nick, herelen);
1594 pos += herelen; buff[pos++] = ' ';
1598 reply("NSMSG_HANDLEINFO_NICKS", buff);
1603 reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none);
1606 if (hi->masks->used) {
1607 for (i=0; i < hi->masks->used; i++) {
1608 herelen = strlen(hi->masks->list[i]);
1609 if (pos + herelen + 1 > ArrayLength(buff)) {
1611 goto print_mask_buff;
1613 memcpy(buff+pos, hi->masks->list[i], herelen);
1614 pos += herelen; buff[pos++] = ' ';
1615 if (i+1 == hi->masks->used) {
1618 reply("NSMSG_HANDLEINFO_MASKS", buff);
1623 reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none);
1627 struct userData *chan, *next;
1630 for (chan = hi->channels; chan; chan = next) {
1631 next = chan->u_next;
1632 name = chan->channel->channel->name;
1633 herelen = strlen(name);
1634 if (pos + herelen + 7 > ArrayLength(buff)) {
1636 goto print_chans_buff;
1638 if (IsUserSuspended(chan))
1640 pos += sprintf(buff+pos, "%d:%s ", chan->access, name);
1644 reply("NSMSG_HANDLEINFO_CHANNELS", buff);
1649 reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none);
1652 for (target = hi->users; target; target = next_un) {
1653 herelen = strlen(target->nick);
1654 if (pos + herelen + 1 > ArrayLength(buff)) {
1656 goto print_cnick_buff;
1658 next_un = target->next_authed;
1660 memcpy(buff+pos, target->nick, herelen);
1661 pos += herelen; buff[pos++] = ' ';
1665 reply("NSMSG_HANDLEINFO_CURRENT", buff);
1670 return 1 | ((hi != user->handle_info) ? CMD_LOG_STAFF : 0);
1673 static NICKSERV_FUNC(cmd_userinfo)
1675 struct userNode *target;
1677 NICKSERV_MIN_PARMS(2);
1678 if (!(target = GetUserH(argv[1]))) {
1679 reply("MSG_NICK_UNKNOWN", argv[1]);
1682 if (target->handle_info)
1683 reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle);
1685 reply("NSMSG_USERINFO_NOT_AUTHED", target->nick);
1689 static NICKSERV_FUNC(cmd_nickinfo)
1691 struct nick_info *ni;
1693 NICKSERV_MIN_PARMS(2);
1694 if (!(ni = get_nick_info(argv[1]))) {
1695 reply("MSG_NICK_UNKNOWN", argv[1]);
1698 reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle);
1702 static NICKSERV_FUNC(cmd_notes)
1704 struct handle_info *hi;
1705 struct handle_note *prev, *note;
1708 NICKSERV_MIN_PARMS(2);
1709 if (!(hi = get_victim_oper(user, argv[1])))
1712 WALK_NOTES(hi, prev, note) {
1713 char set_time[INTERVALLEN];
1714 intervalString(set_time, now - note->set, user->handle_info);
1715 if (note->expires) {
1716 char exp_time[INTERVALLEN];
1717 intervalString(exp_time, note->expires - now, user->handle_info);
1718 reply("NSMSG_NOTE_EXPIRES", note->id, set_time, note->setter, exp_time, note->note);
1720 reply("NSMSG_NOTE", note->id, set_time, note->setter, note->note);
1724 reply("NSMSG_NOTE_COUNT", hits, argv[1]);
1728 static NICKSERV_FUNC(cmd_rename_handle)
1730 struct handle_info *hi;
1731 char msgbuf[MAXLEN], *old_handle;
1734 NICKSERV_MIN_PARMS(3);
1735 if (!(hi = get_victim_oper(user, argv[1])))
1737 if (!is_valid_handle(argv[2])) {
1738 reply("NSMSG_FAIL_RENAME", argv[1], argv[2]);
1741 if (get_handle_info(argv[2])) {
1742 reply("NSMSG_HANDLE_EXISTS", argv[2]);
1745 if (hi->fakehost && hi->fakehost[0] == '.' &&
1746 (strlen(argv[2]) + strlen(hi->fakehost+1) +
1747 strlen(titlehost_suffix) + 2) > HOSTLEN) {
1748 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED_RENAME");
1752 dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1);
1753 hi->handle = strdup(argv[2]);
1754 dict_insert(nickserv_handle_dict, hi->handle, hi);
1755 for (nn=0; nn<rf_list_used; nn++)
1756 rf_list[nn](hi, old_handle);
1757 snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle);
1758 reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle);
1759 global_message(MESSAGE_RECIPIENT_STAFF, msgbuf);
1761 apply_fakehost(hi, NULL);
1765 static failpw_func_t *failpw_func_list;
1766 static unsigned int failpw_func_size = 0, failpw_func_used = 0;
1769 reg_failpw_func(failpw_func_t func)
1771 if (failpw_func_used == failpw_func_size) {
1772 if (failpw_func_size) {
1773 failpw_func_size <<= 1;
1774 failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t));
1776 failpw_func_size = 8;
1777 failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t));
1780 failpw_func_list[failpw_func_used++] = func;
1783 static struct authlogEntry *authlog_add(struct handle_info *hi, struct userNode *user, const char *mask) {
1784 if(!hi || (!user && !mask)) return NULL;
1786 mask = generate_hostmask(user, GENMASK_USENICK|GENMASK_STRICT_IDENT|GENMASK_NO_HIDING|GENMASK_STRICT_HOST);
1787 struct authlogEntry *authlog, *next, *prev = NULL;
1788 authlog = malloc(sizeof(*authlog));
1789 authlog->login_time = now;
1790 authlog->logout_time = 0;
1791 authlog->hostmask = mask;
1792 authlog->quit_reason = NULL;
1793 authlog->user = user;
1794 authlog->next = hi->authlog;
1795 hi->authlog = authlog;
1797 for(authlog = hi->authlog; authlog; authlog = next) {
1799 next = authlog->next;
1800 if(i > nickserv_conf.max_authlog_len) {
1801 struct pendingLOCUser *pending, *prev_pending = NULL;
1802 for(pending = pendingLOCUsers; pending; pending = pending->next) {
1803 if(pending->authlog == authlog) {
1805 prev_pending->next = pending->next;
1807 pendingLOCUsers = pending->next;
1811 prev_pending = pending;
1813 free((char *) authlog->hostmask);
1814 if(authlog->quit_reason)
1815 free((char *) authlog->quit_reason);
1817 prev->next = authlog->next;
1819 hi->authlog = authlog->next;
1827 static NICKSERV_FUNC(cmd_auth)
1829 int pw_arg, used, maxlogins;
1830 struct handle_info *hi;
1832 struct userNode *other;
1834 if (user->handle_info) {
1835 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
1838 if (IsStamped(user)) {
1839 /* Unauthenticated users might still have been stamped
1840 previously and could therefore have a hidden host;
1841 do not allow them to authenticate. */
1842 reply("NSMSG_STAMPED_AUTH");
1846 hi = dict_find(nickserv_handle_dict, argv[1], NULL);
1848 } else if (argc == 2) {
1849 if (nickserv_conf.disable_nicks) {
1850 if (!(hi = get_handle_info(user->nick))) {
1851 reply("NSMSG_HANDLE_NOT_FOUND");
1855 /* try to look up their handle from their nick */
1856 struct nick_info *ni;
1857 ni = get_nick_info(user->nick);
1859 reply("NSMSG_NICK_NOT_REGISTERED", user->nick);
1866 reply("MSG_MISSING_PARAMS", argv[0]);
1867 svccmd_send_help(user, nickserv, cmd);
1871 reply("NSMSG_HANDLE_NOT_FOUND");
1874 /* Responses from here on look up the language used by the handle they asked about. */
1875 passwd = argv[pw_arg];
1876 if (!valid_user_for(user, hi)) {
1877 if (hi->email_addr && nickserv_conf.email_enabled)
1878 send_message_type(4, user, cmd->parent->bot,
1879 handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"),
1882 send_message_type(4, user, cmd->parent->bot,
1883 handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"),
1885 argv[pw_arg] = "BADMASK";
1888 if (!checkpass(passwd, hi->passwd)) {
1890 send_message_type(4, user, cmd->parent->bot,
1891 handle_find_message(hi, "NSMSG_PASSWORD_INVALID"));
1892 argv[pw_arg] = "BADPASS";
1893 for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi);
1894 if (nickserv_conf.autogag_enabled) {
1895 if (!user->auth_policer.params) {
1896 user->auth_policer.last_req = now;
1897 user->auth_policer.params = nickserv_conf.auth_policer_params;
1899 if (!policer_conforms(&user->auth_policer, now, 1.0)) {
1901 hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING);
1902 log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask);
1903 gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration);
1905 argv[pw_arg] = "GAGGED";
1910 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
1911 send_message_type(4, user, cmd->parent->bot,
1912 handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED"));
1913 argv[pw_arg] = "SUSPENDED";
1916 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
1917 for (used = 0, other = hi->users; other; other = other->next_authed) {
1918 if (++used >= maxlogins) {
1919 send_message_type(4, user, cmd->parent->bot,
1920 handle_find_message(hi, "NSMSG_MAX_LOGINS"),
1922 argv[pw_arg] = "MAXLOGINS";
1926 if (HANDLE_FLAGGED(hi, AUTOHIDE)) {
1927 //ok we have a fakehost set... but we need to set mode +x
1928 irc_svsmode(nickserv,user,"+x");
1931 set_user_handle_info(user, hi, 1);
1932 if (nickserv_conf.email_required && !hi->email_addr)
1933 reply("NSMSG_PLEASE_SET_EMAIL");
1934 if (!is_secure_password(hi->handle, passwd, NULL))
1935 reply("NSMSG_WEAK_PASSWORD");
1936 if (hi->passwd[0] != '$')
1937 cryptpass(passwd, hi->passwd);
1938 if (!hi->masks->used) {
1940 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1941 if (irc_in_addr_is_valid(user->ip) && irc_pton(&ip, NULL, user->hostname))
1942 string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
1944 authlog_add(hi, user, NULL);
1945 argv[pw_arg] = "****";
1946 reply("NSMSG_AUTH_SUCCESS");
1950 struct handle_info *checklogin(const char *user, const char *pass, const char *numeric, const char *hostmask, const char *ipmask)
1952 struct handle_info *hi;
1953 unsigned int match = 0, ii = 0;
1954 hi = dict_find(nickserv_handle_dict, user, NULL);
1957 /* If no hostmasks on the account, allow it. */
1958 if (hi->masks->used) {
1959 /* If any hostmask matches, allow it. */
1960 for (ii=0; ii<hi->masks->used; ii++)
1961 if (match_ircglob(hostmask, hi->masks->list[ii]) || match_ircglob(ipmask, hi->masks->list[ii])) {
1968 if(!checkpass(pass, hi->passwd))
1970 if (HANDLE_FLAGGED(hi, SUSPENDED))
1972 char *ptr = malloc(strlen(hostmask)+1);
1973 strcpy(ptr, hostmask);
1974 struct authlogEntry *authlog = authlog_add(hi, NULL, ptr);
1975 struct pendingLOCUser *pending;
1976 if(authlog && (pending = malloc(sizeof(*pending)))) {
1977 pending->handle_info = hi;
1978 pending->time = now;
1979 pending->authlog = authlog;
1980 pending->next = pendingLOCUsers;
1981 pendingLOCUsers = pending;
1986 char *getfakehost(const char *user)
1988 struct handle_info *hi;
1989 hi = dict_find(nickserv_handle_dict, user, NULL);
1992 return generate_fakehost(hi);
1995 static allowauth_func_t *allowauth_func_list;
1996 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
1999 reg_allowauth_func(allowauth_func_t func)
2001 if (allowauth_func_used == allowauth_func_size) {
2002 if (allowauth_func_size) {
2003 allowauth_func_size <<= 1;
2004 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
2006 allowauth_func_size = 8;
2007 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
2010 allowauth_func_list[allowauth_func_used++] = func;
2013 static MODCMD_FUNC(cmd_authlog)
2015 struct handle_info *hi = user->handle_info;
2016 struct helpfile_table tbl;
2017 struct authlogEntry *authlog;
2020 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
2027 tbl.flags = TABLE_NO_FREE;
2028 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
2029 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
2030 tbl.contents[0][0] = "Hostmask";
2031 tbl.contents[0][1] = "Login";
2032 tbl.contents[0][2] = "Logout";
2033 tbl.contents[0][3] = "Quit Reason";
2036 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
2038 free(tbl.contents[0]);
2044 char intervalBuf[INTERVALLEN];
2046 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
2047 tbl.contents[++i] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
2048 tbl.contents[i][0] = authlog->hostmask;
2049 str = intervalString(intervalBuf, now - authlog->login_time, hi);
2050 ptr = malloc(strlen(str)+1);
2052 tbl.contents[i][1] = ptr;
2053 if(authlog->logout_time)
2054 str = intervalString(intervalBuf, now - authlog->logout_time, hi);
2055 else if(!authlog->user)
2058 sprintf(intervalBuf, "Never (%s)", authlog->user->nick);
2061 ptr = malloc(strlen(str)+1);
2063 tbl.contents[i][2] = ptr;
2064 tbl.contents[i][3] = (authlog->quit_reason ? authlog->quit_reason : "-");
2067 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
2068 for(i = 1; i < tbl.length; ++i)
2070 free((char *) tbl.contents[i][1]);
2071 free((char *) tbl.contents[i][2]);
2072 free(tbl.contents[i]);
2074 free(tbl.contents[0]);
2080 static NICKSERV_FUNC(cmd_allowauth)
2082 struct userNode *target;
2083 struct handle_info *hi;
2086 NICKSERV_MIN_PARMS(2);
2087 if (!(target = GetUserH(argv[1]))) {
2088 reply("MSG_NICK_UNKNOWN", argv[1]);
2091 if (target->handle_info) {
2092 reply("NSMSG_USER_PREV_AUTH", target->nick);
2095 if (IsStamped(target)) {
2096 /* Unauthenticated users might still have been stamped
2097 previously and could therefore have a hidden host;
2098 do not allow them to authenticate to an account. */
2099 reply("NSMSG_USER_PREV_STAMP", target->nick);
2104 else if (!(hi = get_handle_info(argv[2]))) {
2105 reply("MSG_HANDLE_UNKNOWN", argv[2]);
2109 if (hi->opserv_level > user->handle_info->opserv_level) {
2110 reply("MSG_USER_OUTRANKED", hi->handle);
2113 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
2114 || (hi->opserv_level > 0))
2115 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
2116 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
2119 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
2120 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
2121 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
2122 if (nickserv_conf.email_enabled)
2123 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
2125 if (dict_remove(nickserv_allow_auth_dict, target->nick))
2126 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
2128 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
2130 for (n=0; n<allowauth_func_used; n++)
2131 allowauth_func_list[n](user, target, hi);
2135 static NICKSERV_FUNC(cmd_authcookie)
2137 struct handle_info *hi;
2139 NICKSERV_MIN_PARMS(2);
2140 if (user->handle_info) {
2141 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2144 if (IsStamped(user)) {
2145 /* Unauthenticated users might still have been stamped
2146 previously and could therefore have a hidden host;
2147 do not allow them to authenticate to an account. */
2148 reply("NSMSG_STAMPED_AUTHCOOKIE");
2151 if (!(hi = get_handle_info(argv[1]))) {
2152 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2155 if (!hi->email_addr) {
2156 reply("MSG_SET_EMAIL_ADDR");
2159 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
2163 static NICKSERV_FUNC(cmd_delcookie)
2165 struct handle_info *hi;
2167 hi = user->handle_info;
2169 reply("NSMSG_NO_COOKIE");
2172 switch (hi->cookie->type) {
2175 reply("NSMSG_MUST_TIME_OUT");
2178 nickserv_eat_cookie(hi->cookie);
2179 reply("NSMSG_ATE_COOKIE");
2185 static NICKSERV_FUNC(cmd_odelcookie)
2187 struct handle_info *hi;
2189 NICKSERV_MIN_PARMS(2);
2191 if (!(hi = get_victim_oper(user, argv[1])))
2195 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
2199 nickserv_eat_cookie(hi->cookie);
2200 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
2205 static NICKSERV_FUNC(cmd_resetpass)
2207 struct handle_info *hi;
2208 char crypted[MD5_CRYPT_LENGTH];
2210 NICKSERV_MIN_PARMS(3);
2211 if (user->handle_info) {
2212 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2215 if (IsStamped(user)) {
2216 /* Unauthenticated users might still have been stamped
2217 previously and could therefore have a hidden host;
2218 do not allow them to activate an account. */
2219 reply("NSMSG_STAMPED_RESETPASS");
2222 if (!(hi = get_handle_info(argv[1]))) {
2223 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2226 if (!hi->email_addr) {
2227 reply("MSG_SET_EMAIL_ADDR");
2230 cryptpass(argv[2], crypted);
2232 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
2236 static NICKSERV_FUNC(cmd_cookie)
2238 struct handle_info *hi;
2241 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
2244 NICKSERV_MIN_PARMS(3);
2245 if (!(hi = get_handle_info(argv[1]))) {
2246 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2252 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2253 reply("NSMSG_HANDLE_SUSPENDED");
2258 reply("NSMSG_NO_COOKIE");
2262 /* Check validity of operation before comparing cookie to
2263 * prohibit guessing by authed users. */
2264 if (user->handle_info
2265 && (hi->cookie->type != EMAIL_CHANGE)
2266 && (hi->cookie->type != PASSWORD_CHANGE)) {
2267 reply("NSMSG_CANNOT_COOKIE");
2271 if (strcmp(cookie, hi->cookie->cookie)) {
2272 reply("NSMSG_BAD_COOKIE");
2276 switch (hi->cookie->type) {
2278 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2279 set_user_handle_info(user, hi, 1);
2280 reply("NSMSG_HANDLE_ACTIVATED");
2282 case PASSWORD_CHANGE:
2283 set_user_handle_info(user, hi, 1);
2284 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2285 reply("NSMSG_PASSWORD_CHANGED");
2288 nickserv_set_email_addr(hi, hi->cookie->data);
2289 reply("NSMSG_EMAIL_CHANGED");
2292 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2293 set_user_handle_info(user, hi, 1);
2294 nickserv_addmask(user, hi, mask);
2295 reply("NSMSG_AUTH_SUCCESS");
2300 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2301 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2305 nickserv_eat_cookie(hi->cookie);
2310 static NICKSERV_FUNC(cmd_oregnick) {
2312 struct handle_info *target;
2313 struct nick_info *ni;
2315 NICKSERV_MIN_PARMS(3);
2316 if (!(target = modcmd_get_handle_info(user, argv[1])))
2319 if (!is_registerable_nick(nick)) {
2320 reply("NSMSG_BAD_NICK", nick);
2323 ni = dict_find(nickserv_nick_dict, nick, NULL);
2325 reply("NSMSG_NICK_EXISTS", nick);
2328 register_nick(nick, target);
2329 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2333 static NICKSERV_FUNC(cmd_regnick) {
2335 struct nick_info *ni;
2337 if (!is_registerable_nick(user->nick)) {
2338 reply("NSMSG_BAD_NICK", user->nick);
2341 /* count their nicks, see if it's too many */
2342 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2343 if (n >= nickserv_conf.nicks_per_handle) {
2344 reply("NSMSG_TOO_MANY_NICKS");
2347 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2349 reply("NSMSG_NICK_EXISTS", user->nick);
2352 register_nick(user->nick, user->handle_info);
2353 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2357 static NICKSERV_FUNC(cmd_pass)
2359 struct handle_info *hi;
2360 const char *old_pass, *new_pass;
2362 NICKSERV_MIN_PARMS(3);
2363 hi = user->handle_info;
2367 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2368 if (!checkpass(old_pass, hi->passwd)) {
2369 argv[1] = "BADPASS";
2370 reply("NSMSG_PASSWORD_INVALID");
2373 cryptpass(new_pass, hi->passwd);
2375 reply("NSMSG_PASS_SUCCESS");
2380 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2383 char *new_mask = canonicalize_hostmask(strdup(mask));
2384 for (i=0; i<hi->masks->used; i++) {
2385 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2386 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2391 string_list_append(hi->masks, new_mask);
2392 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2396 static NICKSERV_FUNC(cmd_addmask)
2399 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2400 int res = nickserv_addmask(user, user->handle_info, mask);
2404 if (!is_gline(argv[1])) {
2405 reply("NSMSG_MASK_INVALID", argv[1]);
2408 return nickserv_addmask(user, user->handle_info, argv[1]);
2412 static NICKSERV_FUNC(cmd_oaddmask)
2414 struct handle_info *hi;
2416 NICKSERV_MIN_PARMS(3);
2417 if (!(hi = get_victim_oper(user, argv[1])))
2419 return nickserv_addmask(user, hi, argv[2]);
2423 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2426 for (i=0; i<hi->masks->used; i++) {
2427 if (!strcmp(del_mask, hi->masks->list[i])) {
2428 char *old_mask = hi->masks->list[i];
2429 if (hi->masks->used == 1 && !force) {
2430 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2433 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2434 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2439 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2443 static NICKSERV_FUNC(cmd_delmask)
2445 NICKSERV_MIN_PARMS(2);
2446 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2449 static NICKSERV_FUNC(cmd_odelmask)
2451 struct handle_info *hi;
2452 NICKSERV_MIN_PARMS(3);
2453 if (!(hi = get_victim_oper(user, argv[1])))
2455 return nickserv_delmask(user, hi, argv[2], 1);
2459 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2460 unsigned int nn, add = 1, pos;
2461 unsigned long added, removed, flag;
2463 for (added=removed=nn=0; str[nn]; nn++) {
2465 case '+': add = 1; break;
2466 case '-': add = 0; break;
2468 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2469 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2472 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2473 /* cheesy avoidance of looking up the flag name.. */
2474 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2477 flag = 1 << (pos - 1);
2479 added |= flag, removed &= ~flag;
2481 removed |= flag, added &= ~flag;
2486 *premoved = removed;
2491 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2493 unsigned long before, after, added, removed;
2494 struct userNode *uNode;
2496 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2497 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2499 hi->flags = (hi->flags | added) & ~removed;
2500 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2502 /* Strip helping flag if they're only a support helper and not
2503 * currently in #support. */
2504 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2505 struct channelList *schannels;
2507 schannels = chanserv_support_channels();
2508 for (ii = 0; ii < schannels->used; ++ii)
2509 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2511 if (ii == schannels->used)
2512 HANDLE_CLEAR_FLAG(hi, HELPING);
2515 if (after && !before) {
2516 /* Add user to current helper list. */
2517 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2518 userList_append(&curr_helpers, uNode);
2519 } else if (!after && before) {
2520 /* Remove user from current helper list. */
2521 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2522 userList_remove(&curr_helpers, uNode);
2529 set_list(struct userNode *user, struct handle_info *hi, int override)
2533 char *set_display[] = {
2534 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2535 "EMAIL", "MAXLOGINS", "LANGUAGE", "DEVNULL"
2538 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2540 /* Do this so options are presented in a consistent order. */
2541 for (i = 0; i < ArrayLength(set_display); ++i)
2542 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2543 opt(user, hi, override, 0, NULL);
2546 static NICKSERV_FUNC(cmd_set)
2548 struct handle_info *hi;
2551 hi = user->handle_info;
2553 set_list(user, hi, 0);
2556 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2557 reply("NSMSG_INVALID_OPTION", argv[1]);
2560 return opt(user, hi, 0, argc-1, argv+1);
2563 static NICKSERV_FUNC(cmd_oset)
2565 struct handle_info *hi;
2566 struct svccmd *subcmd;
2568 char cmdname[MAXLEN];
2570 NICKSERV_MIN_PARMS(2);
2572 if (!(hi = get_victim_oper(user, argv[1])))
2576 set_list(user, hi, 0);
2580 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2581 reply("NSMSG_INVALID_OPTION", argv[2]);
2585 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2586 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2587 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2590 return opt(user, hi, 1, argc-2, argv+2);
2593 static OPTION_FUNC(opt_info)
2597 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2599 hi->infoline = NULL;
2601 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2605 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2606 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2610 static OPTION_FUNC(opt_devnull)
2612 const char *devnull;
2616 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2619 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2623 devnull = unsplit_string(argv+1, argc-1, NULL);
2624 if(devnull_check(devnull) == 1) {
2627 hi->devnull = strdup(devnull);
2632 devnull = hi->devnull ? hi->devnull : user_find_message(user, "MSG_NONE");
2633 send_message(user, nickserv, "NSMSG_SET_DEVNULL", devnull);
2637 void nickserv_devnull_delete(char *name) {
2639 struct handle_info *hi;
2641 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2643 if (hi->devnull && !irccasecmp(name, hi->devnull)) {
2650 void nickserv_devnull_rename(char *oldname, char *newname) {
2652 struct handle_info *hi;
2654 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2656 if (hi->devnull && !irccasecmp(oldname, hi->devnull)) {
2657 hi->devnull = strdup(newname);
2662 static OPTION_FUNC(opt_website)
2664 const char *website;
2667 if (!HANDLE_FLAGGED(user->handle_info, BOT)) {
2668 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2671 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2675 website = unsplit_string(argv+1, argc-1, NULL);
2676 hi->website = strdup(website);
2679 if (HANDLE_FLAGGED(user->handle_info, BOT)) {
2680 website = hi->website ? hi->website : user_find_message(user, "MSG_NONE");
2681 send_message(user, nickserv, "NSMSG_SET_WEBSITE", website);
2686 static OPTION_FUNC(opt_width)
2689 hi->screen_width = strtoul(argv[1], NULL, 0);
2691 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2692 hi->screen_width = MIN_LINE_SIZE;
2693 else if (hi->screen_width > MAX_LINE_SIZE)
2694 hi->screen_width = MAX_LINE_SIZE;
2696 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2700 static OPTION_FUNC(opt_tablewidth)
2703 hi->table_width = strtoul(argv[1], NULL, 0);
2705 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2706 hi->table_width = MIN_LINE_SIZE;
2707 else if (hi->screen_width > MAX_LINE_SIZE)
2708 hi->table_width = MAX_LINE_SIZE;
2710 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2714 static OPTION_FUNC(opt_color)
2717 if (enabled_string(argv[1]))
2718 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2719 else if (disabled_string(argv[1]))
2720 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2722 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2727 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2731 static OPTION_FUNC(opt_privmsg)
2734 if (enabled_string(argv[1]))
2735 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2736 else if (disabled_string(argv[1]))
2737 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2739 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2744 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2748 static OPTION_FUNC(opt_autohide)
2751 if (enabled_string(argv[1]))
2752 HANDLE_SET_FLAG(hi, AUTOHIDE);
2753 else if (disabled_string(argv[1]))
2754 HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
2756 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2761 send_message(user, nickserv, "NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
2765 static OPTION_FUNC(opt_style)
2770 if (!irccasecmp(argv[1], "Zoot"))
2771 hi->userlist_style = HI_STYLE_ZOOT;
2772 else if (!irccasecmp(argv[1], "def"))
2773 hi->userlist_style = HI_STYLE_DEF;
2776 switch (hi->userlist_style) {
2785 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2789 static OPTION_FUNC(opt_password)
2792 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2797 cryptpass(argv[1], hi->passwd);
2799 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2803 static OPTION_FUNC(opt_flags)
2806 unsigned int ii, flen;
2809 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2814 nickserv_apply_flags(user, hi, argv[1]);
2816 for (ii = flen = 0; handle_flags[ii]; ii++)
2817 if (hi->flags & (1 << ii))
2818 flags[flen++] = handle_flags[ii];
2821 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2823 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2827 static OPTION_FUNC(opt_email)
2831 if (!is_valid_email_addr(argv[1])) {
2832 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2835 if ((str = mail_prohibited_address(argv[1]))) {
2836 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2839 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2840 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2842 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2844 nickserv_set_email_addr(hi, argv[1]);
2846 nickserv_eat_cookie(hi->cookie);
2847 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2850 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2854 static OPTION_FUNC(opt_maxlogins)
2856 unsigned char maxlogins;
2858 maxlogins = strtoul(argv[1], NULL, 0);
2859 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2860 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2863 hi->maxlogins = maxlogins;
2865 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2866 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2870 static OPTION_FUNC(opt_language)
2872 struct language *lang;
2874 lang = language_find(argv[1]);
2875 if (irccasecmp(lang->name, argv[1]))
2876 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2877 hi->language = lang;
2879 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2883 static OPTION_FUNC(opt_karma)
2886 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2891 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2892 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2893 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2894 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2896 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2900 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2905 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2906 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2908 if ((user->handle_info->opserv_level < target->opserv_level)
2909 || ((user->handle_info->opserv_level == target->opserv_level)
2910 && (user->handle_info->opserv_level < 1000))) {
2911 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2914 if ((user->handle_info->opserv_level < new_level)
2915 || ((user->handle_info->opserv_level == new_level)
2916 && (user->handle_info->opserv_level < 1000))) {
2917 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2920 if (user->handle_info == target) {
2921 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2924 if (target->opserv_level == new_level)
2926 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2927 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2928 target->opserv_level = new_level;
2932 static OPTION_FUNC(opt_level)
2937 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2941 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2942 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2946 static OPTION_FUNC(opt_epithet)
2949 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2953 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2954 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2957 if ((epithet[0] == '*') && !epithet[1])
2960 hi->epithet = strdup(epithet);
2964 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2966 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2970 static OPTION_FUNC(opt_title)
2975 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2979 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2981 if (strchr(title, '.')) {
2982 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2985 if ((strlen(user->handle_info->handle) + strlen(title) +
2986 strlen(titlehost_suffix) + 2) > HOSTLEN) {
2987 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2992 if (!strcmp(title, "*")) {
2993 hi->fakehost = NULL;
2995 hi->fakehost = malloc(strlen(title)+2);
2996 hi->fakehost[0] = '.';
2997 strcpy(hi->fakehost+1, title);
2999 apply_fakehost(hi, NULL);
3000 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
3001 title = hi->fakehost + 1;
3005 title = user_find_message(user, "MSG_NONE");
3006 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
3010 static OPTION_FUNC(opt_fakehost)
3012 char mask[USERLEN + HOSTLEN + 2];
3016 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3020 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
3021 if(strlen(argv[1]) >= sizeof(mask)) {
3022 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
3026 safestrncpy(mask, argv[1], sizeof(mask));
3028 if ((host = strrchr(mask, '@')) && host != mask) {
3029 /* If ident@host was used and the user doesn't have access to set idents, do not change anything. */
3030 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3042 if (ident && strlen(ident) > USERLEN) {
3043 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3047 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
3048 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
3052 if (host && host[0]) {
3054 if (!strcmp(host, "*"))
3055 hi->fakehost = NULL;
3057 hi->fakehost = strdup(host);
3058 host = hi->fakehost;
3061 host = generate_fakehost(hi);
3064 free(hi->fakeident);
3065 if (!strcmp(ident, "*"))
3066 hi->fakeident = NULL;
3068 hi->fakeident = strdup(ident);
3069 ident = hi->fakeident;
3072 ident = generate_fakeident(hi, NULL);
3074 apply_fakehost(hi, NULL);
3076 host = generate_fakehost(hi);
3077 ident = generate_fakeident(hi, NULL);
3080 host = (char *) user_find_message(user, "MSG_NONE");
3082 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
3084 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
3088 static OPTION_FUNC(opt_fakeident)
3093 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3097 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3099 if (strlen(ident) > USERLEN) {
3100 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3103 free(hi->fakeident);
3104 if (!strcmp(ident, "*"))
3105 hi->fakeident = NULL;
3107 hi->fakeident = strdup(ident);
3108 ident = hi->fakeident;
3109 apply_fakehost(hi, NULL);
3111 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
3113 ident = user_find_message(user, "MSG_NONE");
3114 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
3118 static NICKSERV_FUNC(cmd_reclaim)
3120 struct nick_info *ni;
3121 struct userNode *victim;
3123 NICKSERV_MIN_PARMS(2);
3124 ni = dict_find(nickserv_nick_dict, argv[1], 0);
3126 reply("NSMSG_UNKNOWN_NICK", argv[1]);
3129 if (ni->owner != user->handle_info) {
3130 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
3133 victim = GetUserH(ni->nick);
3135 reply("MSG_NICK_UNKNOWN", ni->nick);
3138 if (victim == user) {
3139 reply("NSMSG_NICK_USER_YOU");
3142 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
3143 switch (nickserv_conf.reclaim_action) {
3144 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
3145 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
3146 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
3147 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
3152 static NICKSERV_FUNC(cmd_unregnick)
3155 struct handle_info *hi;
3156 struct nick_info *ni;
3158 hi = user->handle_info;
3159 nick = (argc < 2) ? user->nick : (const char*)argv[1];
3160 ni = dict_find(nickserv_nick_dict, nick, NULL);
3162 reply("NSMSG_UNKNOWN_NICK", nick);
3165 if (hi != ni->owner) {
3166 reply("NSMSG_NOT_YOUR_NICK", nick);
3169 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3174 static NICKSERV_FUNC(cmd_ounregnick)
3176 struct nick_info *ni;
3178 NICKSERV_MIN_PARMS(2);
3179 if (!(ni = get_nick_info(argv[1]))) {
3180 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
3183 if (!oper_outranks(user, ni->owner))
3185 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3190 static NICKSERV_FUNC(cmd_unregister)
3192 struct handle_info *hi;
3195 NICKSERV_MIN_PARMS(2);
3196 hi = user->handle_info;
3199 if (checkpass(passwd, hi->passwd)) {
3200 nickserv_unregister_handle(hi, user);
3203 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3204 reply("NSMSG_PASSWORD_INVALID");
3209 static NICKSERV_FUNC(cmd_ounregister)
3211 struct handle_info *hi;
3212 char reason[MAXLEN];
3215 NICKSERV_MIN_PARMS(2);
3216 if (!(hi = get_victim_oper(user, argv[1])))
3219 if (HANDLE_FLAGGED(hi, NODELETE)) {
3220 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
3224 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
3226 ((hi->flags & nickserv_conf.ounregister_flags)
3228 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
3229 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
3233 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
3234 global_message(MESSAGE_RECIPIENT_STAFF, reason);
3235 nickserv_unregister_handle(hi, user);
3239 static NICKSERV_FUNC(cmd_status)
3241 if (nickserv_conf.disable_nicks) {
3242 reply("NSMSG_GLOBAL_STATS_NONICK",
3243 dict_size(nickserv_handle_dict));
3245 if (user->handle_info) {
3247 struct nick_info *ni;
3248 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
3249 reply("NSMSG_HANDLE_STATS", cnt);
3251 reply("NSMSG_HANDLE_NONE");
3253 reply("NSMSG_GLOBAL_STATS",
3254 dict_size(nickserv_handle_dict),
3255 dict_size(nickserv_nick_dict));
3260 static NICKSERV_FUNC(cmd_ghost)
3262 struct userNode *target;
3263 char reason[MAXLEN];
3265 NICKSERV_MIN_PARMS(2);
3266 if (!(target = GetUserH(argv[1]))) {
3267 reply("MSG_NICK_UNKNOWN", argv[1]);
3270 if (target == user) {
3271 reply("NSMSG_CANNOT_GHOST_SELF");
3274 if (!target->handle_info || (target->handle_info != user->handle_info)) {
3275 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
3278 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
3279 DelUser(target, nickserv, 1, reason);
3280 reply("NSMSG_GHOST_KILLED", argv[1]);
3284 static NICKSERV_FUNC(cmd_vacation)
3286 HANDLE_SET_FLAG(user->handle_info, FROZEN);
3287 reply("NSMSG_ON_VACATION");
3291 static NICKSERV_FUNC(cmd_addnote)
3293 struct handle_info *hi;
3294 unsigned long duration;
3297 struct handle_note *prev;
3298 struct handle_note *note;
3300 /* Parse parameters and figure out values for note's fields. */
3301 NICKSERV_MIN_PARMS(4);
3302 hi = get_victim_oper(user, argv[1]);
3305 if(!strcmp(argv[2], "0"))
3307 else if(!(duration = ParseInterval(argv[2])))
3309 reply("MSG_INVALID_DURATION", argv[2]);
3312 if (duration > 2*365*86400) {
3313 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
3316 unsplit_string(argv + 3, argc - 3, text);
3317 WALK_NOTES(hi, prev, note) {}
3318 id = prev ? (prev->id + 1) : 1;
3320 /* Create the new note structure. */
3321 note = calloc(1, sizeof(*note) + strlen(text));
3323 note->expires = duration ? (now + duration) : 0;
3326 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
3327 strcpy(note->note, text);
3332 reply("NSMSG_NOTE_ADDED", id, hi->handle);
3336 static NICKSERV_FUNC(cmd_delnote)
3338 struct handle_info *hi;
3339 struct handle_note *prev;
3340 struct handle_note *note;
3343 NICKSERV_MIN_PARMS(3);
3344 hi = get_victim_oper(user, argv[1]);
3347 id = strtoul(argv[2], NULL, 10);
3348 WALK_NOTES(hi, prev, note) {
3349 if (id == note->id) {
3351 prev->next = note->next;
3353 hi->notes = note->next;
3355 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3359 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3364 nickserv_saxdb_write(struct saxdb_context *ctx) {
3366 struct handle_info *hi;
3369 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3371 assert(hi->id != 0);
3372 saxdb_start_record(ctx, iter_key(it), 0);
3374 struct handle_cookie *cookie = hi->cookie;
3377 switch (cookie->type) {
3378 case ACTIVATION: type = KEY_ACTIVATION; break;
3379 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3380 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3381 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3382 default: type = NULL; break;
3385 saxdb_start_record(ctx, KEY_COOKIE, 0);
3386 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3387 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3389 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3390 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3391 saxdb_end_record(ctx);
3395 struct handle_note *prev, *note;
3396 saxdb_start_record(ctx, KEY_NOTES, 0);
3397 WALK_NOTES(hi, prev, note) {
3398 snprintf(flags, sizeof(flags), "%d", note->id);
3399 saxdb_start_record(ctx, flags, 0);
3401 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3402 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3403 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3404 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3405 saxdb_end_record(ctx);
3407 saxdb_end_record(ctx);
3410 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3412 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3414 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3416 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3420 for (ii=flen=0; handle_flags[ii]; ++ii)
3421 if (hi->flags & (1 << ii))
3422 flags[flen++] = handle_flags[ii];
3424 saxdb_write_string(ctx, KEY_FLAGS, flags);
3426 saxdb_write_int(ctx, KEY_ID, hi->id);
3428 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3430 saxdb_write_string(ctx, KEY_DEVNULL, hi->devnull);
3432 saxdb_write_string(ctx, KEY_WEBSITE, hi->website);
3433 if (hi->last_quit_host[0])
3434 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3435 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3437 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3438 if (hi->masks->used)
3439 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3441 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3443 struct string_list *slist;
3444 struct nick_info *ni;
3446 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3447 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3448 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3452 if (hi->opserv_level)
3453 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3454 if (hi->language != lang_C)
3455 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3456 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3457 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3458 if (hi->screen_width)
3459 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3460 if (hi->table_width)
3461 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3462 flags[0] = hi->userlist_style;
3464 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3466 saxdb_start_record(ctx, KEY_AUTHLOG, 0);
3467 struct authlogEntry *authlog;
3469 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
3470 saxdb_start_record(ctx, strtab(++i), 0);
3471 saxdb_write_int(ctx, KEY_AUTHLOG_LOGIN_TIME, authlog->login_time);
3472 saxdb_write_int(ctx, KEY_AUTHLOG_LOGOUT_TIME, authlog->logout_time);
3473 saxdb_write_string(ctx, KEY_AUTHLOG_HOSTMASK, authlog->hostmask);
3474 if(authlog->quit_reason)
3475 saxdb_write_string(ctx, KEY_AUTHLOG_QUIT_REASON, authlog->quit_reason);
3476 saxdb_end_record(ctx);
3478 saxdb_end_record(ctx); //END KEY_AUTHLOG
3480 saxdb_end_record(ctx);
3485 static handle_merge_func_t *handle_merge_func_list;
3486 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3489 reg_handle_merge_func(handle_merge_func_t func)
3491 if (handle_merge_func_used == handle_merge_func_size) {
3492 if (handle_merge_func_size) {
3493 handle_merge_func_size <<= 1;
3494 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3496 handle_merge_func_size = 8;
3497 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3500 handle_merge_func_list[handle_merge_func_used++] = func;
3503 static NICKSERV_FUNC(cmd_merge)
3505 struct handle_info *hi_from, *hi_to;
3506 struct userNode *last_user;
3507 struct userData *cList, *cListNext;
3508 unsigned int ii, jj, n;
3509 char buffer[MAXLEN];
3511 NICKSERV_MIN_PARMS(3);
3513 if (!(hi_from = get_victim_oper(user, argv[1])))
3515 if (!(hi_to = get_victim_oper(user, argv[2])))
3517 if (hi_to == hi_from) {
3518 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3522 for (n=0; n<handle_merge_func_used; n++)
3523 handle_merge_func_list[n](user, hi_to, hi_from);
3525 /* Append "from" handle's nicks to "to" handle's nick list. */
3527 struct nick_info *last_ni;
3528 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3529 last_ni->next = hi_from->nicks;
3531 while (hi_from->nicks) {
3532 hi_from->nicks->owner = hi_to;
3533 hi_from->nicks = hi_from->nicks->next;
3536 /* Merge the hostmasks. */
3537 for (ii=0; ii<hi_from->masks->used; ii++) {
3538 char *mask = hi_from->masks->list[ii];
3539 for (jj=0; jj<hi_to->masks->used; jj++)
3540 if (match_ircglobs(hi_to->masks->list[jj], mask))
3542 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3543 string_list_append(hi_to->masks, strdup(mask));
3546 /* Merge the lists of authed users. */
3548 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3549 last_user->next_authed = hi_from->users;
3551 hi_to->users = hi_from->users;
3553 /* Repoint the old "from" handle's users. */
3554 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3555 last_user->handle_info = hi_to;
3557 hi_from->users = NULL;
3559 /* Merge channel userlists. */
3560 for (cList=hi_from->channels; cList; cList=cListNext) {
3561 struct userData *cList2;
3562 cListNext = cList->u_next;
3563 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3564 if (cList->channel == cList2->channel)
3566 if (cList2 && (cList2->access >= cList->access)) {
3567 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);
3568 /* keep cList2 in hi_to; remove cList from hi_from */
3569 del_channel_user(cList, 1);
3572 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);
3573 /* remove the lower-ranking cList2 from hi_to */
3574 del_channel_user(cList2, 1);
3576 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3578 /* cList needs to be moved from hi_from to hi_to */
3579 cList->handle = hi_to;
3580 /* Remove from linked list for hi_from */
3581 assert(!cList->u_prev);
3582 hi_from->channels = cList->u_next;
3584 cList->u_next->u_prev = cList->u_prev;
3585 /* Add to linked list for hi_to */
3586 cList->u_prev = NULL;
3587 cList->u_next = hi_to->channels;
3588 if (hi_to->channels)
3589 hi_to->channels->u_prev = cList;
3590 hi_to->channels = cList;
3594 /* Do they get an OpServ level promotion? */
3595 if (hi_from->opserv_level > hi_to->opserv_level)
3596 hi_to->opserv_level = hi_from->opserv_level;
3598 /* What about last seen time? */
3599 if (hi_from->lastseen > hi_to->lastseen)
3600 hi_to->lastseen = hi_from->lastseen;
3602 /* New karma is the sum of the two original karmas. */
3603 hi_to->karma += hi_from->karma;
3605 /* Does a fakehost carry over? (This intentionally doesn't set it
3606 * for users previously attached to hi_to. They'll just have to
3609 if (hi_from->fakehost && !hi_to->fakehost)
3610 hi_to->fakehost = strdup(hi_from->fakehost);
3611 if (hi_from->fakeident && !hi_to->fakeident)
3612 hi_to->fakeident = strdup(hi_from->fakeident);
3614 /* Notify of success. */
3615 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3616 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3617 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3619 /* Unregister the "from" handle. */
3620 nickserv_unregister_handle(hi_from, NULL);
3625 #define NICKSERV_DISCRIM_FIELDS_AUTH 0x01
3626 #define NICKSERV_DISCRIM_FIELDS_EMAIL 0x02
3627 #define NICKSERV_DISCRIM_FIELDS_SEEN 0x04
3628 #define NICKSERV_DISCRIM_FIELDS_ACCESS 0x08
3629 #define NICKSERV_DISCRIM_FIELDS_FAKEHOST 0x10
3630 #define NICKSERV_DISCRIM_FIELDS_WEBSITE 0x20
3631 #define NICKSERV_DISCRIM_FIELDS_DEVNULL 0x40
3633 #define NICKSERV_DISCRIM_FIELD_COUNT 7
3635 struct nickserv_discrim {
3636 unsigned int show_fields;
3637 struct helpfile_table *output_table;
3638 int output_table_pos;
3639 unsigned int output_table_free_fields;
3641 unsigned long flags_on, flags_off;
3642 unsigned long min_registered, max_registered;
3643 unsigned long lastseen;
3645 int min_level, max_level;
3646 int min_karma, max_karma;
3647 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3648 const char *nickmask;
3649 const char *hostmask;
3650 const char *fakehostmask;
3651 const char *fakeidentmask;
3652 const char *website;
3653 const char *devnullclass;
3654 const char *handlemask;
3655 const char *emailmask;
3658 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi, struct nickserv_discrim *discrim);
3660 struct discrim_apply_info {
3661 struct nickserv_discrim *discrim;
3662 discrim_search_func func;
3663 struct userNode *source;
3664 unsigned int matched;
3667 static struct nickserv_discrim *
3668 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3671 struct nickserv_discrim *discrim;
3673 discrim = malloc(sizeof(*discrim));
3674 memset(discrim, 0, sizeof(*discrim));
3675 discrim->min_level = 0;
3676 discrim->max_level = INT_MAX;
3677 discrim->limit = 50;
3678 discrim->min_registered = 0;
3679 discrim->max_registered = ULONG_MAX;
3680 discrim->lastseen = ULONG_MAX;
3681 discrim->min_karma = INT_MIN;
3682 discrim->max_karma = INT_MAX;
3684 for (i=0; i<argc; i++) {
3685 if (i == argc - 1) {
3686 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3689 if (!irccasecmp(argv[i], "limit")) {
3690 discrim->limit = strtoul(argv[++i], NULL, 0);
3691 } else if (!irccasecmp(argv[i], "flags")) {
3692 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3693 } else if (!irccasecmp(argv[i], "fields")) {
3694 char *fields = argv[++i];
3695 char *delimiter = strstr(fields, ",");
3699 if(!irccasecmp(fields, "auth"))
3700 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_AUTH;
3701 else if(!irccasecmp(fields, "email"))
3702 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_EMAIL;
3703 else if(!irccasecmp(fields, "seen"))
3704 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_SEEN;
3705 else if(!irccasecmp(fields, "access"))
3706 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_ACCESS;
3707 else if(!irccasecmp(fields, "fakehost"))
3708 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_FAKEHOST;
3709 else if(!irccasecmp(fields, "website") && IsBot(user))
3710 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_WEBSITE;
3711 else if(!irccasecmp(fields, "devnull"))
3712 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_DEVNULL;
3714 send_message(user, nickserv, "MSG_INVALID_FIELD", fields);
3719 fields = delimiter+1;
3721 delimiter = strstr(fields, ",");
3727 } else if (!irccasecmp(argv[i], "registered")) {
3728 const char *cmp = argv[++i];
3729 if (cmp[0] == '<') {
3730 if (cmp[1] == '=') {
3731 discrim->min_registered = now - ParseInterval(cmp+2);
3733 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3735 } else if (cmp[0] == '=') {
3736 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3737 } else if (cmp[0] == '>') {
3738 if (cmp[1] == '=') {
3739 discrim->max_registered = now - ParseInterval(cmp+2);
3741 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3744 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3746 } else if (!irccasecmp(argv[i], "seen")) {
3747 discrim->lastseen = now - ParseInterval(argv[++i]);
3748 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3749 discrim->nickmask = argv[++i];
3750 } else if (!irccasecmp(argv[i], "hostmask")) {
3752 if (!irccasecmp(argv[i], "exact")) {
3753 if (i == argc - 1) {
3754 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3757 discrim->hostmask_type = EXACT;
3758 } else if (!irccasecmp(argv[i], "subset")) {
3759 if (i == argc - 1) {
3760 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3763 discrim->hostmask_type = SUBSET;
3764 } else if (!irccasecmp(argv[i], "superset")) {
3765 if (i == argc - 1) {
3766 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3769 discrim->hostmask_type = SUPERSET;
3770 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3771 if (i == argc - 1) {
3772 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3775 discrim->hostmask_type = LASTQUIT;
3778 discrim->hostmask_type = SUPERSET;
3780 discrim->hostmask = argv[++i];
3781 } else if (!irccasecmp(argv[i], "fakehost")) {
3782 if (!irccasecmp(argv[++i], "*")) {
3783 discrim->fakehostmask = 0;
3785 discrim->fakehostmask = argv[i];
3787 } else if (!irccasecmp(argv[i], "fakeident")) {
3788 if (!irccasecmp(argv[++i], "*")) {
3789 discrim->fakeidentmask = 0;
3791 discrim->fakeidentmask = argv[i];
3793 } else if (!irccasecmp(argv[i], "website")) {
3794 if (!irccasecmp(argv[++i], "*")) {
3795 discrim->website = 0;
3797 discrim->website = argv[i];
3799 } else if (!irccasecmp(argv[i], "devnull")) {
3800 if (!irccasecmp(argv[++i], "*")) {
3801 discrim->devnullclass = 0;
3803 discrim->devnullclass = argv[i];
3805 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3806 if (!irccasecmp(argv[++i], "*")) {
3807 discrim->handlemask = 0;
3809 discrim->handlemask = argv[i];
3811 } else if (!irccasecmp(argv[i], "email")) {
3812 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3813 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3815 } else if (!irccasecmp(argv[++i], "*")) {
3816 discrim->emailmask = 0;
3818 discrim->emailmask = argv[i];
3820 } else if (!irccasecmp(argv[i], "access")) {
3821 const char *cmp = argv[++i];
3822 if (cmp[0] == '<') {
3823 if (discrim->min_level == 0) discrim->min_level = 1;
3824 if (cmp[1] == '=') {
3825 discrim->max_level = strtoul(cmp+2, NULL, 0);
3827 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3829 } else if (cmp[0] == '=') {
3830 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3831 } else if (cmp[0] == '>') {
3832 if (cmp[1] == '=') {
3833 discrim->min_level = strtoul(cmp+2, NULL, 0);
3835 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3838 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3840 } else if (!irccasecmp(argv[i], "karma")) {
3841 const char *cmp = argv[++i];
3842 if (cmp[0] == '<') {
3843 if (cmp[1] == '=') {
3844 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3846 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3848 } else if (cmp[0] == '=') {
3849 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3850 } else if (cmp[0] == '>') {
3851 if (cmp[1] == '=') {
3852 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3854 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3857 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3860 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3871 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3873 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3874 || (discrim->flags_off & hi->flags)
3875 || (discrim->min_registered > hi->registered)
3876 || (discrim->max_registered < hi->registered)
3877 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3878 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3879 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3880 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3881 || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website)))
3882 || (discrim->devnullclass && (!hi->devnull || !match_ircglob(hi->devnull, discrim->devnullclass)))
3883 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3884 || (discrim->min_level > hi->opserv_level)
3885 || (discrim->max_level < hi->opserv_level)
3886 || (discrim->min_karma > hi->karma)
3887 || (discrim->max_karma < hi->karma)
3891 if (discrim->hostmask) {
3893 for (i=0; i<hi->masks->used; i++) {
3894 const char *mask = hi->masks->list[i];
3895 if ((discrim->hostmask_type == SUBSET)
3896 && (match_ircglobs(discrim->hostmask, mask))) break;
3897 else if ((discrim->hostmask_type == EXACT)
3898 && !irccasecmp(discrim->hostmask, mask)) break;
3899 else if ((discrim->hostmask_type == SUPERSET)
3900 && (match_ircglobs(mask, discrim->hostmask))) break;
3901 else if ((discrim->hostmask_type == LASTQUIT)
3902 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3904 if (i==hi->masks->used) return 0;
3906 if (discrim->nickmask) {
3907 struct nick_info *nick = hi->nicks;
3909 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3912 if (!nick) return 0;
3918 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3920 dict_iterator_t it, next;
3921 unsigned int matched;
3923 for (it = dict_first(nickserv_handle_dict), matched = 0;
3924 it && (matched < discrim->limit);
3926 next = iter_next(it);
3927 if (nickserv_discrim_match(discrim, iter_data(it))) {
3928 dsf(source, iter_data(it), discrim);
3936 search_print_func(struct userNode *source, struct handle_info *match, struct nickserv_discrim *discrim)
3938 if(discrim->show_fields) {
3940 if(discrim->output_table) {
3941 discrim->output_table->contents[++discrim->output_table_pos] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
3943 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
3944 discrim->output_table->contents[discrim->output_table_pos][i++] = match->handle;
3945 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
3946 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->email_addr ? match->email_addr : "*");
3947 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN) {
3949 char seenBuf[INTERVALLEN];
3952 } else if(match->lastseen == 0) {
3955 seen = intervalString(seenBuf, now - match->lastseen, source->handle_info);
3957 discrim->output_table_free_fields |= 1 << i;
3958 discrim->output_table->contents[discrim->output_table_pos][i++] = strdup(seen);
3960 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
3961 discrim->output_table->contents[discrim->output_table_pos][i++] = strtab(match->opserv_level);
3962 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
3963 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->fakehost ? match->fakehost : "*");
3964 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
3965 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->website ? match->website : "*");
3966 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
3967 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->devnull ? match->devnull : "*");
3971 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3975 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match), UNUSED_ARG(struct nickserv_discrim *discrim))
3980 search_unregister_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
3982 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3983 nickserv_unregister_handle(match, source);
3987 nickserv_sort_accounts_by_access(const void *a, const void *b)
3989 const struct handle_info *hi_a = *(const struct handle_info**)a;
3990 const struct handle_info *hi_b = *(const struct handle_info**)b;
3991 if (hi_a->opserv_level != hi_b->opserv_level)
3992 return hi_b->opserv_level - hi_a->opserv_level;
3993 return irccasecmp(hi_a->handle, hi_b->handle);
3997 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3999 struct handle_info_list hil;
4000 struct helpfile_table tbl;
4005 memset(&hil, 0, sizeof(hil));
4006 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
4007 struct handle_info *hi = iter_data(it);
4008 if (hi->opserv_level)
4009 handle_info_list_append(&hil, hi);
4011 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
4012 tbl.length = hil.used + 1;
4014 tbl.flags = TABLE_NO_FREE;
4015 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4016 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
4019 for (ii = 0; ii < hil.used; ) {
4020 ary = malloc(tbl.width * sizeof(ary[0]));
4021 ary[0] = hil.list[ii]->handle;
4022 ary[1] = strtab(hil.list[ii]->opserv_level);
4023 tbl.contents[++ii] = ary;
4025 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4026 reply("MSG_MATCH_COUNT", hil.used);
4027 for (ii = 0; ii < hil.used; ii++)
4028 free(tbl.contents[ii]);
4033 static NICKSERV_FUNC(cmd_search)
4035 struct nickserv_discrim *discrim;
4036 discrim_search_func action;
4037 struct svccmd *subcmd;
4038 unsigned int matches;
4041 NICKSERV_MIN_PARMS(3);
4042 sprintf(buf, "search %s", argv[1]);
4043 subcmd = dict_find(nickserv_service->commands, buf, NULL);
4044 if (!irccasecmp(argv[1], "print"))
4045 action = search_print_func;
4046 else if (!irccasecmp(argv[1], "count"))
4047 action = search_count_func;
4048 else if (!irccasecmp(argv[1], "unregister"))
4049 action = search_unregister_func;
4051 reply("NSMSG_INVALID_ACTION", argv[1]);
4055 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
4058 discrim = nickserv_discrim_create(user, argc-2, argv+2);
4062 if (action == search_print_func)
4063 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
4064 else if (action == search_count_func)
4065 discrim->limit = INT_MAX;
4067 matches = nickserv_discrim_search(discrim, action, user);
4069 if(discrim->show_fields) {
4072 for(ii = 0; ii < NICKSERV_DISCRIM_FIELD_COUNT; ii++) {
4073 if(discrim->show_fields & (1 << ii)) width++;
4075 discrim->output_table = malloc(sizeof(discrim->output_table[0]));
4076 discrim->output_table->length = matches+1;
4077 discrim->output_table->width = width;
4078 discrim->output_table->flags = TABLE_NO_FREE;
4079 discrim->output_table->contents = malloc(discrim->output_table->length * sizeof(discrim->output_table->contents[0]));
4080 discrim->output_table->contents[0] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
4083 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
4084 discrim->output_table->contents[0][ii++] = "Auth";
4085 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
4086 discrim->output_table->contents[0][ii++] = "EMail";
4087 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN)
4088 discrim->output_table->contents[0][ii++] = "Seen";
4089 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
4090 discrim->output_table->contents[0][ii++] = "Access";
4091 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
4092 discrim->output_table->contents[0][ii++] = "Fakehost";
4093 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
4094 discrim->output_table->contents[0][ii++] = "Website";
4095 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
4096 discrim->output_table->contents[0][ii++] = "DevNull";
4098 nickserv_discrim_search(discrim, action, user);
4100 table_send(nickserv, user->nick, 0, NULL, *discrim->output_table);
4102 for(ii = 1; ii < discrim->output_table->length; ++ii) {
4104 for(ij = 0; ij < NICKSERV_DISCRIM_FIELD_COUNT; ij++) {
4105 if(discrim->output_table_free_fields & (1 << ij))
4106 free((char*)discrim->output_table->contents[ii][ij]);
4108 free(discrim->output_table->contents[ii]);
4110 free(discrim->output_table->contents[0]);
4111 free(discrim->output_table->contents);
4112 free(discrim->output_table);
4115 reply("MSG_MATCH_COUNT", matches);
4117 reply("MSG_NO_MATCHES");
4124 static MODCMD_FUNC(cmd_checkpass)
4126 struct handle_info *hi;
4128 NICKSERV_MIN_PARMS(3);
4129 if (!(hi = get_handle_info(argv[1]))) {
4130 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4133 if (checkpass(argv[2], hi->passwd))
4134 reply("CHECKPASS_YES");
4136 reply("CHECKPASS_NO");
4141 static MODCMD_FUNC(cmd_checkemail)
4143 struct handle_info *hi;
4145 NICKSERV_MIN_PARMS(3);
4146 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
4149 if (!hi->email_addr)
4150 reply("CHECKEMAIL_NOT_SET");
4151 else if (!irccasecmp(argv[2], hi->email_addr))
4152 reply("CHECKEMAIL_YES");
4154 reply("CHECKEMAIL_NO");
4159 nickserv_db_read_authlog(UNUSED_ARG(const char *key), void *data, void *extra)
4161 struct record_data *rd = data;
4162 struct handle_info *hi = extra;
4164 struct authlogEntry *authlog;
4165 authlog = malloc(sizeof(*authlog));
4167 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGIN_TIME, RECDB_QSTRING);
4168 authlog->login_time = str ? strtoul(str, NULL, 0) : 0;
4170 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGOUT_TIME, RECDB_QSTRING);
4171 authlog->logout_time = str ? strtoul(str, NULL, 0) : 0;
4173 str = database_get_data(rd->d.object, KEY_AUTHLOG_HOSTMASK, RECDB_QSTRING);
4174 authlog->hostmask = str ? strdup(str) : NULL;
4176 str = database_get_data(rd->d.object, KEY_AUTHLOG_QUIT_REASON, RECDB_QSTRING);
4177 authlog->quit_reason = str ? strdup(str) : NULL;
4179 authlog->next = NULL;
4181 //append it to the end of the list...
4182 struct authlogEntry *authlog_entry;
4184 hi->authlog = authlog;
4186 for(authlog_entry = hi->authlog; authlog_entry; authlog_entry = authlog_entry->next) {
4187 if(!authlog_entry->next) {
4188 authlog_entry->next = authlog;
4197 nickserv_db_read_handle(const char *handle, dict_t obj)
4200 struct string_list *masks, *slist;
4201 struct handle_info *hi;
4202 struct userNode *authed_users;
4203 struct userData *channel_list;
4208 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
4209 id = str ? strtoul(str, NULL, 0) : 0;
4210 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
4212 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
4215 if ((hi = get_handle_info(handle))) {
4216 authed_users = hi->users;
4217 channel_list = hi->channels;
4219 hi->channels = NULL;
4220 dict_remove(nickserv_handle_dict, hi->handle);
4222 authed_users = NULL;
4223 channel_list = NULL;
4225 hi = register_handle(handle, str, id);
4227 hi->users = authed_users;
4228 while (authed_users) {
4229 authed_users->handle_info = hi;
4230 authed_users = authed_users->next_authed;
4233 hi->channels = channel_list;
4234 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
4235 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
4236 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
4237 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
4238 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
4239 hi->language = language_find(str ? str : "C");
4240 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
4241 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
4242 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
4244 hi->infoline = strdup(str);
4245 str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING);
4247 hi->website = strdup(str);
4248 str = database_get_data(obj, KEY_DEVNULL, RECDB_QSTRING);
4250 hi->devnull = strdup(str);
4251 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
4252 hi->registered = str ? strtoul(str, NULL, 0) : now;
4253 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
4254 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
4255 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
4256 hi->karma = str ? strtoul(str, NULL, 0) : 0;
4257 /* We want to read the nicks even if disable_nicks is set. This is so
4258 * that we don't lose the nick data entirely. */
4259 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
4261 for (ii=0; ii<slist->used; ii++)
4262 register_nick(slist->list[ii], hi);
4264 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
4266 for (ii=0; str[ii]; ii++)
4267 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
4269 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
4270 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
4271 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
4272 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
4273 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
4274 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
4275 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
4277 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
4279 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
4280 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
4282 nickserv_set_email_addr(hi, str);
4283 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
4285 hi->epithet = strdup(str);
4286 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
4288 hi->fakehost = strdup(str);
4289 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
4291 hi->fakeident = strdup(str);
4292 /* Read the "cookie" sub-database (if it exists). */
4293 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
4295 const char *data, *type, *expires, *cookie_str;
4296 struct handle_cookie *cookie;
4298 cookie = calloc(1, sizeof(*cookie));
4299 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
4300 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
4301 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
4302 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
4303 if (!type || !expires || !cookie_str) {
4304 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
4307 if (!irccasecmp(type, KEY_ACTIVATION))
4308 cookie->type = ACTIVATION;
4309 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
4310 cookie->type = PASSWORD_CHANGE;
4311 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
4312 cookie->type = EMAIL_CHANGE;
4313 else if (!irccasecmp(type, KEY_ALLOWAUTH))
4314 cookie->type = ALLOWAUTH;
4316 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
4319 cookie->expires = strtoul(expires, NULL, 0);
4320 if (cookie->expires < now)
4323 cookie->data = strdup(data);
4324 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
4328 nickserv_bake_cookie(cookie);
4330 nickserv_free_cookie(cookie);
4332 /* Read the "notes" sub-database (if it exists). */
4333 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
4336 struct handle_note *last_note;
4337 struct handle_note *note;
4340 for (it = dict_first(subdb); it; it = iter_next(it)) {
4341 const char *expires;
4345 const char *note_id;
4348 note_id = iter_key(it);
4349 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
4351 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
4354 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
4355 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
4356 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
4357 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
4358 if (!setter || !text || !set) {
4359 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
4362 note = calloc(1, sizeof(*note) + strlen(text));
4364 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
4365 note->set = strtoul(set, NULL, 10);
4366 note->id = strtoul(note_id, NULL, 10);
4367 safestrncpy(note->setter, setter, sizeof(note->setter));
4368 strcpy(note->note, text);
4370 last_note->next = note;
4376 if ((subdb = database_get_data(obj, KEY_AUTHLOG, RECDB_OBJECT)))
4377 dict_foreach(subdb, nickserv_db_read_authlog, hi);
4381 nickserv_saxdb_read(dict_t db) {
4383 struct record_data *rd;
4385 for (it=dict_first(db); it; it=iter_next(it)) {
4387 nickserv_db_read_handle(iter_key(it), rd->d.object);
4392 static NICKSERV_FUNC(cmd_mergedb)
4394 struct timeval start, stop;
4397 NICKSERV_MIN_PARMS(2);
4398 gettimeofday(&start, NULL);
4399 if (!(db = parse_database(argv[1]))) {
4400 reply("NSMSG_DB_UNREADABLE", argv[1]);
4403 nickserv_saxdb_read(db);
4405 gettimeofday(&stop, NULL);
4406 stop.tv_sec -= start.tv_sec;
4407 stop.tv_usec -= start.tv_usec;
4408 if (stop.tv_usec < 0) {
4410 stop.tv_usec += 1000000;
4412 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
4417 expire_handles(UNUSED_ARG(void *data))
4419 dict_iterator_t it, next;
4420 unsigned long expiry;
4421 struct handle_info *hi;
4423 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4424 next = iter_next(it);
4426 if ((hi->opserv_level > 0)
4428 || HANDLE_FLAGGED(hi, FROZEN)
4429 || HANDLE_FLAGGED(hi, NODELETE)) {
4432 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4433 if ((now - hi->lastseen) > expiry) {
4434 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
4435 nickserv_unregister_handle(hi, NULL);
4439 if (nickserv_conf.handle_expire_frequency)
4440 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4444 nickserv_load_dict(const char *fname)
4448 if (!(file = fopen(fname, "r"))) {
4449 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4452 while (fgets(line, sizeof(line), file)) {
4455 if (line[strlen(line)-1] == '\n')
4456 line[strlen(line)-1] = 0;
4457 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4460 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4463 static enum reclaim_action
4464 reclaim_action_from_string(const char *str) {
4466 return RECLAIM_NONE;
4467 else if (!irccasecmp(str, "warn"))
4468 return RECLAIM_WARN;
4469 else if (!irccasecmp(str, "svsnick"))
4470 return RECLAIM_SVSNICK;
4471 else if (!irccasecmp(str, "kill"))
4472 return RECLAIM_KILL;
4474 return RECLAIM_NONE;
4478 nickserv_conf_read(void)
4480 dict_t conf_node, child;
4484 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4485 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4488 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4490 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4491 if (nickserv_conf.valid_handle_regex_set)
4492 regfree(&nickserv_conf.valid_handle_regex);
4494 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4495 nickserv_conf.valid_handle_regex_set = !err;
4496 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4498 nickserv_conf.valid_handle_regex_set = 0;
4500 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4501 if (nickserv_conf.valid_nick_regex_set)
4502 regfree(&nickserv_conf.valid_nick_regex);
4504 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4505 nickserv_conf.valid_nick_regex_set = !err;
4506 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4508 nickserv_conf.valid_nick_regex_set = 0;
4510 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4512 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4513 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4514 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4515 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4516 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4517 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4518 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4519 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4520 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4521 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4522 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4523 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4524 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4525 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4526 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4527 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4528 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4529 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4530 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4531 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4532 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4533 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4534 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4535 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4536 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
4537 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
4538 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4540 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4541 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4542 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4544 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4545 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4546 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4548 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4549 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4550 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4551 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4552 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4553 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4554 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4555 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4556 str = database_get_data(conf_node, KEY_MAX_AUTHLOG_LEN, RECDB_QSTRING);
4557 nickserv_conf.max_authlog_len = str ? strtoul(str, NULL, 0) : 30;
4558 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4559 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4560 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4563 nickserv_conf.ounregister_flags = 0;
4565 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4568 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4570 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4572 nickserv_conf.handle_ts_mode = TS_IGNORE;
4573 else if (!irccasecmp(str, "ircu"))
4574 nickserv_conf.handle_ts_mode = TS_IRCU;
4576 nickserv_conf.handle_ts_mode = TS_IGNORE;
4577 if (!nickserv_conf.disable_nicks) {
4578 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4579 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4580 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4581 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4582 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4583 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4584 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4585 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4587 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4588 for (it=dict_first(child); it; it=iter_next(it)) {
4589 const char *key = iter_key(it), *value;
4593 if (!strncasecmp(key, "uc_", 3))
4594 flag = toupper(key[3]);
4595 else if (!strncasecmp(key, "lc_", 3))
4596 flag = tolower(key[3]);
4600 if ((pos = handle_inverse_flags[flag])) {
4601 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4602 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4605 if (nickserv_conf.weak_password_dict)
4606 dict_delete(nickserv_conf.weak_password_dict);
4607 nickserv_conf.weak_password_dict = dict_new();
4608 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4609 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4610 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4611 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4613 nickserv_load_dict(str);
4614 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4615 if (nickserv && str)
4616 NickChange(nickserv, str, 0);
4617 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4618 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4619 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4620 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4621 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4622 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4623 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4624 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4625 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4626 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4627 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4628 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4629 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4630 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4631 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4632 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4633 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4634 titlehost_suffix = str ? str : "example.net";
4635 str = conf_get_data("server/network", RECDB_QSTRING);
4636 nickserv_conf.network_name = str ? str : "some IRC network";
4637 if (!nickserv_conf.auth_policer_params) {
4638 nickserv_conf.auth_policer_params = policer_params_new();
4639 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4640 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4642 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4643 for (it=dict_first(child); it; it=iter_next(it))
4644 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4648 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4650 char newnick[NICKLEN+1];
4659 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4661 case RECLAIM_SVSNICK:
4663 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4664 } while (GetUserH(newnick));
4665 irc_svsnick(nickserv, user, newnick);
4668 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4669 DelUser(user, nickserv, 1, msg);
4675 nickserv_reclaim_p(void *data) {
4676 struct userNode *user = data;
4677 struct nick_info *ni = get_nick_info(user->nick);
4679 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4683 check_user_nick(struct userNode *user) {
4684 //check if this user is a pending LOC user
4685 if(pendingLOCUsers) {
4686 struct pendingLOCUser *pending, *next, *prev = NULL;
4687 for(pending = pendingLOCUsers; pending; pending = next) {
4688 next = pending->next;
4689 if(user->handle_info == pending->handle_info) {
4690 pending->authlog->user = user;
4691 free((char*) pending->authlog->hostmask);
4692 pending->authlog->hostmask = generate_hostmask(user, GENMASK_USENICK|GENMASK_STRICT_IDENT|GENMASK_NO_HIDING|GENMASK_STRICT_HOST);
4696 pendingLOCUsers = next;
4699 if(now - pending->time > 10) {
4703 pendingLOCUsers = next;
4708 struct nick_info *ni;
4709 user->modes &= ~FLAGS_REGNICK;
4710 if (!(ni = get_nick_info(user->nick)))
4712 if (user->handle_info == ni->owner) {
4713 user->modes |= FLAGS_REGNICK;
4717 if (nickserv_conf.warn_nick_owned)
4718 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4719 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4721 if (nickserv_conf.auto_reclaim_delay)
4722 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4724 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4728 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4730 struct handle_info *hi = NULL;
4733 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4734 if ((hi == NULL) && (serial != 0)) {
4736 inttobase64(id, serial, IDLEN);
4737 hi = dict_find(nickserv_id_dict, id, NULL);
4741 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4742 && (timestamp != hi->registered)) {
4745 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4748 set_user_handle_info(user, hi, 0);
4750 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4755 handle_nick_change(struct userNode *user, const char *old_nick)
4757 struct handle_info *hi;
4759 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4760 dict_remove(nickserv_allow_auth_dict, old_nick);
4761 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4763 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4764 check_user_nick(user);
4768 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), const char *why)
4770 if(user->handle_info) {
4771 //check if theres an open authlog entry
4772 struct authlogEntry *authlog;
4773 for(authlog = user->handle_info->authlog; authlog; authlog = authlog->next) {
4774 if(authlog->user == user) {
4775 authlog->user = NULL;
4776 authlog->logout_time = now;
4777 authlog->quit_reason = strdup(why);
4782 dict_remove(nickserv_allow_auth_dict, user->nick);
4783 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4784 set_user_handle_info(user, NULL, 0);
4787 static struct modcmd *
4788 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4790 if (min_level > 0) {
4792 sprintf(buf, "%u", min_level);
4793 if (must_be_qualified) {
4794 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4796 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4798 } else if (min_level == 0) {
4799 if (must_be_qualified) {
4800 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4802 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4805 if (must_be_qualified) {
4806 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4808 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4814 nickserv_db_cleanup(void)
4816 unreg_del_user_func(nickserv_remove_user);
4817 userList_clean(&curr_helpers);
4818 policer_params_delete(nickserv_conf.auth_policer_params);
4819 dict_delete(nickserv_handle_dict);
4820 dict_delete(nickserv_nick_dict);
4821 dict_delete(nickserv_opt_dict);
4822 dict_delete(nickserv_allow_auth_dict);
4823 dict_delete(nickserv_email_dict);
4824 dict_delete(nickserv_id_dict);
4825 dict_delete(nickserv_conf.weak_password_dict);
4826 free(auth_func_list);
4827 free(unreg_func_list);
4829 free(allowauth_func_list);
4830 free(handle_merge_func_list);
4831 free(failpw_func_list);
4832 if (nickserv_conf.valid_handle_regex_set)
4833 regfree(&nickserv_conf.valid_handle_regex);
4834 if (nickserv_conf.valid_nick_regex_set)
4835 regfree(&nickserv_conf.valid_nick_regex);
4836 struct pendingLOCUser *pending, *next;
4837 for(pending = pendingLOCUsers; pending; pending = next) {
4838 next = pending->next;
4841 pendingLOCUsers = NULL;
4845 init_nickserv(const char *nick)
4848 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4849 reg_new_user_func(check_user_nick);
4850 reg_nick_change_func(handle_nick_change);
4851 reg_del_user_func(nickserv_remove_user);
4852 reg_account_func(handle_account);
4854 /* set up handle_inverse_flags */
4855 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4856 for (i=0; handle_flags[i]; i++) {
4857 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4858 flag_access_levels[i] = 0;
4861 conf_register_reload(nickserv_conf_read);
4862 nickserv_opt_dict = dict_new();
4863 nickserv_email_dict = dict_new();
4864 dict_set_free_keys(nickserv_email_dict, free);
4865 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4867 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4868 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4869 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4870 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4871 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4872 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4873 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4874 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4875 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4876 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4877 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4878 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4879 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4880 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4881 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4882 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4883 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4884 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4885 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4886 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4887 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4888 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4889 if (!nickserv_conf.disable_nicks) {
4890 /* nick management commands */
4891 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4892 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4893 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4894 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4895 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4896 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4898 if (nickserv_conf.email_enabled) {
4899 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4900 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4901 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4902 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4903 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4904 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4906 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4907 /* miscellaneous commands */
4908 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4909 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4910 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4911 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4912 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4913 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4914 nickserv_define_func("AUTHLOG", cmd_authlog, 0, 1, 0);
4916 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4917 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4918 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4919 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4920 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4921 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4922 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
4923 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4924 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4925 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4926 dict_insert(nickserv_opt_dict, "WEBSITE", opt_website);
4927 dict_insert(nickserv_opt_dict, "DEVNULL", opt_devnull);
4928 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4929 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4930 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4931 if (titlehost_suffix) {
4932 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4933 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4934 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4936 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4937 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4938 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4939 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4941 nickserv_handle_dict = dict_new();
4942 dict_set_free_keys(nickserv_handle_dict, free);
4943 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4945 nickserv_id_dict = dict_new();
4946 dict_set_free_keys(nickserv_id_dict, free);
4948 nickserv_nick_dict = dict_new();
4949 dict_set_free_data(nickserv_nick_dict, free);
4951 nickserv_allow_auth_dict = dict_new();
4953 userList_init(&curr_helpers);
4956 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4957 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4958 nickserv_service = service_register(nickserv);
4960 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4961 reg_exit_func(nickserv_db_cleanup);
4962 if(nickserv_conf.handle_expire_frequency)
4963 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4964 message_register_table(msgtab);