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);
2056 str = (authlog->user ? "Never" : "Unknown");
2057 ptr = malloc(strlen(str)+1);
2059 tbl.contents[i][2] = ptr;
2060 tbl.contents[i][3] = (authlog->quit_reason ? authlog->quit_reason : "-");
2063 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
2064 for(i = 1; i < tbl.length; ++i)
2066 free((char *) tbl.contents[i][1]);
2067 free((char *) tbl.contents[i][2]);
2068 free(tbl.contents[i]);
2070 free(tbl.contents[0]);
2076 static NICKSERV_FUNC(cmd_allowauth)
2078 struct userNode *target;
2079 struct handle_info *hi;
2082 NICKSERV_MIN_PARMS(2);
2083 if (!(target = GetUserH(argv[1]))) {
2084 reply("MSG_NICK_UNKNOWN", argv[1]);
2087 if (target->handle_info) {
2088 reply("NSMSG_USER_PREV_AUTH", target->nick);
2091 if (IsStamped(target)) {
2092 /* Unauthenticated users might still have been stamped
2093 previously and could therefore have a hidden host;
2094 do not allow them to authenticate to an account. */
2095 reply("NSMSG_USER_PREV_STAMP", target->nick);
2100 else if (!(hi = get_handle_info(argv[2]))) {
2101 reply("MSG_HANDLE_UNKNOWN", argv[2]);
2105 if (hi->opserv_level > user->handle_info->opserv_level) {
2106 reply("MSG_USER_OUTRANKED", hi->handle);
2109 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
2110 || (hi->opserv_level > 0))
2111 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
2112 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
2115 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
2116 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
2117 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
2118 if (nickserv_conf.email_enabled)
2119 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
2121 if (dict_remove(nickserv_allow_auth_dict, target->nick))
2122 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
2124 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
2126 for (n=0; n<allowauth_func_used; n++)
2127 allowauth_func_list[n](user, target, hi);
2131 static NICKSERV_FUNC(cmd_authcookie)
2133 struct handle_info *hi;
2135 NICKSERV_MIN_PARMS(2);
2136 if (user->handle_info) {
2137 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2140 if (IsStamped(user)) {
2141 /* Unauthenticated users might still have been stamped
2142 previously and could therefore have a hidden host;
2143 do not allow them to authenticate to an account. */
2144 reply("NSMSG_STAMPED_AUTHCOOKIE");
2147 if (!(hi = get_handle_info(argv[1]))) {
2148 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2151 if (!hi->email_addr) {
2152 reply("MSG_SET_EMAIL_ADDR");
2155 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
2159 static NICKSERV_FUNC(cmd_delcookie)
2161 struct handle_info *hi;
2163 hi = user->handle_info;
2165 reply("NSMSG_NO_COOKIE");
2168 switch (hi->cookie->type) {
2171 reply("NSMSG_MUST_TIME_OUT");
2174 nickserv_eat_cookie(hi->cookie);
2175 reply("NSMSG_ATE_COOKIE");
2181 static NICKSERV_FUNC(cmd_odelcookie)
2183 struct handle_info *hi;
2185 NICKSERV_MIN_PARMS(2);
2187 if (!(hi = get_victim_oper(user, argv[1])))
2191 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
2195 nickserv_eat_cookie(hi->cookie);
2196 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
2201 static NICKSERV_FUNC(cmd_resetpass)
2203 struct handle_info *hi;
2204 char crypted[MD5_CRYPT_LENGTH];
2206 NICKSERV_MIN_PARMS(3);
2207 if (user->handle_info) {
2208 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2211 if (IsStamped(user)) {
2212 /* Unauthenticated users might still have been stamped
2213 previously and could therefore have a hidden host;
2214 do not allow them to activate an account. */
2215 reply("NSMSG_STAMPED_RESETPASS");
2218 if (!(hi = get_handle_info(argv[1]))) {
2219 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2222 if (!hi->email_addr) {
2223 reply("MSG_SET_EMAIL_ADDR");
2226 cryptpass(argv[2], crypted);
2228 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
2232 static NICKSERV_FUNC(cmd_cookie)
2234 struct handle_info *hi;
2237 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
2240 NICKSERV_MIN_PARMS(3);
2241 if (!(hi = get_handle_info(argv[1]))) {
2242 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2248 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2249 reply("NSMSG_HANDLE_SUSPENDED");
2254 reply("NSMSG_NO_COOKIE");
2258 /* Check validity of operation before comparing cookie to
2259 * prohibit guessing by authed users. */
2260 if (user->handle_info
2261 && (hi->cookie->type != EMAIL_CHANGE)
2262 && (hi->cookie->type != PASSWORD_CHANGE)) {
2263 reply("NSMSG_CANNOT_COOKIE");
2267 if (strcmp(cookie, hi->cookie->cookie)) {
2268 reply("NSMSG_BAD_COOKIE");
2272 switch (hi->cookie->type) {
2274 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2275 set_user_handle_info(user, hi, 1);
2276 reply("NSMSG_HANDLE_ACTIVATED");
2278 case PASSWORD_CHANGE:
2279 set_user_handle_info(user, hi, 1);
2280 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2281 reply("NSMSG_PASSWORD_CHANGED");
2284 nickserv_set_email_addr(hi, hi->cookie->data);
2285 reply("NSMSG_EMAIL_CHANGED");
2288 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2289 set_user_handle_info(user, hi, 1);
2290 nickserv_addmask(user, hi, mask);
2291 reply("NSMSG_AUTH_SUCCESS");
2296 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2297 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2301 nickserv_eat_cookie(hi->cookie);
2306 static NICKSERV_FUNC(cmd_oregnick) {
2308 struct handle_info *target;
2309 struct nick_info *ni;
2311 NICKSERV_MIN_PARMS(3);
2312 if (!(target = modcmd_get_handle_info(user, argv[1])))
2315 if (!is_registerable_nick(nick)) {
2316 reply("NSMSG_BAD_NICK", nick);
2319 ni = dict_find(nickserv_nick_dict, nick, NULL);
2321 reply("NSMSG_NICK_EXISTS", nick);
2324 register_nick(nick, target);
2325 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2329 static NICKSERV_FUNC(cmd_regnick) {
2331 struct nick_info *ni;
2333 if (!is_registerable_nick(user->nick)) {
2334 reply("NSMSG_BAD_NICK", user->nick);
2337 /* count their nicks, see if it's too many */
2338 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2339 if (n >= nickserv_conf.nicks_per_handle) {
2340 reply("NSMSG_TOO_MANY_NICKS");
2343 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2345 reply("NSMSG_NICK_EXISTS", user->nick);
2348 register_nick(user->nick, user->handle_info);
2349 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2353 static NICKSERV_FUNC(cmd_pass)
2355 struct handle_info *hi;
2356 const char *old_pass, *new_pass;
2358 NICKSERV_MIN_PARMS(3);
2359 hi = user->handle_info;
2363 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2364 if (!checkpass(old_pass, hi->passwd)) {
2365 argv[1] = "BADPASS";
2366 reply("NSMSG_PASSWORD_INVALID");
2369 cryptpass(new_pass, hi->passwd);
2371 reply("NSMSG_PASS_SUCCESS");
2376 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2379 char *new_mask = canonicalize_hostmask(strdup(mask));
2380 for (i=0; i<hi->masks->used; i++) {
2381 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2382 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2387 string_list_append(hi->masks, new_mask);
2388 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2392 static NICKSERV_FUNC(cmd_addmask)
2395 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2396 int res = nickserv_addmask(user, user->handle_info, mask);
2400 if (!is_gline(argv[1])) {
2401 reply("NSMSG_MASK_INVALID", argv[1]);
2404 return nickserv_addmask(user, user->handle_info, argv[1]);
2408 static NICKSERV_FUNC(cmd_oaddmask)
2410 struct handle_info *hi;
2412 NICKSERV_MIN_PARMS(3);
2413 if (!(hi = get_victim_oper(user, argv[1])))
2415 return nickserv_addmask(user, hi, argv[2]);
2419 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2422 for (i=0; i<hi->masks->used; i++) {
2423 if (!strcmp(del_mask, hi->masks->list[i])) {
2424 char *old_mask = hi->masks->list[i];
2425 if (hi->masks->used == 1 && !force) {
2426 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2429 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2430 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2435 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2439 static NICKSERV_FUNC(cmd_delmask)
2441 NICKSERV_MIN_PARMS(2);
2442 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2445 static NICKSERV_FUNC(cmd_odelmask)
2447 struct handle_info *hi;
2448 NICKSERV_MIN_PARMS(3);
2449 if (!(hi = get_victim_oper(user, argv[1])))
2451 return nickserv_delmask(user, hi, argv[2], 1);
2455 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2456 unsigned int nn, add = 1, pos;
2457 unsigned long added, removed, flag;
2459 for (added=removed=nn=0; str[nn]; nn++) {
2461 case '+': add = 1; break;
2462 case '-': add = 0; break;
2464 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2465 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2468 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2469 /* cheesy avoidance of looking up the flag name.. */
2470 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2473 flag = 1 << (pos - 1);
2475 added |= flag, removed &= ~flag;
2477 removed |= flag, added &= ~flag;
2482 *premoved = removed;
2487 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2489 unsigned long before, after, added, removed;
2490 struct userNode *uNode;
2492 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2493 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2495 hi->flags = (hi->flags | added) & ~removed;
2496 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2498 /* Strip helping flag if they're only a support helper and not
2499 * currently in #support. */
2500 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2501 struct channelList *schannels;
2503 schannels = chanserv_support_channels();
2504 for (ii = 0; ii < schannels->used; ++ii)
2505 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2507 if (ii == schannels->used)
2508 HANDLE_CLEAR_FLAG(hi, HELPING);
2511 if (after && !before) {
2512 /* Add user to current helper list. */
2513 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2514 userList_append(&curr_helpers, uNode);
2515 } else if (!after && before) {
2516 /* Remove user from current helper list. */
2517 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2518 userList_remove(&curr_helpers, uNode);
2525 set_list(struct userNode *user, struct handle_info *hi, int override)
2529 char *set_display[] = {
2530 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2531 "EMAIL", "MAXLOGINS", "LANGUAGE", "DEVNULL"
2534 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2536 /* Do this so options are presented in a consistent order. */
2537 for (i = 0; i < ArrayLength(set_display); ++i)
2538 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2539 opt(user, hi, override, 0, NULL);
2542 static NICKSERV_FUNC(cmd_set)
2544 struct handle_info *hi;
2547 hi = user->handle_info;
2549 set_list(user, hi, 0);
2552 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2553 reply("NSMSG_INVALID_OPTION", argv[1]);
2556 return opt(user, hi, 0, argc-1, argv+1);
2559 static NICKSERV_FUNC(cmd_oset)
2561 struct handle_info *hi;
2562 struct svccmd *subcmd;
2564 char cmdname[MAXLEN];
2566 NICKSERV_MIN_PARMS(2);
2568 if (!(hi = get_victim_oper(user, argv[1])))
2572 set_list(user, hi, 0);
2576 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2577 reply("NSMSG_INVALID_OPTION", argv[2]);
2581 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2582 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2583 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2586 return opt(user, hi, 1, argc-2, argv+2);
2589 static OPTION_FUNC(opt_info)
2593 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2595 hi->infoline = NULL;
2597 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2601 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2602 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2606 static OPTION_FUNC(opt_devnull)
2608 const char *devnull;
2612 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2615 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2619 devnull = unsplit_string(argv+1, argc-1, NULL);
2620 if(devnull_check(devnull) == 1) {
2623 hi->devnull = strdup(devnull);
2628 devnull = hi->devnull ? hi->devnull : user_find_message(user, "MSG_NONE");
2629 send_message(user, nickserv, "NSMSG_SET_DEVNULL", devnull);
2633 void nickserv_devnull_delete(char *name) {
2635 struct handle_info *hi;
2637 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2639 if (hi->devnull && !irccasecmp(name, hi->devnull)) {
2646 void nickserv_devnull_rename(char *oldname, char *newname) {
2648 struct handle_info *hi;
2650 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2652 if (hi->devnull && !irccasecmp(oldname, hi->devnull)) {
2653 hi->devnull = strdup(newname);
2658 static OPTION_FUNC(opt_website)
2660 const char *website;
2663 if (!HANDLE_FLAGGED(user->handle_info, BOT)) {
2664 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2667 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2671 website = unsplit_string(argv+1, argc-1, NULL);
2672 hi->website = strdup(website);
2675 if (HANDLE_FLAGGED(user->handle_info, BOT)) {
2676 website = hi->website ? hi->website : user_find_message(user, "MSG_NONE");
2677 send_message(user, nickserv, "NSMSG_SET_WEBSITE", website);
2682 static OPTION_FUNC(opt_width)
2685 hi->screen_width = strtoul(argv[1], NULL, 0);
2687 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2688 hi->screen_width = MIN_LINE_SIZE;
2689 else if (hi->screen_width > MAX_LINE_SIZE)
2690 hi->screen_width = MAX_LINE_SIZE;
2692 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2696 static OPTION_FUNC(opt_tablewidth)
2699 hi->table_width = strtoul(argv[1], NULL, 0);
2701 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2702 hi->table_width = MIN_LINE_SIZE;
2703 else if (hi->screen_width > MAX_LINE_SIZE)
2704 hi->table_width = MAX_LINE_SIZE;
2706 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2710 static OPTION_FUNC(opt_color)
2713 if (enabled_string(argv[1]))
2714 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2715 else if (disabled_string(argv[1]))
2716 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2718 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2723 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2727 static OPTION_FUNC(opt_privmsg)
2730 if (enabled_string(argv[1]))
2731 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2732 else if (disabled_string(argv[1]))
2733 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2735 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2740 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2744 static OPTION_FUNC(opt_autohide)
2747 if (enabled_string(argv[1]))
2748 HANDLE_SET_FLAG(hi, AUTOHIDE);
2749 else if (disabled_string(argv[1]))
2750 HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
2752 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2757 send_message(user, nickserv, "NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
2761 static OPTION_FUNC(opt_style)
2766 if (!irccasecmp(argv[1], "Zoot"))
2767 hi->userlist_style = HI_STYLE_ZOOT;
2768 else if (!irccasecmp(argv[1], "def"))
2769 hi->userlist_style = HI_STYLE_DEF;
2772 switch (hi->userlist_style) {
2781 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2785 static OPTION_FUNC(opt_password)
2788 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2793 cryptpass(argv[1], hi->passwd);
2795 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2799 static OPTION_FUNC(opt_flags)
2802 unsigned int ii, flen;
2805 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2810 nickserv_apply_flags(user, hi, argv[1]);
2812 for (ii = flen = 0; handle_flags[ii]; ii++)
2813 if (hi->flags & (1 << ii))
2814 flags[flen++] = handle_flags[ii];
2817 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2819 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2823 static OPTION_FUNC(opt_email)
2827 if (!is_valid_email_addr(argv[1])) {
2828 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2831 if ((str = mail_prohibited_address(argv[1]))) {
2832 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2835 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2836 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2838 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2840 nickserv_set_email_addr(hi, argv[1]);
2842 nickserv_eat_cookie(hi->cookie);
2843 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2846 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2850 static OPTION_FUNC(opt_maxlogins)
2852 unsigned char maxlogins;
2854 maxlogins = strtoul(argv[1], NULL, 0);
2855 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2856 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2859 hi->maxlogins = maxlogins;
2861 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2862 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2866 static OPTION_FUNC(opt_language)
2868 struct language *lang;
2870 lang = language_find(argv[1]);
2871 if (irccasecmp(lang->name, argv[1]))
2872 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2873 hi->language = lang;
2875 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2879 static OPTION_FUNC(opt_karma)
2882 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2887 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2888 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2889 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2890 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2892 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2896 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2901 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2902 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2904 if ((user->handle_info->opserv_level < target->opserv_level)
2905 || ((user->handle_info->opserv_level == target->opserv_level)
2906 && (user->handle_info->opserv_level < 1000))) {
2907 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2910 if ((user->handle_info->opserv_level < new_level)
2911 || ((user->handle_info->opserv_level == new_level)
2912 && (user->handle_info->opserv_level < 1000))) {
2913 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2916 if (user->handle_info == target) {
2917 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2920 if (target->opserv_level == new_level)
2922 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2923 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2924 target->opserv_level = new_level;
2928 static OPTION_FUNC(opt_level)
2933 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2937 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2938 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2942 static OPTION_FUNC(opt_epithet)
2945 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2949 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2950 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2953 if ((epithet[0] == '*') && !epithet[1])
2956 hi->epithet = strdup(epithet);
2960 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2962 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2966 static OPTION_FUNC(opt_title)
2971 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2975 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2977 if (strchr(title, '.')) {
2978 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2981 if ((strlen(user->handle_info->handle) + strlen(title) +
2982 strlen(titlehost_suffix) + 2) > HOSTLEN) {
2983 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2988 if (!strcmp(title, "*")) {
2989 hi->fakehost = NULL;
2991 hi->fakehost = malloc(strlen(title)+2);
2992 hi->fakehost[0] = '.';
2993 strcpy(hi->fakehost+1, title);
2995 apply_fakehost(hi, NULL);
2996 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2997 title = hi->fakehost + 1;
3001 title = user_find_message(user, "MSG_NONE");
3002 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
3006 static OPTION_FUNC(opt_fakehost)
3008 char mask[USERLEN + HOSTLEN + 2];
3012 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3016 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
3017 if(strlen(argv[1]) >= sizeof(mask)) {
3018 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
3022 safestrncpy(mask, argv[1], sizeof(mask));
3024 if ((host = strrchr(mask, '@')) && host != mask) {
3025 /* If ident@host was used and the user doesn't have access to set idents, do not change anything. */
3026 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3038 if (ident && strlen(ident) > USERLEN) {
3039 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3043 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
3044 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
3048 if (host && host[0]) {
3050 if (!strcmp(host, "*"))
3051 hi->fakehost = NULL;
3053 hi->fakehost = strdup(host);
3054 host = hi->fakehost;
3057 host = generate_fakehost(hi);
3060 free(hi->fakeident);
3061 if (!strcmp(ident, "*"))
3062 hi->fakeident = NULL;
3064 hi->fakeident = strdup(ident);
3065 ident = hi->fakeident;
3068 ident = generate_fakeident(hi, NULL);
3070 apply_fakehost(hi, NULL);
3072 host = generate_fakehost(hi);
3073 ident = generate_fakeident(hi, NULL);
3076 host = (char *) user_find_message(user, "MSG_NONE");
3078 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
3080 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
3084 static OPTION_FUNC(opt_fakeident)
3089 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3093 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3095 if (strlen(ident) > USERLEN) {
3096 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3099 free(hi->fakeident);
3100 if (!strcmp(ident, "*"))
3101 hi->fakeident = NULL;
3103 hi->fakeident = strdup(ident);
3104 ident = hi->fakeident;
3105 apply_fakehost(hi, NULL);
3107 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
3109 ident = user_find_message(user, "MSG_NONE");
3110 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
3114 static NICKSERV_FUNC(cmd_reclaim)
3116 struct nick_info *ni;
3117 struct userNode *victim;
3119 NICKSERV_MIN_PARMS(2);
3120 ni = dict_find(nickserv_nick_dict, argv[1], 0);
3122 reply("NSMSG_UNKNOWN_NICK", argv[1]);
3125 if (ni->owner != user->handle_info) {
3126 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
3129 victim = GetUserH(ni->nick);
3131 reply("MSG_NICK_UNKNOWN", ni->nick);
3134 if (victim == user) {
3135 reply("NSMSG_NICK_USER_YOU");
3138 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
3139 switch (nickserv_conf.reclaim_action) {
3140 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
3141 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
3142 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
3143 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
3148 static NICKSERV_FUNC(cmd_unregnick)
3151 struct handle_info *hi;
3152 struct nick_info *ni;
3154 hi = user->handle_info;
3155 nick = (argc < 2) ? user->nick : (const char*)argv[1];
3156 ni = dict_find(nickserv_nick_dict, nick, NULL);
3158 reply("NSMSG_UNKNOWN_NICK", nick);
3161 if (hi != ni->owner) {
3162 reply("NSMSG_NOT_YOUR_NICK", nick);
3165 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3170 static NICKSERV_FUNC(cmd_ounregnick)
3172 struct nick_info *ni;
3174 NICKSERV_MIN_PARMS(2);
3175 if (!(ni = get_nick_info(argv[1]))) {
3176 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
3179 if (!oper_outranks(user, ni->owner))
3181 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3186 static NICKSERV_FUNC(cmd_unregister)
3188 struct handle_info *hi;
3191 NICKSERV_MIN_PARMS(2);
3192 hi = user->handle_info;
3195 if (checkpass(passwd, hi->passwd)) {
3196 nickserv_unregister_handle(hi, user);
3199 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3200 reply("NSMSG_PASSWORD_INVALID");
3205 static NICKSERV_FUNC(cmd_ounregister)
3207 struct handle_info *hi;
3208 char reason[MAXLEN];
3211 NICKSERV_MIN_PARMS(2);
3212 if (!(hi = get_victim_oper(user, argv[1])))
3215 if (HANDLE_FLAGGED(hi, NODELETE)) {
3216 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
3220 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
3222 ((hi->flags & nickserv_conf.ounregister_flags)
3224 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
3225 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
3229 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
3230 global_message(MESSAGE_RECIPIENT_STAFF, reason);
3231 nickserv_unregister_handle(hi, user);
3235 static NICKSERV_FUNC(cmd_status)
3237 if (nickserv_conf.disable_nicks) {
3238 reply("NSMSG_GLOBAL_STATS_NONICK",
3239 dict_size(nickserv_handle_dict));
3241 if (user->handle_info) {
3243 struct nick_info *ni;
3244 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
3245 reply("NSMSG_HANDLE_STATS", cnt);
3247 reply("NSMSG_HANDLE_NONE");
3249 reply("NSMSG_GLOBAL_STATS",
3250 dict_size(nickserv_handle_dict),
3251 dict_size(nickserv_nick_dict));
3256 static NICKSERV_FUNC(cmd_ghost)
3258 struct userNode *target;
3259 char reason[MAXLEN];
3261 NICKSERV_MIN_PARMS(2);
3262 if (!(target = GetUserH(argv[1]))) {
3263 reply("MSG_NICK_UNKNOWN", argv[1]);
3266 if (target == user) {
3267 reply("NSMSG_CANNOT_GHOST_SELF");
3270 if (!target->handle_info || (target->handle_info != user->handle_info)) {
3271 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
3274 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
3275 DelUser(target, nickserv, 1, reason);
3276 reply("NSMSG_GHOST_KILLED", argv[1]);
3280 static NICKSERV_FUNC(cmd_vacation)
3282 HANDLE_SET_FLAG(user->handle_info, FROZEN);
3283 reply("NSMSG_ON_VACATION");
3287 static NICKSERV_FUNC(cmd_addnote)
3289 struct handle_info *hi;
3290 unsigned long duration;
3293 struct handle_note *prev;
3294 struct handle_note *note;
3296 /* Parse parameters and figure out values for note's fields. */
3297 NICKSERV_MIN_PARMS(4);
3298 hi = get_victim_oper(user, argv[1]);
3301 if(!strcmp(argv[2], "0"))
3303 else if(!(duration = ParseInterval(argv[2])))
3305 reply("MSG_INVALID_DURATION", argv[2]);
3308 if (duration > 2*365*86400) {
3309 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
3312 unsplit_string(argv + 3, argc - 3, text);
3313 WALK_NOTES(hi, prev, note) {}
3314 id = prev ? (prev->id + 1) : 1;
3316 /* Create the new note structure. */
3317 note = calloc(1, sizeof(*note) + strlen(text));
3319 note->expires = duration ? (now + duration) : 0;
3322 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
3323 strcpy(note->note, text);
3328 reply("NSMSG_NOTE_ADDED", id, hi->handle);
3332 static NICKSERV_FUNC(cmd_delnote)
3334 struct handle_info *hi;
3335 struct handle_note *prev;
3336 struct handle_note *note;
3339 NICKSERV_MIN_PARMS(3);
3340 hi = get_victim_oper(user, argv[1]);
3343 id = strtoul(argv[2], NULL, 10);
3344 WALK_NOTES(hi, prev, note) {
3345 if (id == note->id) {
3347 prev->next = note->next;
3349 hi->notes = note->next;
3351 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3355 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3360 nickserv_saxdb_write(struct saxdb_context *ctx) {
3362 struct handle_info *hi;
3365 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3367 assert(hi->id != 0);
3368 saxdb_start_record(ctx, iter_key(it), 0);
3370 struct handle_cookie *cookie = hi->cookie;
3373 switch (cookie->type) {
3374 case ACTIVATION: type = KEY_ACTIVATION; break;
3375 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3376 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3377 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3378 default: type = NULL; break;
3381 saxdb_start_record(ctx, KEY_COOKIE, 0);
3382 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3383 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3385 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3386 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3387 saxdb_end_record(ctx);
3391 struct handle_note *prev, *note;
3392 saxdb_start_record(ctx, KEY_NOTES, 0);
3393 WALK_NOTES(hi, prev, note) {
3394 snprintf(flags, sizeof(flags), "%d", note->id);
3395 saxdb_start_record(ctx, flags, 0);
3397 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3398 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3399 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3400 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3401 saxdb_end_record(ctx);
3403 saxdb_end_record(ctx);
3406 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3408 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3410 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3412 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3416 for (ii=flen=0; handle_flags[ii]; ++ii)
3417 if (hi->flags & (1 << ii))
3418 flags[flen++] = handle_flags[ii];
3420 saxdb_write_string(ctx, KEY_FLAGS, flags);
3422 saxdb_write_int(ctx, KEY_ID, hi->id);
3424 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3426 saxdb_write_string(ctx, KEY_DEVNULL, hi->devnull);
3428 saxdb_write_string(ctx, KEY_WEBSITE, hi->website);
3429 if (hi->last_quit_host[0])
3430 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3431 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3433 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3434 if (hi->masks->used)
3435 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3437 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3439 struct string_list *slist;
3440 struct nick_info *ni;
3442 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3443 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3444 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3448 if (hi->opserv_level)
3449 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3450 if (hi->language != lang_C)
3451 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3452 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3453 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3454 if (hi->screen_width)
3455 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3456 if (hi->table_width)
3457 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3458 flags[0] = hi->userlist_style;
3460 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3462 saxdb_start_record(ctx, KEY_AUTHLOG, 0);
3463 struct authlogEntry *authlog;
3465 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
3466 saxdb_start_record(ctx, strtab(++i), 0);
3467 saxdb_write_int(ctx, KEY_AUTHLOG_LOGIN_TIME, authlog->login_time);
3468 saxdb_write_int(ctx, KEY_AUTHLOG_LOGOUT_TIME, authlog->logout_time);
3469 saxdb_write_string(ctx, KEY_AUTHLOG_HOSTMASK, authlog->hostmask);
3470 if(authlog->quit_reason)
3471 saxdb_write_string(ctx, KEY_AUTHLOG_QUIT_REASON, authlog->quit_reason);
3472 saxdb_end_record(ctx);
3474 saxdb_end_record(ctx); //END KEY_AUTHLOG
3476 saxdb_end_record(ctx);
3481 static handle_merge_func_t *handle_merge_func_list;
3482 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3485 reg_handle_merge_func(handle_merge_func_t func)
3487 if (handle_merge_func_used == handle_merge_func_size) {
3488 if (handle_merge_func_size) {
3489 handle_merge_func_size <<= 1;
3490 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3492 handle_merge_func_size = 8;
3493 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3496 handle_merge_func_list[handle_merge_func_used++] = func;
3499 static NICKSERV_FUNC(cmd_merge)
3501 struct handle_info *hi_from, *hi_to;
3502 struct userNode *last_user;
3503 struct userData *cList, *cListNext;
3504 unsigned int ii, jj, n;
3505 char buffer[MAXLEN];
3507 NICKSERV_MIN_PARMS(3);
3509 if (!(hi_from = get_victim_oper(user, argv[1])))
3511 if (!(hi_to = get_victim_oper(user, argv[2])))
3513 if (hi_to == hi_from) {
3514 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3518 for (n=0; n<handle_merge_func_used; n++)
3519 handle_merge_func_list[n](user, hi_to, hi_from);
3521 /* Append "from" handle's nicks to "to" handle's nick list. */
3523 struct nick_info *last_ni;
3524 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3525 last_ni->next = hi_from->nicks;
3527 while (hi_from->nicks) {
3528 hi_from->nicks->owner = hi_to;
3529 hi_from->nicks = hi_from->nicks->next;
3532 /* Merge the hostmasks. */
3533 for (ii=0; ii<hi_from->masks->used; ii++) {
3534 char *mask = hi_from->masks->list[ii];
3535 for (jj=0; jj<hi_to->masks->used; jj++)
3536 if (match_ircglobs(hi_to->masks->list[jj], mask))
3538 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3539 string_list_append(hi_to->masks, strdup(mask));
3542 /* Merge the lists of authed users. */
3544 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3545 last_user->next_authed = hi_from->users;
3547 hi_to->users = hi_from->users;
3549 /* Repoint the old "from" handle's users. */
3550 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3551 last_user->handle_info = hi_to;
3553 hi_from->users = NULL;
3555 /* Merge channel userlists. */
3556 for (cList=hi_from->channels; cList; cList=cListNext) {
3557 struct userData *cList2;
3558 cListNext = cList->u_next;
3559 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3560 if (cList->channel == cList2->channel)
3562 if (cList2 && (cList2->access >= cList->access)) {
3563 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);
3564 /* keep cList2 in hi_to; remove cList from hi_from */
3565 del_channel_user(cList, 1);
3568 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);
3569 /* remove the lower-ranking cList2 from hi_to */
3570 del_channel_user(cList2, 1);
3572 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3574 /* cList needs to be moved from hi_from to hi_to */
3575 cList->handle = hi_to;
3576 /* Remove from linked list for hi_from */
3577 assert(!cList->u_prev);
3578 hi_from->channels = cList->u_next;
3580 cList->u_next->u_prev = cList->u_prev;
3581 /* Add to linked list for hi_to */
3582 cList->u_prev = NULL;
3583 cList->u_next = hi_to->channels;
3584 if (hi_to->channels)
3585 hi_to->channels->u_prev = cList;
3586 hi_to->channels = cList;
3590 /* Do they get an OpServ level promotion? */
3591 if (hi_from->opserv_level > hi_to->opserv_level)
3592 hi_to->opserv_level = hi_from->opserv_level;
3594 /* What about last seen time? */
3595 if (hi_from->lastseen > hi_to->lastseen)
3596 hi_to->lastseen = hi_from->lastseen;
3598 /* New karma is the sum of the two original karmas. */
3599 hi_to->karma += hi_from->karma;
3601 /* Does a fakehost carry over? (This intentionally doesn't set it
3602 * for users previously attached to hi_to. They'll just have to
3605 if (hi_from->fakehost && !hi_to->fakehost)
3606 hi_to->fakehost = strdup(hi_from->fakehost);
3607 if (hi_from->fakeident && !hi_to->fakeident)
3608 hi_to->fakeident = strdup(hi_from->fakeident);
3610 /* Notify of success. */
3611 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3612 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3613 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3615 /* Unregister the "from" handle. */
3616 nickserv_unregister_handle(hi_from, NULL);
3621 #define NICKSERV_DISCRIM_FIELDS_AUTH 0x01
3622 #define NICKSERV_DISCRIM_FIELDS_EMAIL 0x02
3623 #define NICKSERV_DISCRIM_FIELDS_SEEN 0x04
3624 #define NICKSERV_DISCRIM_FIELDS_ACCESS 0x08
3625 #define NICKSERV_DISCRIM_FIELDS_FAKEHOST 0x10
3626 #define NICKSERV_DISCRIM_FIELDS_WEBSITE 0x20
3627 #define NICKSERV_DISCRIM_FIELDS_DEVNULL 0x40
3629 #define NICKSERV_DISCRIM_FIELD_COUNT 7
3631 struct nickserv_discrim {
3632 unsigned int show_fields;
3633 struct helpfile_table *output_table;
3634 int output_table_pos;
3635 unsigned int output_table_free_fields;
3637 unsigned long flags_on, flags_off;
3638 unsigned long min_registered, max_registered;
3639 unsigned long lastseen;
3641 int min_level, max_level;
3642 int min_karma, max_karma;
3643 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3644 const char *nickmask;
3645 const char *hostmask;
3646 const char *fakehostmask;
3647 const char *fakeidentmask;
3648 const char *website;
3649 const char *devnullclass;
3650 const char *handlemask;
3651 const char *emailmask;
3654 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi, struct nickserv_discrim *discrim);
3656 struct discrim_apply_info {
3657 struct nickserv_discrim *discrim;
3658 discrim_search_func func;
3659 struct userNode *source;
3660 unsigned int matched;
3663 static struct nickserv_discrim *
3664 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3667 struct nickserv_discrim *discrim;
3669 discrim = malloc(sizeof(*discrim));
3670 memset(discrim, 0, sizeof(*discrim));
3671 discrim->min_level = 0;
3672 discrim->max_level = INT_MAX;
3673 discrim->limit = 50;
3674 discrim->min_registered = 0;
3675 discrim->max_registered = ULONG_MAX;
3676 discrim->lastseen = ULONG_MAX;
3677 discrim->min_karma = INT_MIN;
3678 discrim->max_karma = INT_MAX;
3680 for (i=0; i<argc; i++) {
3681 if (i == argc - 1) {
3682 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3685 if (!irccasecmp(argv[i], "limit")) {
3686 discrim->limit = strtoul(argv[++i], NULL, 0);
3687 } else if (!irccasecmp(argv[i], "flags")) {
3688 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3689 } else if (!irccasecmp(argv[i], "fields")) {
3690 char *fields = argv[++i];
3691 char *delimiter = strstr(fields, ",");
3695 if(!irccasecmp(fields, "auth"))
3696 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_AUTH;
3697 else if(!irccasecmp(fields, "email"))
3698 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_EMAIL;
3699 else if(!irccasecmp(fields, "seen"))
3700 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_SEEN;
3701 else if(!irccasecmp(fields, "access"))
3702 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_ACCESS;
3703 else if(!irccasecmp(fields, "fakehost"))
3704 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_FAKEHOST;
3705 else if(!irccasecmp(fields, "website") && IsBot(user))
3706 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_WEBSITE;
3707 else if(!irccasecmp(fields, "devnull"))
3708 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_DEVNULL;
3710 send_message(user, nickserv, "MSG_INVALID_FIELD", fields);
3715 fields = delimiter+1;
3717 delimiter = strstr(fields, ",");
3723 } else if (!irccasecmp(argv[i], "registered")) {
3724 const char *cmp = argv[++i];
3725 if (cmp[0] == '<') {
3726 if (cmp[1] == '=') {
3727 discrim->min_registered = now - ParseInterval(cmp+2);
3729 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3731 } else if (cmp[0] == '=') {
3732 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3733 } else if (cmp[0] == '>') {
3734 if (cmp[1] == '=') {
3735 discrim->max_registered = now - ParseInterval(cmp+2);
3737 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3740 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3742 } else if (!irccasecmp(argv[i], "seen")) {
3743 discrim->lastseen = now - ParseInterval(argv[++i]);
3744 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3745 discrim->nickmask = argv[++i];
3746 } else if (!irccasecmp(argv[i], "hostmask")) {
3748 if (!irccasecmp(argv[i], "exact")) {
3749 if (i == argc - 1) {
3750 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3753 discrim->hostmask_type = EXACT;
3754 } else if (!irccasecmp(argv[i], "subset")) {
3755 if (i == argc - 1) {
3756 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3759 discrim->hostmask_type = SUBSET;
3760 } else if (!irccasecmp(argv[i], "superset")) {
3761 if (i == argc - 1) {
3762 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3765 discrim->hostmask_type = SUPERSET;
3766 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3767 if (i == argc - 1) {
3768 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3771 discrim->hostmask_type = LASTQUIT;
3774 discrim->hostmask_type = SUPERSET;
3776 discrim->hostmask = argv[++i];
3777 } else if (!irccasecmp(argv[i], "fakehost")) {
3778 if (!irccasecmp(argv[++i], "*")) {
3779 discrim->fakehostmask = 0;
3781 discrim->fakehostmask = argv[i];
3783 } else if (!irccasecmp(argv[i], "fakeident")) {
3784 if (!irccasecmp(argv[++i], "*")) {
3785 discrim->fakeidentmask = 0;
3787 discrim->fakeidentmask = argv[i];
3789 } else if (!irccasecmp(argv[i], "website")) {
3790 if (!irccasecmp(argv[++i], "*")) {
3791 discrim->website = 0;
3793 discrim->website = argv[i];
3795 } else if (!irccasecmp(argv[i], "devnull")) {
3796 if (!irccasecmp(argv[++i], "*")) {
3797 discrim->devnullclass = 0;
3799 discrim->devnullclass = argv[i];
3801 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3802 if (!irccasecmp(argv[++i], "*")) {
3803 discrim->handlemask = 0;
3805 discrim->handlemask = argv[i];
3807 } else if (!irccasecmp(argv[i], "email")) {
3808 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3809 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3811 } else if (!irccasecmp(argv[++i], "*")) {
3812 discrim->emailmask = 0;
3814 discrim->emailmask = argv[i];
3816 } else if (!irccasecmp(argv[i], "access")) {
3817 const char *cmp = argv[++i];
3818 if (cmp[0] == '<') {
3819 if (discrim->min_level == 0) discrim->min_level = 1;
3820 if (cmp[1] == '=') {
3821 discrim->max_level = strtoul(cmp+2, NULL, 0);
3823 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3825 } else if (cmp[0] == '=') {
3826 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3827 } else if (cmp[0] == '>') {
3828 if (cmp[1] == '=') {
3829 discrim->min_level = strtoul(cmp+2, NULL, 0);
3831 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3834 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3836 } else if (!irccasecmp(argv[i], "karma")) {
3837 const char *cmp = argv[++i];
3838 if (cmp[0] == '<') {
3839 if (cmp[1] == '=') {
3840 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3842 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3844 } else if (cmp[0] == '=') {
3845 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3846 } else if (cmp[0] == '>') {
3847 if (cmp[1] == '=') {
3848 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3850 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3853 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3856 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3867 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3869 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3870 || (discrim->flags_off & hi->flags)
3871 || (discrim->min_registered > hi->registered)
3872 || (discrim->max_registered < hi->registered)
3873 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3874 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3875 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3876 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3877 || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website)))
3878 || (discrim->devnullclass && (!hi->devnull || !match_ircglob(hi->devnull, discrim->devnullclass)))
3879 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3880 || (discrim->min_level > hi->opserv_level)
3881 || (discrim->max_level < hi->opserv_level)
3882 || (discrim->min_karma > hi->karma)
3883 || (discrim->max_karma < hi->karma)
3887 if (discrim->hostmask) {
3889 for (i=0; i<hi->masks->used; i++) {
3890 const char *mask = hi->masks->list[i];
3891 if ((discrim->hostmask_type == SUBSET)
3892 && (match_ircglobs(discrim->hostmask, mask))) break;
3893 else if ((discrim->hostmask_type == EXACT)
3894 && !irccasecmp(discrim->hostmask, mask)) break;
3895 else if ((discrim->hostmask_type == SUPERSET)
3896 && (match_ircglobs(mask, discrim->hostmask))) break;
3897 else if ((discrim->hostmask_type == LASTQUIT)
3898 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3900 if (i==hi->masks->used) return 0;
3902 if (discrim->nickmask) {
3903 struct nick_info *nick = hi->nicks;
3905 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3908 if (!nick) return 0;
3914 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3916 dict_iterator_t it, next;
3917 unsigned int matched;
3919 for (it = dict_first(nickserv_handle_dict), matched = 0;
3920 it && (matched < discrim->limit);
3922 next = iter_next(it);
3923 if (nickserv_discrim_match(discrim, iter_data(it))) {
3924 dsf(source, iter_data(it), discrim);
3932 search_print_func(struct userNode *source, struct handle_info *match, struct nickserv_discrim *discrim)
3934 if(discrim->show_fields) {
3936 if(discrim->output_table) {
3937 discrim->output_table->contents[++discrim->output_table_pos] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
3939 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
3940 discrim->output_table->contents[discrim->output_table_pos][i++] = match->handle;
3941 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
3942 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->email_addr ? match->email_addr : "*");
3943 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN) {
3945 char seenBuf[INTERVALLEN];
3948 } else if(match->lastseen == 0) {
3951 seen = intervalString(seenBuf, now - match->lastseen, source->handle_info);
3953 discrim->output_table_free_fields |= 1 << i;
3954 discrim->output_table->contents[discrim->output_table_pos][i++] = strdup(seen);
3956 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
3957 discrim->output_table->contents[discrim->output_table_pos][i++] = strtab(match->opserv_level);
3958 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
3959 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->fakehost ? match->fakehost : "*");
3960 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
3961 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->website ? match->website : "*");
3962 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
3963 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->devnull ? match->devnull : "*");
3967 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3971 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match), UNUSED_ARG(struct nickserv_discrim *discrim))
3976 search_unregister_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
3978 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3979 nickserv_unregister_handle(match, source);
3983 nickserv_sort_accounts_by_access(const void *a, const void *b)
3985 const struct handle_info *hi_a = *(const struct handle_info**)a;
3986 const struct handle_info *hi_b = *(const struct handle_info**)b;
3987 if (hi_a->opserv_level != hi_b->opserv_level)
3988 return hi_b->opserv_level - hi_a->opserv_level;
3989 return irccasecmp(hi_a->handle, hi_b->handle);
3993 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3995 struct handle_info_list hil;
3996 struct helpfile_table tbl;
4001 memset(&hil, 0, sizeof(hil));
4002 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
4003 struct handle_info *hi = iter_data(it);
4004 if (hi->opserv_level)
4005 handle_info_list_append(&hil, hi);
4007 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
4008 tbl.length = hil.used + 1;
4010 tbl.flags = TABLE_NO_FREE;
4011 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4012 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
4015 for (ii = 0; ii < hil.used; ) {
4016 ary = malloc(tbl.width * sizeof(ary[0]));
4017 ary[0] = hil.list[ii]->handle;
4018 ary[1] = strtab(hil.list[ii]->opserv_level);
4019 tbl.contents[++ii] = ary;
4021 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4022 reply("MSG_MATCH_COUNT", hil.used);
4023 for (ii = 0; ii < hil.used; ii++)
4024 free(tbl.contents[ii]);
4029 static NICKSERV_FUNC(cmd_search)
4031 struct nickserv_discrim *discrim;
4032 discrim_search_func action;
4033 struct svccmd *subcmd;
4034 unsigned int matches;
4037 NICKSERV_MIN_PARMS(3);
4038 sprintf(buf, "search %s", argv[1]);
4039 subcmd = dict_find(nickserv_service->commands, buf, NULL);
4040 if (!irccasecmp(argv[1], "print"))
4041 action = search_print_func;
4042 else if (!irccasecmp(argv[1], "count"))
4043 action = search_count_func;
4044 else if (!irccasecmp(argv[1], "unregister"))
4045 action = search_unregister_func;
4047 reply("NSMSG_INVALID_ACTION", argv[1]);
4051 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
4054 discrim = nickserv_discrim_create(user, argc-2, argv+2);
4058 if (action == search_print_func)
4059 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
4060 else if (action == search_count_func)
4061 discrim->limit = INT_MAX;
4063 matches = nickserv_discrim_search(discrim, action, user);
4065 if(discrim->show_fields) {
4068 for(ii = 0; ii < NICKSERV_DISCRIM_FIELD_COUNT; ii++) {
4069 if(discrim->show_fields & (1 << ii)) width++;
4071 discrim->output_table = malloc(sizeof(discrim->output_table[0]));
4072 discrim->output_table->length = matches+1;
4073 discrim->output_table->width = width;
4074 discrim->output_table->flags = TABLE_NO_FREE;
4075 discrim->output_table->contents = malloc(discrim->output_table->length * sizeof(discrim->output_table->contents[0]));
4076 discrim->output_table->contents[0] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
4079 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
4080 discrim->output_table->contents[0][ii++] = "Auth";
4081 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
4082 discrim->output_table->contents[0][ii++] = "EMail";
4083 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN)
4084 discrim->output_table->contents[0][ii++] = "Seen";
4085 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
4086 discrim->output_table->contents[0][ii++] = "Access";
4087 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
4088 discrim->output_table->contents[0][ii++] = "Fakehost";
4089 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
4090 discrim->output_table->contents[0][ii++] = "Website";
4091 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
4092 discrim->output_table->contents[0][ii++] = "DevNull";
4094 nickserv_discrim_search(discrim, action, user);
4096 table_send(nickserv, user->nick, 0, NULL, *discrim->output_table);
4098 for(ii = 1; ii < discrim->output_table->length; ++ii) {
4100 for(ij = 0; ij < NICKSERV_DISCRIM_FIELD_COUNT; ij++) {
4101 if(discrim->output_table_free_fields & (1 << ij))
4102 free((char*)discrim->output_table->contents[ii][ij]);
4104 free(discrim->output_table->contents[ii]);
4106 free(discrim->output_table->contents[0]);
4107 free(discrim->output_table->contents);
4108 free(discrim->output_table);
4111 reply("MSG_MATCH_COUNT", matches);
4113 reply("MSG_NO_MATCHES");
4120 static MODCMD_FUNC(cmd_checkpass)
4122 struct handle_info *hi;
4124 NICKSERV_MIN_PARMS(3);
4125 if (!(hi = get_handle_info(argv[1]))) {
4126 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4129 if (checkpass(argv[2], hi->passwd))
4130 reply("CHECKPASS_YES");
4132 reply("CHECKPASS_NO");
4137 static MODCMD_FUNC(cmd_checkemail)
4139 struct handle_info *hi;
4141 NICKSERV_MIN_PARMS(3);
4142 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
4145 if (!hi->email_addr)
4146 reply("CHECKEMAIL_NOT_SET");
4147 else if (!irccasecmp(argv[2], hi->email_addr))
4148 reply("CHECKEMAIL_YES");
4150 reply("CHECKEMAIL_NO");
4155 nickserv_db_read_authlog(UNUSED_ARG(const char *key), void *data, void *extra)
4157 struct record_data *rd = data;
4158 struct handle_info *hi = extra;
4160 struct authlogEntry *authlog;
4161 authlog = malloc(sizeof(*authlog));
4163 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGIN_TIME, RECDB_QSTRING);
4164 authlog->login_time = str ? strtoul(str, NULL, 0) : 0;
4166 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGOUT_TIME, RECDB_QSTRING);
4167 authlog->logout_time = str ? strtoul(str, NULL, 0) : 0;
4169 str = database_get_data(rd->d.object, KEY_AUTHLOG_HOSTMASK, RECDB_QSTRING);
4170 authlog->hostmask = str ? strdup(str) : NULL;
4172 str = database_get_data(rd->d.object, KEY_AUTHLOG_QUIT_REASON, RECDB_QSTRING);
4173 authlog->quit_reason = str ? strdup(str) : NULL;
4175 authlog->next = NULL;
4177 //append it to the end of the list...
4178 struct authlogEntry *authlog_entry;
4180 hi->authlog = authlog;
4182 for(authlog_entry = hi->authlog; authlog_entry; authlog_entry = authlog_entry->next) {
4183 if(!authlog_entry->next) {
4184 authlog_entry->next = authlog;
4193 nickserv_db_read_handle(const char *handle, dict_t obj)
4196 struct string_list *masks, *slist;
4197 struct handle_info *hi;
4198 struct userNode *authed_users;
4199 struct userData *channel_list;
4204 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
4205 id = str ? strtoul(str, NULL, 0) : 0;
4206 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
4208 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
4211 if ((hi = get_handle_info(handle))) {
4212 authed_users = hi->users;
4213 channel_list = hi->channels;
4215 hi->channels = NULL;
4216 dict_remove(nickserv_handle_dict, hi->handle);
4218 authed_users = NULL;
4219 channel_list = NULL;
4221 hi = register_handle(handle, str, id);
4223 hi->users = authed_users;
4224 while (authed_users) {
4225 authed_users->handle_info = hi;
4226 authed_users = authed_users->next_authed;
4229 hi->channels = channel_list;
4230 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
4231 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
4232 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
4233 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
4234 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
4235 hi->language = language_find(str ? str : "C");
4236 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
4237 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
4238 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
4240 hi->infoline = strdup(str);
4241 str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING);
4243 hi->website = strdup(str);
4244 str = database_get_data(obj, KEY_DEVNULL, RECDB_QSTRING);
4246 hi->devnull = strdup(str);
4247 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
4248 hi->registered = str ? strtoul(str, NULL, 0) : now;
4249 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
4250 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
4251 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
4252 hi->karma = str ? strtoul(str, NULL, 0) : 0;
4253 /* We want to read the nicks even if disable_nicks is set. This is so
4254 * that we don't lose the nick data entirely. */
4255 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
4257 for (ii=0; ii<slist->used; ii++)
4258 register_nick(slist->list[ii], hi);
4260 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
4262 for (ii=0; str[ii]; ii++)
4263 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
4265 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
4266 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
4267 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
4268 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
4269 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
4270 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
4271 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
4273 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
4275 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
4276 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
4278 nickserv_set_email_addr(hi, str);
4279 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
4281 hi->epithet = strdup(str);
4282 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
4284 hi->fakehost = strdup(str);
4285 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
4287 hi->fakeident = strdup(str);
4288 /* Read the "cookie" sub-database (if it exists). */
4289 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
4291 const char *data, *type, *expires, *cookie_str;
4292 struct handle_cookie *cookie;
4294 cookie = calloc(1, sizeof(*cookie));
4295 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
4296 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
4297 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
4298 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
4299 if (!type || !expires || !cookie_str) {
4300 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
4303 if (!irccasecmp(type, KEY_ACTIVATION))
4304 cookie->type = ACTIVATION;
4305 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
4306 cookie->type = PASSWORD_CHANGE;
4307 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
4308 cookie->type = EMAIL_CHANGE;
4309 else if (!irccasecmp(type, KEY_ALLOWAUTH))
4310 cookie->type = ALLOWAUTH;
4312 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
4315 cookie->expires = strtoul(expires, NULL, 0);
4316 if (cookie->expires < now)
4319 cookie->data = strdup(data);
4320 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
4324 nickserv_bake_cookie(cookie);
4326 nickserv_free_cookie(cookie);
4328 /* Read the "notes" sub-database (if it exists). */
4329 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
4332 struct handle_note *last_note;
4333 struct handle_note *note;
4336 for (it = dict_first(subdb); it; it = iter_next(it)) {
4337 const char *expires;
4341 const char *note_id;
4344 note_id = iter_key(it);
4345 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
4347 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
4350 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
4351 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
4352 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
4353 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
4354 if (!setter || !text || !set) {
4355 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
4358 note = calloc(1, sizeof(*note) + strlen(text));
4360 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
4361 note->set = strtoul(set, NULL, 10);
4362 note->id = strtoul(note_id, NULL, 10);
4363 safestrncpy(note->setter, setter, sizeof(note->setter));
4364 strcpy(note->note, text);
4366 last_note->next = note;
4372 if ((subdb = database_get_data(obj, KEY_AUTHLOG, RECDB_OBJECT)))
4373 dict_foreach(subdb, nickserv_db_read_authlog, hi);
4377 nickserv_saxdb_read(dict_t db) {
4379 struct record_data *rd;
4381 for (it=dict_first(db); it; it=iter_next(it)) {
4383 nickserv_db_read_handle(iter_key(it), rd->d.object);
4388 static NICKSERV_FUNC(cmd_mergedb)
4390 struct timeval start, stop;
4393 NICKSERV_MIN_PARMS(2);
4394 gettimeofday(&start, NULL);
4395 if (!(db = parse_database(argv[1]))) {
4396 reply("NSMSG_DB_UNREADABLE", argv[1]);
4399 nickserv_saxdb_read(db);
4401 gettimeofday(&stop, NULL);
4402 stop.tv_sec -= start.tv_sec;
4403 stop.tv_usec -= start.tv_usec;
4404 if (stop.tv_usec < 0) {
4406 stop.tv_usec += 1000000;
4408 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
4413 expire_handles(UNUSED_ARG(void *data))
4415 dict_iterator_t it, next;
4416 unsigned long expiry;
4417 struct handle_info *hi;
4419 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4420 next = iter_next(it);
4422 if ((hi->opserv_level > 0)
4424 || HANDLE_FLAGGED(hi, FROZEN)
4425 || HANDLE_FLAGGED(hi, NODELETE)) {
4428 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4429 if ((now - hi->lastseen) > expiry) {
4430 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
4431 nickserv_unregister_handle(hi, NULL);
4435 if (nickserv_conf.handle_expire_frequency)
4436 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4440 nickserv_load_dict(const char *fname)
4444 if (!(file = fopen(fname, "r"))) {
4445 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4448 while (fgets(line, sizeof(line), file)) {
4451 if (line[strlen(line)-1] == '\n')
4452 line[strlen(line)-1] = 0;
4453 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4456 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4459 static enum reclaim_action
4460 reclaim_action_from_string(const char *str) {
4462 return RECLAIM_NONE;
4463 else if (!irccasecmp(str, "warn"))
4464 return RECLAIM_WARN;
4465 else if (!irccasecmp(str, "svsnick"))
4466 return RECLAIM_SVSNICK;
4467 else if (!irccasecmp(str, "kill"))
4468 return RECLAIM_KILL;
4470 return RECLAIM_NONE;
4474 nickserv_conf_read(void)
4476 dict_t conf_node, child;
4480 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4481 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4484 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4486 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4487 if (nickserv_conf.valid_handle_regex_set)
4488 regfree(&nickserv_conf.valid_handle_regex);
4490 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4491 nickserv_conf.valid_handle_regex_set = !err;
4492 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4494 nickserv_conf.valid_handle_regex_set = 0;
4496 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4497 if (nickserv_conf.valid_nick_regex_set)
4498 regfree(&nickserv_conf.valid_nick_regex);
4500 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4501 nickserv_conf.valid_nick_regex_set = !err;
4502 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4504 nickserv_conf.valid_nick_regex_set = 0;
4506 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4508 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4509 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4510 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4511 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4512 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4513 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4514 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4515 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4516 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4517 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4518 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4519 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4520 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4521 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4522 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4523 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4524 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4525 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4526 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4527 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4528 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4529 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4530 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4531 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4532 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
4533 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
4534 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4536 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4537 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4538 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4540 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4541 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4542 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4544 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4545 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4546 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4547 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4548 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4549 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4550 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4551 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4552 str = database_get_data(conf_node, KEY_MAX_AUTHLOG_LEN, RECDB_QSTRING);
4553 nickserv_conf.max_authlog_len = str ? strtoul(str, NULL, 0) : 30;
4554 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4555 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4556 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4559 nickserv_conf.ounregister_flags = 0;
4561 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4564 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4566 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4568 nickserv_conf.handle_ts_mode = TS_IGNORE;
4569 else if (!irccasecmp(str, "ircu"))
4570 nickserv_conf.handle_ts_mode = TS_IRCU;
4572 nickserv_conf.handle_ts_mode = TS_IGNORE;
4573 if (!nickserv_conf.disable_nicks) {
4574 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4575 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4576 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4577 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4578 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4579 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4580 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4581 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4583 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4584 for (it=dict_first(child); it; it=iter_next(it)) {
4585 const char *key = iter_key(it), *value;
4589 if (!strncasecmp(key, "uc_", 3))
4590 flag = toupper(key[3]);
4591 else if (!strncasecmp(key, "lc_", 3))
4592 flag = tolower(key[3]);
4596 if ((pos = handle_inverse_flags[flag])) {
4597 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4598 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4601 if (nickserv_conf.weak_password_dict)
4602 dict_delete(nickserv_conf.weak_password_dict);
4603 nickserv_conf.weak_password_dict = dict_new();
4604 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4605 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4606 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4607 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4609 nickserv_load_dict(str);
4610 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4611 if (nickserv && str)
4612 NickChange(nickserv, str, 0);
4613 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4614 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4615 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4616 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4617 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4618 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4619 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4620 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4621 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4622 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4623 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4624 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4625 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4626 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4627 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4628 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4629 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4630 titlehost_suffix = str ? str : "example.net";
4631 str = conf_get_data("server/network", RECDB_QSTRING);
4632 nickserv_conf.network_name = str ? str : "some IRC network";
4633 if (!nickserv_conf.auth_policer_params) {
4634 nickserv_conf.auth_policer_params = policer_params_new();
4635 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4636 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4638 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4639 for (it=dict_first(child); it; it=iter_next(it))
4640 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4644 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4646 char newnick[NICKLEN+1];
4655 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4657 case RECLAIM_SVSNICK:
4659 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4660 } while (GetUserH(newnick));
4661 irc_svsnick(nickserv, user, newnick);
4664 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4665 DelUser(user, nickserv, 1, msg);
4671 nickserv_reclaim_p(void *data) {
4672 struct userNode *user = data;
4673 struct nick_info *ni = get_nick_info(user->nick);
4675 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4679 check_user_nick(struct userNode *user) {
4680 //check if this user is a pending LOC user
4681 if(pendingLOCUsers) {
4682 struct pendingLOCUser *pending, *next, *prev = NULL;
4683 for(pending = pendingLOCUsers; pending; pending = next) {
4684 next = pending->next;
4685 if(user->handle_info == pending->handle_info) {
4686 pending->authlog->user = user;
4690 pendingLOCUsers = next;
4693 if(now - pending->time > 10) {
4697 pendingLOCUsers = next;
4702 struct nick_info *ni;
4703 user->modes &= ~FLAGS_REGNICK;
4704 if (!(ni = get_nick_info(user->nick)))
4706 if (user->handle_info == ni->owner) {
4707 user->modes |= FLAGS_REGNICK;
4711 if (nickserv_conf.warn_nick_owned)
4712 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4713 if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE)
4715 if (nickserv_conf.auto_reclaim_delay)
4716 timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user);
4718 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4722 handle_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
4724 struct handle_info *hi = NULL;
4727 hi = dict_find(nickserv_handle_dict, stamp, NULL);
4728 if ((hi == NULL) && (serial != 0)) {
4730 inttobase64(id, serial, IDLEN);
4731 hi = dict_find(nickserv_id_dict, id, NULL);
4735 if ((nickserv_conf.handle_ts_mode == TS_IRCU)
4736 && (timestamp != hi->registered)) {
4739 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
4742 set_user_handle_info(user, hi, 0);
4744 log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s:%lu:%lu.", user->nick, stamp, timestamp, serial);
4749 handle_nick_change(struct userNode *user, const char *old_nick)
4751 struct handle_info *hi;
4753 if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) {
4754 dict_remove(nickserv_allow_auth_dict, old_nick);
4755 dict_insert(nickserv_allow_auth_dict, user->nick, hi);
4757 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4758 check_user_nick(user);
4762 nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), const char *why)
4764 if(user->handle_info) {
4765 //check if theres an open authlog entry
4766 struct authlogEntry *authlog;
4767 for(authlog = user->handle_info->authlog; authlog; authlog = authlog->next) {
4768 if(authlog->user == user) {
4769 authlog->user = NULL;
4770 authlog->logout_time = now;
4771 authlog->quit_reason = strdup(why);
4776 dict_remove(nickserv_allow_auth_dict, user->nick);
4777 timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN);
4778 set_user_handle_info(user, NULL, 0);
4781 static struct modcmd *
4782 nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified)
4784 if (min_level > 0) {
4786 sprintf(buf, "%u", min_level);
4787 if (must_be_qualified) {
4788 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL);
4790 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL);
4792 } else if (min_level == 0) {
4793 if (must_be_qualified) {
4794 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4796 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL);
4799 if (must_be_qualified) {
4800 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL);
4802 return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL);
4808 nickserv_db_cleanup(void)
4810 unreg_del_user_func(nickserv_remove_user);
4811 userList_clean(&curr_helpers);
4812 policer_params_delete(nickserv_conf.auth_policer_params);
4813 dict_delete(nickserv_handle_dict);
4814 dict_delete(nickserv_nick_dict);
4815 dict_delete(nickserv_opt_dict);
4816 dict_delete(nickserv_allow_auth_dict);
4817 dict_delete(nickserv_email_dict);
4818 dict_delete(nickserv_id_dict);
4819 dict_delete(nickserv_conf.weak_password_dict);
4820 free(auth_func_list);
4821 free(unreg_func_list);
4823 free(allowauth_func_list);
4824 free(handle_merge_func_list);
4825 free(failpw_func_list);
4826 if (nickserv_conf.valid_handle_regex_set)
4827 regfree(&nickserv_conf.valid_handle_regex);
4828 if (nickserv_conf.valid_nick_regex_set)
4829 regfree(&nickserv_conf.valid_nick_regex);
4830 struct pendingLOCUser *pending, *next;
4831 for(pending = pendingLOCUsers; pending; pending = next) {
4832 next = pending->next;
4835 pendingLOCUsers = NULL;
4839 init_nickserv(const char *nick)
4842 NS_LOG = log_register_type("NickServ", "file:nickserv.log");
4843 reg_new_user_func(check_user_nick);
4844 reg_nick_change_func(handle_nick_change);
4845 reg_del_user_func(nickserv_remove_user);
4846 reg_account_func(handle_account);
4848 /* set up handle_inverse_flags */
4849 memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
4850 for (i=0; handle_flags[i]; i++) {
4851 handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1;
4852 flag_access_levels[i] = 0;
4855 conf_register_reload(nickserv_conf_read);
4856 nickserv_opt_dict = dict_new();
4857 nickserv_email_dict = dict_new();
4858 dict_set_free_keys(nickserv_email_dict, free);
4859 dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr);
4861 nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL);
4862 modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+qualified,+loghostmask", NULL);
4863 nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0);
4864 nickserv_define_func("REGISTER", cmd_register, -1, 0, 1);
4865 nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0);
4866 nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 1);
4867 nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0);
4868 nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0);
4869 nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0);
4870 nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0);
4871 nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0);
4872 nickserv_define_func("PASS", cmd_pass, -1, 1, 1);
4873 nickserv_define_func("SET", cmd_set, -1, 1, 0);
4874 nickserv_define_func("OSET", cmd_oset, 0, 1, 0);
4875 nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0);
4876 nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0);
4877 nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0);
4878 nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0);
4879 nickserv_define_func("MERGE", cmd_merge, 750, 1, 0);
4880 nickserv_define_func("ADDNOTE", cmd_addnote, 0, 1, 0);
4881 nickserv_define_func("DELNOTE", cmd_delnote, 0, 1, 0);
4882 nickserv_define_func("NOTES", cmd_notes, 0, 1, 0);
4883 if (!nickserv_conf.disable_nicks) {
4884 /* nick management commands */
4885 nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0);
4886 nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0);
4887 nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0);
4888 nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0);
4889 nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0);
4890 nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0);
4892 if (nickserv_conf.email_enabled) {
4893 nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0);
4894 nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 1);
4895 nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 1);
4896 nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0);
4897 nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0);
4898 dict_insert(nickserv_opt_dict, "EMAIL", opt_email);
4900 nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0);
4901 /* miscellaneous commands */
4902 nickserv_define_func("STATUS", cmd_status, -1, 0, 0);
4903 nickserv_define_func("SEARCH", cmd_search, 100, 1, 0);
4904 nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0);
4905 nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0);
4906 nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0);
4907 nickserv_define_func("CHECKEMAIL", cmd_checkemail, 0, 1, 0);
4908 nickserv_define_func("AUTHLOG", cmd_authlog, 0, 1, 0);
4910 dict_insert(nickserv_opt_dict, "INFO", opt_info);
4911 dict_insert(nickserv_opt_dict, "WIDTH", opt_width);
4912 dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth);
4913 dict_insert(nickserv_opt_dict, "COLOR", opt_color);
4914 dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg);
4915 dict_insert(nickserv_opt_dict, "STYLE", opt_style);
4916 dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide);
4917 dict_insert(nickserv_opt_dict, "PASS", opt_password);
4918 dict_insert(nickserv_opt_dict, "PASSWORD", opt_password);
4919 dict_insert(nickserv_opt_dict, "FLAGS", opt_flags);
4920 dict_insert(nickserv_opt_dict, "WEBSITE", opt_website);
4921 dict_insert(nickserv_opt_dict, "DEVNULL", opt_devnull);
4922 dict_insert(nickserv_opt_dict, "ACCESS", opt_level);
4923 dict_insert(nickserv_opt_dict, "LEVEL", opt_level);
4924 dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet);
4925 if (titlehost_suffix) {
4926 dict_insert(nickserv_opt_dict, "TITLE", opt_title);
4927 dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost);
4928 dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident);
4930 dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins);
4931 dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language);
4932 dict_insert(nickserv_opt_dict, "KARMA", opt_karma);
4933 nickserv_define_func("OSET KARMA", NULL, 0, 1, 0);
4935 nickserv_handle_dict = dict_new();
4936 dict_set_free_keys(nickserv_handle_dict, free);
4937 dict_set_free_data(nickserv_handle_dict, free_handle_info);
4939 nickserv_id_dict = dict_new();
4940 dict_set_free_keys(nickserv_id_dict, free);
4942 nickserv_nick_dict = dict_new();
4943 dict_set_free_data(nickserv_nick_dict, free);
4945 nickserv_allow_auth_dict = dict_new();
4947 userList_init(&curr_helpers);
4950 const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING);
4951 nickserv = AddLocalUser(nick, nick, NULL, "Nick Services", modes);
4952 nickserv_service = service_register(nickserv);
4954 saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write);
4955 reg_exit_func(nickserv_db_cleanup);
4956 if(nickserv_conf.handle_expire_frequency)
4957 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4958 message_register_table(msgtab);