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 char numeric[COMBO_NUMERIC_LEN+1];
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 for(ii = 0; ii < COMBO_NUMERIC_LEN; ii++)
1978 pending->numeric[ii] = numeric[ii];
1979 pending->numeric[COMBO_NUMERIC_LEN] = '\0';
1980 pending->time = now;
1981 pending->authlog = authlog;
1982 pending->next = pendingLOCUsers;
1983 pendingLOCUsers = pending;
1988 char *getfakehost(const char *user)
1990 struct handle_info *hi;
1991 hi = dict_find(nickserv_handle_dict, user, NULL);
1994 return generate_fakehost(hi);
1997 static allowauth_func_t *allowauth_func_list;
1998 static unsigned int allowauth_func_size = 0, allowauth_func_used = 0;
2001 reg_allowauth_func(allowauth_func_t func)
2003 if (allowauth_func_used == allowauth_func_size) {
2004 if (allowauth_func_size) {
2005 allowauth_func_size <<= 1;
2006 allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t));
2008 allowauth_func_size = 8;
2009 allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t));
2012 allowauth_func_list[allowauth_func_used++] = func;
2015 static MODCMD_FUNC(cmd_authlog)
2017 struct handle_info *hi = user->handle_info;
2018 struct helpfile_table tbl;
2019 struct authlogEntry *authlog;
2022 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
2029 tbl.flags = TABLE_NO_FREE;
2030 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
2031 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
2032 tbl.contents[0][0] = "Hostmask";
2033 tbl.contents[0][1] = "Login";
2034 tbl.contents[0][2] = "Logout";
2035 tbl.contents[0][3] = "Quit Reason";
2038 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
2040 free(tbl.contents[0]);
2046 char intervalBuf[INTERVALLEN];
2048 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
2049 tbl.contents[++i] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
2050 tbl.contents[i][0] = authlog->hostmask;
2051 str = intervalString(intervalBuf, now - authlog->login_time, hi);
2052 ptr = malloc(strlen(str)+1);
2054 tbl.contents[i][1] = ptr;
2055 if(authlog->logout_time)
2056 str = intervalString(intervalBuf, now - authlog->logout_time, hi);
2058 str = (authlog->user ? "Never" : "Unknown");
2059 ptr = malloc(strlen(str)+1);
2061 tbl.contents[i][2] = ptr;
2062 tbl.contents[i][3] = (authlog->quit_reason ? authlog->quit_reason : "-");
2065 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
2066 for(i = 1; i < tbl.length; ++i)
2068 free((char *) tbl.contents[i][1]);
2069 free((char *) tbl.contents[i][2]);
2070 free(tbl.contents[i]);
2072 free(tbl.contents[0]);
2078 static NICKSERV_FUNC(cmd_allowauth)
2080 struct userNode *target;
2081 struct handle_info *hi;
2084 NICKSERV_MIN_PARMS(2);
2085 if (!(target = GetUserH(argv[1]))) {
2086 reply("MSG_NICK_UNKNOWN", argv[1]);
2089 if (target->handle_info) {
2090 reply("NSMSG_USER_PREV_AUTH", target->nick);
2093 if (IsStamped(target)) {
2094 /* Unauthenticated users might still have been stamped
2095 previously and could therefore have a hidden host;
2096 do not allow them to authenticate to an account. */
2097 reply("NSMSG_USER_PREV_STAMP", target->nick);
2102 else if (!(hi = get_handle_info(argv[2]))) {
2103 reply("MSG_HANDLE_UNKNOWN", argv[2]);
2107 if (hi->opserv_level > user->handle_info->opserv_level) {
2108 reply("MSG_USER_OUTRANKED", hi->handle);
2111 if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER))
2112 || (hi->opserv_level > 0))
2113 && ((argc < 4) || irccasecmp(argv[3], "staff"))) {
2114 reply("NSMSG_ALLOWAUTH_STAFF", hi->handle);
2117 dict_insert(nickserv_allow_auth_dict, target->nick, hi);
2118 reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle);
2119 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle);
2120 if (nickserv_conf.email_enabled)
2121 send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL");
2123 if (dict_remove(nickserv_allow_auth_dict, target->nick))
2124 reply("NSMSG_AUTH_NORMAL_ONLY", target->nick);
2126 reply("NSMSG_AUTH_UNSPECIAL", target->nick);
2128 for (n=0; n<allowauth_func_used; n++)
2129 allowauth_func_list[n](user, target, hi);
2133 static NICKSERV_FUNC(cmd_authcookie)
2135 struct handle_info *hi;
2137 NICKSERV_MIN_PARMS(2);
2138 if (user->handle_info) {
2139 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2142 if (IsStamped(user)) {
2143 /* Unauthenticated users might still have been stamped
2144 previously and could therefore have a hidden host;
2145 do not allow them to authenticate to an account. */
2146 reply("NSMSG_STAMPED_AUTHCOOKIE");
2149 if (!(hi = get_handle_info(argv[1]))) {
2150 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2153 if (!hi->email_addr) {
2154 reply("MSG_SET_EMAIL_ADDR");
2157 nickserv_make_cookie(user, hi, ALLOWAUTH, NULL);
2161 static NICKSERV_FUNC(cmd_delcookie)
2163 struct handle_info *hi;
2165 hi = user->handle_info;
2167 reply("NSMSG_NO_COOKIE");
2170 switch (hi->cookie->type) {
2173 reply("NSMSG_MUST_TIME_OUT");
2176 nickserv_eat_cookie(hi->cookie);
2177 reply("NSMSG_ATE_COOKIE");
2183 static NICKSERV_FUNC(cmd_odelcookie)
2185 struct handle_info *hi;
2187 NICKSERV_MIN_PARMS(2);
2189 if (!(hi = get_victim_oper(user, argv[1])))
2193 reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle);
2197 nickserv_eat_cookie(hi->cookie);
2198 reply("NSMSG_ATE_COOKIE_FOREIGN", hi->handle);
2203 static NICKSERV_FUNC(cmd_resetpass)
2205 struct handle_info *hi;
2206 char crypted[MD5_CRYPT_LENGTH];
2208 NICKSERV_MIN_PARMS(3);
2209 if (user->handle_info) {
2210 reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle);
2213 if (IsStamped(user)) {
2214 /* Unauthenticated users might still have been stamped
2215 previously and could therefore have a hidden host;
2216 do not allow them to activate an account. */
2217 reply("NSMSG_STAMPED_RESETPASS");
2220 if (!(hi = get_handle_info(argv[1]))) {
2221 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2224 if (!hi->email_addr) {
2225 reply("MSG_SET_EMAIL_ADDR");
2228 cryptpass(argv[2], crypted);
2230 nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted);
2234 static NICKSERV_FUNC(cmd_cookie)
2236 struct handle_info *hi;
2239 if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) {
2242 NICKSERV_MIN_PARMS(3);
2243 if (!(hi = get_handle_info(argv[1]))) {
2244 reply("MSG_HANDLE_UNKNOWN", argv[1]);
2250 if (HANDLE_FLAGGED(hi, SUSPENDED)) {
2251 reply("NSMSG_HANDLE_SUSPENDED");
2256 reply("NSMSG_NO_COOKIE");
2260 /* Check validity of operation before comparing cookie to
2261 * prohibit guessing by authed users. */
2262 if (user->handle_info
2263 && (hi->cookie->type != EMAIL_CHANGE)
2264 && (hi->cookie->type != PASSWORD_CHANGE)) {
2265 reply("NSMSG_CANNOT_COOKIE");
2269 if (strcmp(cookie, hi->cookie->cookie)) {
2270 reply("NSMSG_BAD_COOKIE");
2274 switch (hi->cookie->type) {
2276 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2277 set_user_handle_info(user, hi, 1);
2278 reply("NSMSG_HANDLE_ACTIVATED");
2280 case PASSWORD_CHANGE:
2281 set_user_handle_info(user, hi, 1);
2282 safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd));
2283 reply("NSMSG_PASSWORD_CHANGED");
2286 nickserv_set_email_addr(hi, hi->cookie->data);
2287 reply("NSMSG_EMAIL_CHANGED");
2290 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2291 set_user_handle_info(user, hi, 1);
2292 nickserv_addmask(user, hi, mask);
2293 reply("NSMSG_AUTH_SUCCESS");
2298 reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type);
2299 log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle);
2303 nickserv_eat_cookie(hi->cookie);
2308 static NICKSERV_FUNC(cmd_oregnick) {
2310 struct handle_info *target;
2311 struct nick_info *ni;
2313 NICKSERV_MIN_PARMS(3);
2314 if (!(target = modcmd_get_handle_info(user, argv[1])))
2317 if (!is_registerable_nick(nick)) {
2318 reply("NSMSG_BAD_NICK", nick);
2321 ni = dict_find(nickserv_nick_dict, nick, NULL);
2323 reply("NSMSG_NICK_EXISTS", nick);
2326 register_nick(nick, target);
2327 reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle);
2331 static NICKSERV_FUNC(cmd_regnick) {
2333 struct nick_info *ni;
2335 if (!is_registerable_nick(user->nick)) {
2336 reply("NSMSG_BAD_NICK", user->nick);
2339 /* count their nicks, see if it's too many */
2340 for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ;
2341 if (n >= nickserv_conf.nicks_per_handle) {
2342 reply("NSMSG_TOO_MANY_NICKS");
2345 ni = dict_find(nickserv_nick_dict, user->nick, NULL);
2347 reply("NSMSG_NICK_EXISTS", user->nick);
2350 register_nick(user->nick, user->handle_info);
2351 reply("NSMSG_REGNICK_SUCCESS", user->nick);
2355 static NICKSERV_FUNC(cmd_pass)
2357 struct handle_info *hi;
2358 const char *old_pass, *new_pass;
2360 NICKSERV_MIN_PARMS(3);
2361 hi = user->handle_info;
2365 if (!is_secure_password(hi->handle, new_pass, user)) return 0;
2366 if (!checkpass(old_pass, hi->passwd)) {
2367 argv[1] = "BADPASS";
2368 reply("NSMSG_PASSWORD_INVALID");
2371 cryptpass(new_pass, hi->passwd);
2373 reply("NSMSG_PASS_SUCCESS");
2378 nickserv_addmask(struct userNode *user, struct handle_info *hi, const char *mask)
2381 char *new_mask = canonicalize_hostmask(strdup(mask));
2382 for (i=0; i<hi->masks->used; i++) {
2383 if (!irccasecmp(new_mask, hi->masks->list[i])) {
2384 send_message(user, nickserv, "NSMSG_ADDMASK_ALREADY", new_mask);
2389 string_list_append(hi->masks, new_mask);
2390 send_message(user, nickserv, "NSMSG_ADDMASK_SUCCESS", new_mask);
2394 static NICKSERV_FUNC(cmd_addmask)
2397 char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
2398 int res = nickserv_addmask(user, user->handle_info, mask);
2402 if (!is_gline(argv[1])) {
2403 reply("NSMSG_MASK_INVALID", argv[1]);
2406 return nickserv_addmask(user, user->handle_info, argv[1]);
2410 static NICKSERV_FUNC(cmd_oaddmask)
2412 struct handle_info *hi;
2414 NICKSERV_MIN_PARMS(3);
2415 if (!(hi = get_victim_oper(user, argv[1])))
2417 return nickserv_addmask(user, hi, argv[2]);
2421 nickserv_delmask(struct userNode *user, struct handle_info *hi, const char *del_mask, int force)
2424 for (i=0; i<hi->masks->used; i++) {
2425 if (!strcmp(del_mask, hi->masks->list[i])) {
2426 char *old_mask = hi->masks->list[i];
2427 if (hi->masks->used == 1 && !force) {
2428 send_message(user, nickserv, "NSMSG_DELMASK_NOTLAST");
2431 hi->masks->list[i] = hi->masks->list[--hi->masks->used];
2432 send_message(user, nickserv, "NSMSG_DELMASK_SUCCESS", old_mask);
2437 send_message(user, nickserv, "NSMSG_DELMASK_NOT_FOUND");
2441 static NICKSERV_FUNC(cmd_delmask)
2443 NICKSERV_MIN_PARMS(2);
2444 return nickserv_delmask(user, user->handle_info, argv[1], 0);
2447 static NICKSERV_FUNC(cmd_odelmask)
2449 struct handle_info *hi;
2450 NICKSERV_MIN_PARMS(3);
2451 if (!(hi = get_victim_oper(user, argv[1])))
2453 return nickserv_delmask(user, hi, argv[2], 1);
2457 nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) {
2458 unsigned int nn, add = 1, pos;
2459 unsigned long added, removed, flag;
2461 for (added=removed=nn=0; str[nn]; nn++) {
2463 case '+': add = 1; break;
2464 case '-': add = 0; break;
2466 if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) {
2467 send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]);
2470 if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) {
2471 /* cheesy avoidance of looking up the flag name.. */
2472 send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]);
2475 flag = 1 << (pos - 1);
2477 added |= flag, removed &= ~flag;
2479 removed |= flag, added &= ~flag;
2484 *premoved = removed;
2489 nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags)
2491 unsigned long before, after, added, removed;
2492 struct userNode *uNode;
2494 before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2495 if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed))
2497 hi->flags = (hi->flags | added) & ~removed;
2498 after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER);
2500 /* Strip helping flag if they're only a support helper and not
2501 * currently in #support. */
2502 if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) {
2503 struct channelList *schannels;
2505 schannels = chanserv_support_channels();
2506 for (ii = 0; ii < schannels->used; ++ii)
2507 if (find_handle_in_channel(schannels->list[ii], hi, NULL))
2509 if (ii == schannels->used)
2510 HANDLE_CLEAR_FLAG(hi, HELPING);
2513 if (after && !before) {
2514 /* Add user to current helper list. */
2515 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2516 userList_append(&curr_helpers, uNode);
2517 } else if (!after && before) {
2518 /* Remove user from current helper list. */
2519 for (uNode = hi->users; uNode; uNode = uNode->next_authed)
2520 userList_remove(&curr_helpers, uNode);
2527 set_list(struct userNode *user, struct handle_info *hi, int override)
2531 char *set_display[] = {
2532 "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE",
2533 "EMAIL", "MAXLOGINS", "LANGUAGE", "DEVNULL"
2536 send_message(user, nickserv, "NSMSG_SETTING_LIST");
2538 /* Do this so options are presented in a consistent order. */
2539 for (i = 0; i < ArrayLength(set_display); ++i)
2540 if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL)))
2541 opt(user, hi, override, 0, NULL);
2544 static NICKSERV_FUNC(cmd_set)
2546 struct handle_info *hi;
2549 hi = user->handle_info;
2551 set_list(user, hi, 0);
2554 if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) {
2555 reply("NSMSG_INVALID_OPTION", argv[1]);
2558 return opt(user, hi, 0, argc-1, argv+1);
2561 static NICKSERV_FUNC(cmd_oset)
2563 struct handle_info *hi;
2564 struct svccmd *subcmd;
2566 char cmdname[MAXLEN];
2568 NICKSERV_MIN_PARMS(2);
2570 if (!(hi = get_victim_oper(user, argv[1])))
2574 set_list(user, hi, 0);
2578 if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) {
2579 reply("NSMSG_INVALID_OPTION", argv[2]);
2583 sprintf(cmdname, "%s %s", cmd->name, argv[2]);
2584 subcmd = dict_find(cmd->parent->commands, cmdname, NULL);
2585 if (subcmd && !svccmd_can_invoke(user, cmd->parent->bot, subcmd, NULL, SVCCMD_NOISY))
2588 return opt(user, hi, 1, argc-2, argv+2);
2591 static OPTION_FUNC(opt_info)
2595 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2597 hi->infoline = NULL;
2599 hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL));
2603 info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE");
2604 send_message(user, nickserv, "NSMSG_SET_INFO", info);
2608 static OPTION_FUNC(opt_devnull)
2610 const char *devnull;
2614 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2617 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2621 devnull = unsplit_string(argv+1, argc-1, NULL);
2622 if(devnull_check(devnull) == 1) {
2625 hi->devnull = strdup(devnull);
2630 devnull = hi->devnull ? hi->devnull : user_find_message(user, "MSG_NONE");
2631 send_message(user, nickserv, "NSMSG_SET_DEVNULL", devnull);
2635 void nickserv_devnull_delete(char *name) {
2637 struct handle_info *hi;
2639 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2641 if (hi->devnull && !irccasecmp(name, hi->devnull)) {
2648 void nickserv_devnull_rename(char *oldname, char *newname) {
2650 struct handle_info *hi;
2652 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
2654 if (hi->devnull && !irccasecmp(oldname, hi->devnull)) {
2655 hi->devnull = strdup(newname);
2660 static OPTION_FUNC(opt_website)
2662 const char *website;
2665 if (!HANDLE_FLAGGED(user->handle_info, BOT)) {
2666 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2669 if ((argv[1][0] == '*') && (argv[1][1] == 0)) {
2673 website = unsplit_string(argv+1, argc-1, NULL);
2674 hi->website = strdup(website);
2677 if (HANDLE_FLAGGED(user->handle_info, BOT)) {
2678 website = hi->website ? hi->website : user_find_message(user, "MSG_NONE");
2679 send_message(user, nickserv, "NSMSG_SET_WEBSITE", website);
2684 static OPTION_FUNC(opt_width)
2687 hi->screen_width = strtoul(argv[1], NULL, 0);
2689 if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE))
2690 hi->screen_width = MIN_LINE_SIZE;
2691 else if (hi->screen_width > MAX_LINE_SIZE)
2692 hi->screen_width = MAX_LINE_SIZE;
2694 send_message(user, nickserv, "NSMSG_SET_WIDTH", hi->screen_width);
2698 static OPTION_FUNC(opt_tablewidth)
2701 hi->table_width = strtoul(argv[1], NULL, 0);
2703 if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE))
2704 hi->table_width = MIN_LINE_SIZE;
2705 else if (hi->screen_width > MAX_LINE_SIZE)
2706 hi->table_width = MAX_LINE_SIZE;
2708 send_message(user, nickserv, "NSMSG_SET_TABLEWIDTH", hi->table_width);
2712 static OPTION_FUNC(opt_color)
2715 if (enabled_string(argv[1]))
2716 HANDLE_SET_FLAG(hi, MIRC_COLOR);
2717 else if (disabled_string(argv[1]))
2718 HANDLE_CLEAR_FLAG(hi, MIRC_COLOR);
2720 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2725 send_message(user, nickserv, "NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF"));
2729 static OPTION_FUNC(opt_privmsg)
2732 if (enabled_string(argv[1]))
2733 HANDLE_SET_FLAG(hi, USE_PRIVMSG);
2734 else if (disabled_string(argv[1]))
2735 HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG);
2737 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2742 send_message(user, nickserv, "NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF"));
2746 static OPTION_FUNC(opt_autohide)
2749 if (enabled_string(argv[1]))
2750 HANDLE_SET_FLAG(hi, AUTOHIDE);
2751 else if (disabled_string(argv[1]))
2752 HANDLE_CLEAR_FLAG(hi, AUTOHIDE);
2754 send_message(user, nickserv, "MSG_INVALID_BINARY", argv[1]);
2759 send_message(user, nickserv, "NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF"));
2763 static OPTION_FUNC(opt_style)
2768 if (!irccasecmp(argv[1], "Zoot"))
2769 hi->userlist_style = HI_STYLE_ZOOT;
2770 else if (!irccasecmp(argv[1], "def"))
2771 hi->userlist_style = HI_STYLE_DEF;
2774 switch (hi->userlist_style) {
2783 send_message(user, nickserv, "NSMSG_SET_STYLE", style);
2787 static OPTION_FUNC(opt_password)
2790 send_message(user, nickserv, "NSMSG_USE_CMD_PASS");
2795 cryptpass(argv[1], hi->passwd);
2797 send_message(user, nickserv, "NSMSG_SET_PASSWORD", "***");
2801 static OPTION_FUNC(opt_flags)
2804 unsigned int ii, flen;
2807 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2812 nickserv_apply_flags(user, hi, argv[1]);
2814 for (ii = flen = 0; handle_flags[ii]; ii++)
2815 if (hi->flags & (1 << ii))
2816 flags[flen++] = handle_flags[ii];
2819 send_message(user, nickserv, "NSMSG_SET_FLAGS", flags);
2821 send_message(user, nickserv, "NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE"));
2825 static OPTION_FUNC(opt_email)
2829 if (!is_valid_email_addr(argv[1])) {
2830 send_message(user, nickserv, "NSMSG_BAD_EMAIL_ADDR");
2833 if ((str = mail_prohibited_address(argv[1]))) {
2834 send_message(user, nickserv, "NSMSG_EMAIL_PROHIBITED", argv[1], str);
2837 if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1]))
2838 send_message(user, nickserv, "NSMSG_EMAIL_SAME");
2840 nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1]);
2842 nickserv_set_email_addr(hi, argv[1]);
2844 nickserv_eat_cookie(hi->cookie);
2845 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2848 send_message(user, nickserv, "NSMSG_SET_EMAIL", visible_email_addr(user, hi));
2852 static OPTION_FUNC(opt_maxlogins)
2854 unsigned char maxlogins;
2856 maxlogins = strtoul(argv[1], NULL, 0);
2857 if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) {
2858 send_message(user, nickserv, "NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins);
2861 hi->maxlogins = maxlogins;
2863 maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins;
2864 send_message(user, nickserv, "NSMSG_SET_MAXLOGINS", maxlogins);
2868 static OPTION_FUNC(opt_language)
2870 struct language *lang;
2872 lang = language_find(argv[1]);
2873 if (irccasecmp(lang->name, argv[1]))
2874 send_message(user, nickserv, "NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name);
2875 hi->language = lang;
2877 send_message(user, nickserv, "NSMSG_SET_LANGUAGE", hi->language->name);
2881 static OPTION_FUNC(opt_karma)
2884 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2889 if (argv[1][0] == '+' && isdigit(argv[1][1])) {
2890 hi->karma += strtoul(argv[1] + 1, NULL, 10);
2891 } else if (argv[1][0] == '-' && isdigit(argv[1][1])) {
2892 hi->karma -= strtoul(argv[1] + 1, NULL, 10);
2894 send_message(user, nickserv, "NSMSG_INVALID_KARMA", argv[1]);
2898 send_message(user, nickserv, "NSMSG_SET_KARMA", hi->karma);
2903 oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) {
2904 if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0))
2906 if ((user->handle_info->opserv_level < target->opserv_level)
2907 || ((user->handle_info->opserv_level == target->opserv_level)
2908 && (user->handle_info->opserv_level < 1000))) {
2909 send_message(user, bot, "MSG_USER_OUTRANKED", target->handle);
2912 if ((user->handle_info->opserv_level < new_level)
2913 || ((user->handle_info->opserv_level == new_level)
2914 && (user->handle_info->opserv_level < 1000))) {
2915 send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD");
2918 if (user->handle_info == target) {
2919 send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE");
2922 if (target->opserv_level == new_level)
2924 log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).",
2925 user->handle_info->handle, target->handle, new_level, target->opserv_level);
2926 target->opserv_level = new_level;
2930 static OPTION_FUNC(opt_level)
2935 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2939 res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0;
2940 send_message(user, nickserv, "NSMSG_SET_LEVEL", hi->opserv_level);
2944 static OPTION_FUNC(opt_epithet)
2947 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2951 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) {
2952 char *epithet = unsplit_string(argv+1, argc-1, NULL);
2955 if ((epithet[0] == '*') && !epithet[1])
2958 hi->epithet = strdup(epithet);
2962 send_message(user, nickserv, "NSMSG_SET_EPITHET", hi->epithet);
2964 send_message(user, nickserv, "NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE"));
2968 static OPTION_FUNC(opt_title)
2973 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
2977 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) {
2979 if (strchr(title, '.')) {
2980 send_message(user, nickserv, "NSMSG_TITLE_INVALID");
2983 if ((strlen(user->handle_info->handle) + strlen(title) +
2984 strlen(titlehost_suffix) + 2) > HOSTLEN) {
2985 send_message(user, nickserv, "NSMSG_TITLE_TRUNCATED");
2990 if (!strcmp(title, "*")) {
2991 hi->fakehost = NULL;
2993 hi->fakehost = malloc(strlen(title)+2);
2994 hi->fakehost[0] = '.';
2995 strcpy(hi->fakehost+1, title);
2997 apply_fakehost(hi, NULL);
2998 } else if (hi->fakehost && (hi->fakehost[0] == '.'))
2999 title = hi->fakehost + 1;
3003 title = user_find_message(user, "MSG_NONE");
3004 send_message(user, nickserv, "NSMSG_SET_TITLE", title);
3008 static OPTION_FUNC(opt_fakehost)
3010 char mask[USERLEN + HOSTLEN + 2];
3014 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3018 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) {
3019 if(strlen(argv[1]) >= sizeof(mask)) {
3020 send_message(user, nickserv, "NSMSG_FAKEMASK_INVALID", USERLEN + HOSTLEN + 1);
3024 safestrncpy(mask, argv[1], sizeof(mask));
3026 if ((host = strrchr(mask, '@')) && host != mask) {
3027 /* If ident@host was used and the user doesn't have access to set idents, do not change anything. */
3028 if (!oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3040 if (ident && strlen(ident) > USERLEN) {
3041 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3045 if (host && ((strlen(host) > HOSTLEN) || (host[0] == '.'))) {
3046 send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN);
3050 if (host && host[0]) {
3052 if (!strcmp(host, "*"))
3053 hi->fakehost = NULL;
3055 hi->fakehost = strdup(host);
3056 host = hi->fakehost;
3059 host = generate_fakehost(hi);
3062 free(hi->fakeident);
3063 if (!strcmp(ident, "*"))
3064 hi->fakeident = NULL;
3066 hi->fakeident = strdup(ident);
3067 ident = hi->fakeident;
3070 ident = generate_fakeident(hi, NULL);
3072 apply_fakehost(hi, NULL);
3074 host = generate_fakehost(hi);
3075 ident = generate_fakeident(hi, NULL);
3078 host = (char *) user_find_message(user, "MSG_NONE");
3080 send_message(user, nickserv, "NSMSG_SET_FAKEIDENTHOST", ident, host);
3082 send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host);
3086 static OPTION_FUNC(opt_fakeident)
3091 send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]);
3095 if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) {
3097 if (strlen(ident) > USERLEN) {
3098 send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN);
3101 free(hi->fakeident);
3102 if (!strcmp(ident, "*"))
3103 hi->fakeident = NULL;
3105 hi->fakeident = strdup(ident);
3106 ident = hi->fakeident;
3107 apply_fakehost(hi, NULL);
3109 ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */
3111 ident = user_find_message(user, "MSG_NONE");
3112 send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident);
3116 static NICKSERV_FUNC(cmd_reclaim)
3118 struct nick_info *ni;
3119 struct userNode *victim;
3121 NICKSERV_MIN_PARMS(2);
3122 ni = dict_find(nickserv_nick_dict, argv[1], 0);
3124 reply("NSMSG_UNKNOWN_NICK", argv[1]);
3127 if (ni->owner != user->handle_info) {
3128 reply("NSMSG_NOT_YOUR_NICK", ni->nick);
3131 victim = GetUserH(ni->nick);
3133 reply("MSG_NICK_UNKNOWN", ni->nick);
3136 if (victim == user) {
3137 reply("NSMSG_NICK_USER_YOU");
3140 nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action);
3141 switch (nickserv_conf.reclaim_action) {
3142 case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break;
3143 case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break;
3144 case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break;
3145 case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break;
3150 static NICKSERV_FUNC(cmd_unregnick)
3153 struct handle_info *hi;
3154 struct nick_info *ni;
3156 hi = user->handle_info;
3157 nick = (argc < 2) ? user->nick : (const char*)argv[1];
3158 ni = dict_find(nickserv_nick_dict, nick, NULL);
3160 reply("NSMSG_UNKNOWN_NICK", nick);
3163 if (hi != ni->owner) {
3164 reply("NSMSG_NOT_YOUR_NICK", nick);
3167 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3172 static NICKSERV_FUNC(cmd_ounregnick)
3174 struct nick_info *ni;
3176 NICKSERV_MIN_PARMS(2);
3177 if (!(ni = get_nick_info(argv[1]))) {
3178 reply("NSMSG_NICK_NOT_REGISTERED", argv[1]);
3181 if (!oper_outranks(user, ni->owner))
3183 reply("NSMSG_UNREGNICK_SUCCESS", ni->nick);
3188 static NICKSERV_FUNC(cmd_unregister)
3190 struct handle_info *hi;
3193 NICKSERV_MIN_PARMS(2);
3194 hi = user->handle_info;
3197 if (checkpass(passwd, hi->passwd)) {
3198 nickserv_unregister_handle(hi, user);
3201 log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle);
3202 reply("NSMSG_PASSWORD_INVALID");
3207 static NICKSERV_FUNC(cmd_ounregister)
3209 struct handle_info *hi;
3210 char reason[MAXLEN];
3213 NICKSERV_MIN_PARMS(2);
3214 if (!(hi = get_victim_oper(user, argv[1])))
3217 if (HANDLE_FLAGGED(hi, NODELETE)) {
3218 reply("NSMSG_UNREGISTER_NODELETE", hi->handle);
3222 force = IsOper(user) && (argc > 2) && !irccasecmp(argv[2], "force");
3224 ((hi->flags & nickserv_conf.ounregister_flags)
3226 || (hi->last_quit_host[0] && ((unsigned)(now - hi->lastseen) < nickserv_conf.ounregister_inactive)))) {
3227 reply((IsOper(user) ? "NSMSG_UNREGISTER_MUST_FORCE" : "NSMSG_UNREGISTER_CANNOT_FORCE"), hi->handle);
3231 snprintf(reason, sizeof(reason), "%s unregistered account %s.", user->handle_info->handle, hi->handle);
3232 global_message(MESSAGE_RECIPIENT_STAFF, reason);
3233 nickserv_unregister_handle(hi, user);
3237 static NICKSERV_FUNC(cmd_status)
3239 if (nickserv_conf.disable_nicks) {
3240 reply("NSMSG_GLOBAL_STATS_NONICK",
3241 dict_size(nickserv_handle_dict));
3243 if (user->handle_info) {
3245 struct nick_info *ni;
3246 for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++;
3247 reply("NSMSG_HANDLE_STATS", cnt);
3249 reply("NSMSG_HANDLE_NONE");
3251 reply("NSMSG_GLOBAL_STATS",
3252 dict_size(nickserv_handle_dict),
3253 dict_size(nickserv_nick_dict));
3258 static NICKSERV_FUNC(cmd_ghost)
3260 struct userNode *target;
3261 char reason[MAXLEN];
3263 NICKSERV_MIN_PARMS(2);
3264 if (!(target = GetUserH(argv[1]))) {
3265 reply("MSG_NICK_UNKNOWN", argv[1]);
3268 if (target == user) {
3269 reply("NSMSG_CANNOT_GHOST_SELF");
3272 if (!target->handle_info || (target->handle_info != user->handle_info)) {
3273 reply("NSMSG_CANNOT_GHOST_USER", target->nick);
3276 snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick);
3277 DelUser(target, nickserv, 1, reason);
3278 reply("NSMSG_GHOST_KILLED", argv[1]);
3282 static NICKSERV_FUNC(cmd_vacation)
3284 HANDLE_SET_FLAG(user->handle_info, FROZEN);
3285 reply("NSMSG_ON_VACATION");
3289 static NICKSERV_FUNC(cmd_addnote)
3291 struct handle_info *hi;
3292 unsigned long duration;
3295 struct handle_note *prev;
3296 struct handle_note *note;
3298 /* Parse parameters and figure out values for note's fields. */
3299 NICKSERV_MIN_PARMS(4);
3300 hi = get_victim_oper(user, argv[1]);
3303 if(!strcmp(argv[2], "0"))
3305 else if(!(duration = ParseInterval(argv[2])))
3307 reply("MSG_INVALID_DURATION", argv[2]);
3310 if (duration > 2*365*86400) {
3311 reply("NSMSG_EXCESSIVE_DURATION", argv[2]);
3314 unsplit_string(argv + 3, argc - 3, text);
3315 WALK_NOTES(hi, prev, note) {}
3316 id = prev ? (prev->id + 1) : 1;
3318 /* Create the new note structure. */
3319 note = calloc(1, sizeof(*note) + strlen(text));
3321 note->expires = duration ? (now + duration) : 0;
3324 safestrncpy(note->setter, user->handle_info->handle, sizeof(note->setter));
3325 strcpy(note->note, text);
3330 reply("NSMSG_NOTE_ADDED", id, hi->handle);
3334 static NICKSERV_FUNC(cmd_delnote)
3336 struct handle_info *hi;
3337 struct handle_note *prev;
3338 struct handle_note *note;
3341 NICKSERV_MIN_PARMS(3);
3342 hi = get_victim_oper(user, argv[1]);
3345 id = strtoul(argv[2], NULL, 10);
3346 WALK_NOTES(hi, prev, note) {
3347 if (id == note->id) {
3349 prev->next = note->next;
3351 hi->notes = note->next;
3353 reply("NSMSG_NOTE_REMOVED", id, hi->handle);
3357 reply("NSMSG_NO_SUCH_NOTE", hi->handle, id);
3362 nickserv_saxdb_write(struct saxdb_context *ctx) {
3364 struct handle_info *hi;
3367 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
3369 assert(hi->id != 0);
3370 saxdb_start_record(ctx, iter_key(it), 0);
3372 struct handle_cookie *cookie = hi->cookie;
3375 switch (cookie->type) {
3376 case ACTIVATION: type = KEY_ACTIVATION; break;
3377 case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break;
3378 case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break;
3379 case ALLOWAUTH: type = KEY_ALLOWAUTH; break;
3380 default: type = NULL; break;
3383 saxdb_start_record(ctx, KEY_COOKIE, 0);
3384 saxdb_write_string(ctx, KEY_COOKIE_TYPE, type);
3385 saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires);
3387 saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data);
3388 saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie);
3389 saxdb_end_record(ctx);
3393 struct handle_note *prev, *note;
3394 saxdb_start_record(ctx, KEY_NOTES, 0);
3395 WALK_NOTES(hi, prev, note) {
3396 snprintf(flags, sizeof(flags), "%d", note->id);
3397 saxdb_start_record(ctx, flags, 0);
3399 saxdb_write_int(ctx, KEY_NOTE_EXPIRES, note->expires);
3400 saxdb_write_int(ctx, KEY_NOTE_SET, note->set);
3401 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
3402 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
3403 saxdb_end_record(ctx);
3405 saxdb_end_record(ctx);
3408 saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr);
3410 saxdb_write_string(ctx, KEY_EPITHET, hi->epithet);
3412 saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost);
3414 saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident);
3418 for (ii=flen=0; handle_flags[ii]; ++ii)
3419 if (hi->flags & (1 << ii))
3420 flags[flen++] = handle_flags[ii];
3422 saxdb_write_string(ctx, KEY_FLAGS, flags);
3424 saxdb_write_int(ctx, KEY_ID, hi->id);
3426 saxdb_write_string(ctx, KEY_INFO, hi->infoline);
3428 saxdb_write_string(ctx, KEY_DEVNULL, hi->devnull);
3430 saxdb_write_string(ctx, KEY_WEBSITE, hi->website);
3431 if (hi->last_quit_host[0])
3432 saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host);
3433 saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen);
3435 saxdb_write_sint(ctx, KEY_KARMA, hi->karma);
3436 if (hi->masks->used)
3437 saxdb_write_string_list(ctx, KEY_MASKS, hi->masks);
3439 saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins);
3441 struct string_list *slist;
3442 struct nick_info *ni;
3444 slist = alloc_string_list(nickserv_conf.nicks_per_handle);
3445 for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick);
3446 saxdb_write_string_list(ctx, KEY_NICKS, slist);
3450 if (hi->opserv_level)
3451 saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level);
3452 if (hi->language != lang_C)
3453 saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name);
3454 saxdb_write_string(ctx, KEY_PASSWD, hi->passwd);
3455 saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered);
3456 if (hi->screen_width)
3457 saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width);
3458 if (hi->table_width)
3459 saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width);
3460 flags[0] = hi->userlist_style;
3462 saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags);
3464 saxdb_start_record(ctx, KEY_AUTHLOG, 0);
3465 struct authlogEntry *authlog;
3467 for(authlog = hi->authlog; authlog; authlog = authlog->next) {
3468 saxdb_start_record(ctx, strtab(++i), 0);
3469 saxdb_write_int(ctx, KEY_AUTHLOG_LOGIN_TIME, authlog->login_time);
3470 saxdb_write_int(ctx, KEY_AUTHLOG_LOGOUT_TIME, authlog->logout_time);
3471 saxdb_write_string(ctx, KEY_AUTHLOG_HOSTMASK, authlog->hostmask);
3472 if(authlog->quit_reason)
3473 saxdb_write_string(ctx, KEY_AUTHLOG_QUIT_REASON, authlog->quit_reason);
3474 saxdb_end_record(ctx);
3476 saxdb_end_record(ctx); //END KEY_AUTHLOG
3478 saxdb_end_record(ctx);
3483 static handle_merge_func_t *handle_merge_func_list;
3484 static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0;
3487 reg_handle_merge_func(handle_merge_func_t func)
3489 if (handle_merge_func_used == handle_merge_func_size) {
3490 if (handle_merge_func_size) {
3491 handle_merge_func_size <<= 1;
3492 handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t));
3494 handle_merge_func_size = 8;
3495 handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t));
3498 handle_merge_func_list[handle_merge_func_used++] = func;
3501 static NICKSERV_FUNC(cmd_merge)
3503 struct handle_info *hi_from, *hi_to;
3504 struct userNode *last_user;
3505 struct userData *cList, *cListNext;
3506 unsigned int ii, jj, n;
3507 char buffer[MAXLEN];
3509 NICKSERV_MIN_PARMS(3);
3511 if (!(hi_from = get_victim_oper(user, argv[1])))
3513 if (!(hi_to = get_victim_oper(user, argv[2])))
3515 if (hi_to == hi_from) {
3516 reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle);
3520 for (n=0; n<handle_merge_func_used; n++)
3521 handle_merge_func_list[n](user, hi_to, hi_from);
3523 /* Append "from" handle's nicks to "to" handle's nick list. */
3525 struct nick_info *last_ni;
3526 for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ;
3527 last_ni->next = hi_from->nicks;
3529 while (hi_from->nicks) {
3530 hi_from->nicks->owner = hi_to;
3531 hi_from->nicks = hi_from->nicks->next;
3534 /* Merge the hostmasks. */
3535 for (ii=0; ii<hi_from->masks->used; ii++) {
3536 char *mask = hi_from->masks->list[ii];
3537 for (jj=0; jj<hi_to->masks->used; jj++)
3538 if (match_ircglobs(hi_to->masks->list[jj], mask))
3540 if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */
3541 string_list_append(hi_to->masks, strdup(mask));
3544 /* Merge the lists of authed users. */
3546 for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ;
3547 last_user->next_authed = hi_from->users;
3549 hi_to->users = hi_from->users;
3551 /* Repoint the old "from" handle's users. */
3552 for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) {
3553 last_user->handle_info = hi_to;
3555 hi_from->users = NULL;
3557 /* Merge channel userlists. */
3558 for (cList=hi_from->channels; cList; cList=cListNext) {
3559 struct userData *cList2;
3560 cListNext = cList->u_next;
3561 for (cList2=hi_to->channels; cList2; cList2=cList2->u_next)
3562 if (cList->channel == cList2->channel)
3564 if (cList2 && (cList2->access >= cList->access)) {
3565 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);
3566 /* keep cList2 in hi_to; remove cList from hi_from */
3567 del_channel_user(cList, 1);
3570 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);
3571 /* remove the lower-ranking cList2 from hi_to */
3572 del_channel_user(cList2, 1);
3574 log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name);
3576 /* cList needs to be moved from hi_from to hi_to */
3577 cList->handle = hi_to;
3578 /* Remove from linked list for hi_from */
3579 assert(!cList->u_prev);
3580 hi_from->channels = cList->u_next;
3582 cList->u_next->u_prev = cList->u_prev;
3583 /* Add to linked list for hi_to */
3584 cList->u_prev = NULL;
3585 cList->u_next = hi_to->channels;
3586 if (hi_to->channels)
3587 hi_to->channels->u_prev = cList;
3588 hi_to->channels = cList;
3592 /* Do they get an OpServ level promotion? */
3593 if (hi_from->opserv_level > hi_to->opserv_level)
3594 hi_to->opserv_level = hi_from->opserv_level;
3596 /* What about last seen time? */
3597 if (hi_from->lastseen > hi_to->lastseen)
3598 hi_to->lastseen = hi_from->lastseen;
3600 /* New karma is the sum of the two original karmas. */
3601 hi_to->karma += hi_from->karma;
3603 /* Does a fakehost carry over? (This intentionally doesn't set it
3604 * for users previously attached to hi_to. They'll just have to
3607 if (hi_from->fakehost && !hi_to->fakehost)
3608 hi_to->fakehost = strdup(hi_from->fakehost);
3609 if (hi_from->fakeident && !hi_to->fakeident)
3610 hi_to->fakeident = strdup(hi_from->fakeident);
3612 /* Notify of success. */
3613 sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle);
3614 reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle);
3615 global_message(MESSAGE_RECIPIENT_STAFF, buffer);
3617 /* Unregister the "from" handle. */
3618 nickserv_unregister_handle(hi_from, NULL);
3623 #define NICKSERV_DISCRIM_FIELDS_AUTH 0x01
3624 #define NICKSERV_DISCRIM_FIELDS_EMAIL 0x02
3625 #define NICKSERV_DISCRIM_FIELDS_SEEN 0x04
3626 #define NICKSERV_DISCRIM_FIELDS_ACCESS 0x08
3627 #define NICKSERV_DISCRIM_FIELDS_FAKEHOST 0x10
3628 #define NICKSERV_DISCRIM_FIELDS_WEBSITE 0x20
3629 #define NICKSERV_DISCRIM_FIELDS_DEVNULL 0x40
3631 #define NICKSERV_DISCRIM_FIELD_COUNT 7
3633 struct nickserv_discrim {
3634 unsigned int show_fields;
3635 struct helpfile_table *output_table;
3636 int output_table_pos;
3637 unsigned int output_table_free_fields;
3639 unsigned long flags_on, flags_off;
3640 unsigned long min_registered, max_registered;
3641 unsigned long lastseen;
3643 int min_level, max_level;
3644 int min_karma, max_karma;
3645 enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type;
3646 const char *nickmask;
3647 const char *hostmask;
3648 const char *fakehostmask;
3649 const char *fakeidentmask;
3650 const char *website;
3651 const char *devnullclass;
3652 const char *handlemask;
3653 const char *emailmask;
3656 typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi, struct nickserv_discrim *discrim);
3658 struct discrim_apply_info {
3659 struct nickserv_discrim *discrim;
3660 discrim_search_func func;
3661 struct userNode *source;
3662 unsigned int matched;
3665 static struct nickserv_discrim *
3666 nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[])
3669 struct nickserv_discrim *discrim;
3671 discrim = malloc(sizeof(*discrim));
3672 memset(discrim, 0, sizeof(*discrim));
3673 discrim->min_level = 0;
3674 discrim->max_level = INT_MAX;
3675 discrim->limit = 50;
3676 discrim->min_registered = 0;
3677 discrim->max_registered = ULONG_MAX;
3678 discrim->lastseen = ULONG_MAX;
3679 discrim->min_karma = INT_MIN;
3680 discrim->max_karma = INT_MAX;
3682 for (i=0; i<argc; i++) {
3683 if (i == argc - 1) {
3684 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3687 if (!irccasecmp(argv[i], "limit")) {
3688 discrim->limit = strtoul(argv[++i], NULL, 0);
3689 } else if (!irccasecmp(argv[i], "flags")) {
3690 nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off);
3691 } else if (!irccasecmp(argv[i], "fields")) {
3692 char *fields = argv[++i];
3693 char *delimiter = strstr(fields, ",");
3697 if(!irccasecmp(fields, "auth"))
3698 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_AUTH;
3699 else if(!irccasecmp(fields, "email"))
3700 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_EMAIL;
3701 else if(!irccasecmp(fields, "seen"))
3702 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_SEEN;
3703 else if(!irccasecmp(fields, "access"))
3704 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_ACCESS;
3705 else if(!irccasecmp(fields, "fakehost"))
3706 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_FAKEHOST;
3707 else if(!irccasecmp(fields, "website") && IsBot(user))
3708 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_WEBSITE;
3709 else if(!irccasecmp(fields, "devnull"))
3710 discrim->show_fields |= NICKSERV_DISCRIM_FIELDS_DEVNULL;
3712 send_message(user, nickserv, "MSG_INVALID_FIELD", fields);
3717 fields = delimiter+1;
3719 delimiter = strstr(fields, ",");
3725 } else if (!irccasecmp(argv[i], "registered")) {
3726 const char *cmp = argv[++i];
3727 if (cmp[0] == '<') {
3728 if (cmp[1] == '=') {
3729 discrim->min_registered = now - ParseInterval(cmp+2);
3731 discrim->min_registered = now - ParseInterval(cmp+1) + 1;
3733 } else if (cmp[0] == '=') {
3734 discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1);
3735 } else if (cmp[0] == '>') {
3736 if (cmp[1] == '=') {
3737 discrim->max_registered = now - ParseInterval(cmp+2);
3739 discrim->max_registered = now - ParseInterval(cmp+1) - 1;
3742 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3744 } else if (!irccasecmp(argv[i], "seen")) {
3745 discrim->lastseen = now - ParseInterval(argv[++i]);
3746 } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) {
3747 discrim->nickmask = argv[++i];
3748 } else if (!irccasecmp(argv[i], "hostmask")) {
3750 if (!irccasecmp(argv[i], "exact")) {
3751 if (i == argc - 1) {
3752 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3755 discrim->hostmask_type = EXACT;
3756 } else if (!irccasecmp(argv[i], "subset")) {
3757 if (i == argc - 1) {
3758 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3761 discrim->hostmask_type = SUBSET;
3762 } else if (!irccasecmp(argv[i], "superset")) {
3763 if (i == argc - 1) {
3764 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3767 discrim->hostmask_type = SUPERSET;
3768 } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) {
3769 if (i == argc - 1) {
3770 send_message(user, nickserv, "MSG_MISSING_PARAMS", argv[i]);
3773 discrim->hostmask_type = LASTQUIT;
3776 discrim->hostmask_type = SUPERSET;
3778 discrim->hostmask = argv[++i];
3779 } else if (!irccasecmp(argv[i], "fakehost")) {
3780 if (!irccasecmp(argv[++i], "*")) {
3781 discrim->fakehostmask = 0;
3783 discrim->fakehostmask = argv[i];
3785 } else if (!irccasecmp(argv[i], "fakeident")) {
3786 if (!irccasecmp(argv[++i], "*")) {
3787 discrim->fakeidentmask = 0;
3789 discrim->fakeidentmask = argv[i];
3791 } else if (!irccasecmp(argv[i], "website")) {
3792 if (!irccasecmp(argv[++i], "*")) {
3793 discrim->website = 0;
3795 discrim->website = argv[i];
3797 } else if (!irccasecmp(argv[i], "devnull")) {
3798 if (!irccasecmp(argv[++i], "*")) {
3799 discrim->devnullclass = 0;
3801 discrim->devnullclass = argv[i];
3803 } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) {
3804 if (!irccasecmp(argv[++i], "*")) {
3805 discrim->handlemask = 0;
3807 discrim->handlemask = argv[i];
3809 } else if (!irccasecmp(argv[i], "email")) {
3810 if (user->handle_info->opserv_level < nickserv_conf.email_search_level) {
3811 send_message(user, nickserv, "MSG_NO_SEARCH_ACCESS", "email");
3813 } else if (!irccasecmp(argv[++i], "*")) {
3814 discrim->emailmask = 0;
3816 discrim->emailmask = argv[i];
3818 } else if (!irccasecmp(argv[i], "access")) {
3819 const char *cmp = argv[++i];
3820 if (cmp[0] == '<') {
3821 if (discrim->min_level == 0) discrim->min_level = 1;
3822 if (cmp[1] == '=') {
3823 discrim->max_level = strtoul(cmp+2, NULL, 0);
3825 discrim->max_level = strtoul(cmp+1, NULL, 0) - 1;
3827 } else if (cmp[0] == '=') {
3828 discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0);
3829 } else if (cmp[0] == '>') {
3830 if (cmp[1] == '=') {
3831 discrim->min_level = strtoul(cmp+2, NULL, 0);
3833 discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
3836 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3838 } else if (!irccasecmp(argv[i], "karma")) {
3839 const char *cmp = argv[++i];
3840 if (cmp[0] == '<') {
3841 if (cmp[1] == '=') {
3842 discrim->max_karma = strtoul(cmp+2, NULL, 0);
3844 discrim->max_karma = strtoul(cmp+1, NULL, 0) - 1;
3846 } else if (cmp[0] == '=') {
3847 discrim->min_karma = discrim->max_karma = strtoul(cmp+1, NULL, 0);
3848 } else if (cmp[0] == '>') {
3849 if (cmp[1] == '=') {
3850 discrim->min_karma = strtoul(cmp+2, NULL, 0);
3852 discrim->min_karma = strtoul(cmp+1, NULL, 0) + 1;
3855 send_message(user, nickserv, "MSG_INVALID_CRITERIA", cmp);
3858 send_message(user, nickserv, "MSG_INVALID_CRITERIA", argv[i]);
3869 nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi)
3871 if (((discrim->flags_on & hi->flags) != discrim->flags_on)
3872 || (discrim->flags_off & hi->flags)
3873 || (discrim->min_registered > hi->registered)
3874 || (discrim->max_registered < hi->registered)
3875 || (discrim->lastseen < (hi->users?now:hi->lastseen))
3876 || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask))
3877 || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask)))
3878 || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask)))
3879 || (discrim->website && (!hi->website || !match_ircglob(hi->website, discrim->website)))
3880 || (discrim->devnullclass && (!hi->devnull || !match_ircglob(hi->devnull, discrim->devnullclass)))
3881 || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask)))
3882 || (discrim->min_level > hi->opserv_level)
3883 || (discrim->max_level < hi->opserv_level)
3884 || (discrim->min_karma > hi->karma)
3885 || (discrim->max_karma < hi->karma)
3889 if (discrim->hostmask) {
3891 for (i=0; i<hi->masks->used; i++) {
3892 const char *mask = hi->masks->list[i];
3893 if ((discrim->hostmask_type == SUBSET)
3894 && (match_ircglobs(discrim->hostmask, mask))) break;
3895 else if ((discrim->hostmask_type == EXACT)
3896 && !irccasecmp(discrim->hostmask, mask)) break;
3897 else if ((discrim->hostmask_type == SUPERSET)
3898 && (match_ircglobs(mask, discrim->hostmask))) break;
3899 else if ((discrim->hostmask_type == LASTQUIT)
3900 && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break;
3902 if (i==hi->masks->used) return 0;
3904 if (discrim->nickmask) {
3905 struct nick_info *nick = hi->nicks;
3907 if (match_ircglob(nick->nick, discrim->nickmask)) break;
3910 if (!nick) return 0;
3916 nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source)
3918 dict_iterator_t it, next;
3919 unsigned int matched;
3921 for (it = dict_first(nickserv_handle_dict), matched = 0;
3922 it && (matched < discrim->limit);
3924 next = iter_next(it);
3925 if (nickserv_discrim_match(discrim, iter_data(it))) {
3926 dsf(source, iter_data(it), discrim);
3934 search_print_func(struct userNode *source, struct handle_info *match, struct nickserv_discrim *discrim)
3936 if(discrim->show_fields) {
3938 if(discrim->output_table) {
3939 discrim->output_table->contents[++discrim->output_table_pos] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
3941 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
3942 discrim->output_table->contents[discrim->output_table_pos][i++] = match->handle;
3943 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
3944 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->email_addr ? match->email_addr : "*");
3945 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN) {
3947 char seenBuf[INTERVALLEN];
3950 } else if(match->lastseen == 0) {
3953 seen = intervalString(seenBuf, now - match->lastseen, source->handle_info);
3955 discrim->output_table_free_fields |= 1 << i;
3956 discrim->output_table->contents[discrim->output_table_pos][i++] = strdup(seen);
3958 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
3959 discrim->output_table->contents[discrim->output_table_pos][i++] = strtab(match->opserv_level);
3960 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
3961 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->fakehost ? match->fakehost : "*");
3962 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
3963 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->website ? match->website : "*");
3964 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
3965 discrim->output_table->contents[discrim->output_table_pos][i++] = (match->devnull ? match->devnull : "*");
3969 send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle);
3973 search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match), UNUSED_ARG(struct nickserv_discrim *discrim))
3978 search_unregister_func (struct userNode *source, struct handle_info *match, UNUSED_ARG(struct nickserv_discrim *discrim))
3980 if (oper_has_access(source, nickserv, match->opserv_level, 0))
3981 nickserv_unregister_handle(match, source);
3985 nickserv_sort_accounts_by_access(const void *a, const void *b)
3987 const struct handle_info *hi_a = *(const struct handle_info**)a;
3988 const struct handle_info *hi_b = *(const struct handle_info**)b;
3989 if (hi_a->opserv_level != hi_b->opserv_level)
3990 return hi_b->opserv_level - hi_a->opserv_level;
3991 return irccasecmp(hi_a->handle, hi_b->handle);
3995 nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd)
3997 struct handle_info_list hil;
3998 struct helpfile_table tbl;
4003 memset(&hil, 0, sizeof(hil));
4004 for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) {
4005 struct handle_info *hi = iter_data(it);
4006 if (hi->opserv_level)
4007 handle_info_list_append(&hil, hi);
4009 qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access);
4010 tbl.length = hil.used + 1;
4012 tbl.flags = TABLE_NO_FREE;
4013 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4014 tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0]));
4017 for (ii = 0; ii < hil.used; ) {
4018 ary = malloc(tbl.width * sizeof(ary[0]));
4019 ary[0] = hil.list[ii]->handle;
4020 ary[1] = strtab(hil.list[ii]->opserv_level);
4021 tbl.contents[++ii] = ary;
4023 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4024 reply("MSG_MATCH_COUNT", hil.used);
4025 for (ii = 0; ii < hil.used; ii++)
4026 free(tbl.contents[ii]);
4031 static NICKSERV_FUNC(cmd_search)
4033 struct nickserv_discrim *discrim;
4034 discrim_search_func action;
4035 struct svccmd *subcmd;
4036 unsigned int matches;
4039 NICKSERV_MIN_PARMS(3);
4040 sprintf(buf, "search %s", argv[1]);
4041 subcmd = dict_find(nickserv_service->commands, buf, NULL);
4042 if (!irccasecmp(argv[1], "print"))
4043 action = search_print_func;
4044 else if (!irccasecmp(argv[1], "count"))
4045 action = search_count_func;
4046 else if (!irccasecmp(argv[1], "unregister"))
4047 action = search_unregister_func;
4049 reply("NSMSG_INVALID_ACTION", argv[1]);
4053 if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY))
4056 discrim = nickserv_discrim_create(user, argc-2, argv+2);
4060 if (action == search_print_func)
4061 reply("NSMSG_ACCOUNT_SEARCH_RESULTS");
4062 else if (action == search_count_func)
4063 discrim->limit = INT_MAX;
4065 matches = nickserv_discrim_search(discrim, action, user);
4067 if(discrim->show_fields) {
4070 for(ii = 0; ii < NICKSERV_DISCRIM_FIELD_COUNT; ii++) {
4071 if(discrim->show_fields & (1 << ii)) width++;
4073 discrim->output_table = malloc(sizeof(discrim->output_table[0]));
4074 discrim->output_table->length = matches+1;
4075 discrim->output_table->width = width;
4076 discrim->output_table->flags = TABLE_NO_FREE;
4077 discrim->output_table->contents = malloc(discrim->output_table->length * sizeof(discrim->output_table->contents[0]));
4078 discrim->output_table->contents[0] = malloc(discrim->output_table->width * sizeof(discrim->output_table->contents[0][0]));
4081 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_AUTH)
4082 discrim->output_table->contents[0][ii++] = "Auth";
4083 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_EMAIL)
4084 discrim->output_table->contents[0][ii++] = "EMail";
4085 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_SEEN)
4086 discrim->output_table->contents[0][ii++] = "Seen";
4087 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_ACCESS)
4088 discrim->output_table->contents[0][ii++] = "Access";
4089 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_FAKEHOST)
4090 discrim->output_table->contents[0][ii++] = "Fakehost";
4091 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_WEBSITE)
4092 discrim->output_table->contents[0][ii++] = "Website";
4093 if(discrim->show_fields & NICKSERV_DISCRIM_FIELDS_DEVNULL)
4094 discrim->output_table->contents[0][ii++] = "DevNull";
4096 nickserv_discrim_search(discrim, action, user);
4098 table_send(nickserv, user->nick, 0, NULL, *discrim->output_table);
4100 for(ii = 1; ii < discrim->output_table->length; ++ii) {
4102 for(ij = 0; ij < NICKSERV_DISCRIM_FIELD_COUNT; ij++) {
4103 if(discrim->output_table_free_fields & (1 << ij))
4104 free((char*)discrim->output_table->contents[ii][ij]);
4106 free(discrim->output_table->contents[ii]);
4108 free(discrim->output_table->contents[0]);
4109 free(discrim->output_table->contents);
4110 free(discrim->output_table);
4113 reply("MSG_MATCH_COUNT", matches);
4115 reply("MSG_NO_MATCHES");
4122 static MODCMD_FUNC(cmd_checkpass)
4124 struct handle_info *hi;
4126 NICKSERV_MIN_PARMS(3);
4127 if (!(hi = get_handle_info(argv[1]))) {
4128 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4131 if (checkpass(argv[2], hi->passwd))
4132 reply("CHECKPASS_YES");
4134 reply("CHECKPASS_NO");
4139 static MODCMD_FUNC(cmd_checkemail)
4141 struct handle_info *hi;
4143 NICKSERV_MIN_PARMS(3);
4144 if (!(hi = modcmd_get_handle_info(user, argv[1]))) {
4147 if (!hi->email_addr)
4148 reply("CHECKEMAIL_NOT_SET");
4149 else if (!irccasecmp(argv[2], hi->email_addr))
4150 reply("CHECKEMAIL_YES");
4152 reply("CHECKEMAIL_NO");
4157 nickserv_db_read_authlog(UNUSED_ARG(const char *key), void *data, void *extra)
4159 struct record_data *rd = data;
4160 struct handle_info *hi = extra;
4162 struct authlogEntry *authlog;
4163 authlog = malloc(sizeof(*authlog));
4165 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGIN_TIME, RECDB_QSTRING);
4166 authlog->login_time = str ? strtoul(str, NULL, 0) : 0;
4168 str = database_get_data(rd->d.object, KEY_AUTHLOG_LOGOUT_TIME, RECDB_QSTRING);
4169 authlog->logout_time = str ? strtoul(str, NULL, 0) : 0;
4171 str = database_get_data(rd->d.object, KEY_AUTHLOG_HOSTMASK, RECDB_QSTRING);
4172 authlog->hostmask = str ? strdup(str) : NULL;
4174 str = database_get_data(rd->d.object, KEY_AUTHLOG_QUIT_REASON, RECDB_QSTRING);
4175 authlog->quit_reason = str ? strdup(str) : NULL;
4177 authlog->next = NULL;
4179 //append it to the end of the list...
4180 struct authlogEntry *authlog_entry;
4182 hi->authlog = authlog;
4184 for(authlog_entry = hi->authlog; authlog_entry; authlog_entry = authlog_entry->next) {
4185 if(!authlog_entry->next) {
4186 authlog_entry->next = authlog;
4195 nickserv_db_read_handle(const char *handle, dict_t obj)
4198 struct string_list *masks, *slist;
4199 struct handle_info *hi;
4200 struct userNode *authed_users;
4201 struct userData *channel_list;
4206 str = database_get_data(obj, KEY_ID, RECDB_QSTRING);
4207 id = str ? strtoul(str, NULL, 0) : 0;
4208 str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING);
4210 log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle);
4213 if ((hi = get_handle_info(handle))) {
4214 authed_users = hi->users;
4215 channel_list = hi->channels;
4217 hi->channels = NULL;
4218 dict_remove(nickserv_handle_dict, hi->handle);
4220 authed_users = NULL;
4221 channel_list = NULL;
4223 hi = register_handle(handle, str, id);
4225 hi->users = authed_users;
4226 while (authed_users) {
4227 authed_users->handle_info = hi;
4228 authed_users = authed_users->next_authed;
4231 hi->channels = channel_list;
4232 masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST);
4233 hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1);
4234 str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING);
4235 hi->maxlogins = str ? strtoul(str, NULL, 0) : 0;
4236 str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING);
4237 hi->language = language_find(str ? str : "C");
4238 str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING);
4239 hi->opserv_level = str ? strtoul(str, NULL, 0) : 0;
4240 str = database_get_data(obj, KEY_INFO, RECDB_QSTRING);
4242 hi->infoline = strdup(str);
4243 str = database_get_data(obj, KEY_WEBSITE, RECDB_QSTRING);
4245 hi->website = strdup(str);
4246 str = database_get_data(obj, KEY_DEVNULL, RECDB_QSTRING);
4248 hi->devnull = strdup(str);
4249 str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING);
4250 hi->registered = str ? strtoul(str, NULL, 0) : now;
4251 str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING);
4252 hi->lastseen = str ? strtoul(str, NULL, 0) : hi->registered;
4253 str = database_get_data(obj, KEY_KARMA, RECDB_QSTRING);
4254 hi->karma = str ? strtoul(str, NULL, 0) : 0;
4255 /* We want to read the nicks even if disable_nicks is set. This is so
4256 * that we don't lose the nick data entirely. */
4257 slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST);
4259 for (ii=0; ii<slist->used; ii++)
4260 register_nick(slist->list[ii], hi);
4262 str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING);
4264 for (ii=0; str[ii]; ii++)
4265 hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1);
4267 str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING);
4268 hi->userlist_style = str ? str[0] : HI_STYLE_ZOOT;
4269 str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING);
4270 hi->screen_width = str ? strtoul(str, NULL, 0) : 0;
4271 str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING);
4272 hi->table_width = str ? strtoul(str, NULL, 0) : 0;
4273 str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING);
4275 str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING);
4277 safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host));
4278 str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING);
4280 nickserv_set_email_addr(hi, str);
4281 str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING);
4283 hi->epithet = strdup(str);
4284 str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING);
4286 hi->fakehost = strdup(str);
4287 str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING);
4289 hi->fakeident = strdup(str);
4290 /* Read the "cookie" sub-database (if it exists). */
4291 subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT);
4293 const char *data, *type, *expires, *cookie_str;
4294 struct handle_cookie *cookie;
4296 cookie = calloc(1, sizeof(*cookie));
4297 type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING);
4298 data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING);
4299 expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING);
4300 cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING);
4301 if (!type || !expires || !cookie_str) {
4302 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle);
4305 if (!irccasecmp(type, KEY_ACTIVATION))
4306 cookie->type = ACTIVATION;
4307 else if (!irccasecmp(type, KEY_PASSWORD_CHANGE))
4308 cookie->type = PASSWORD_CHANGE;
4309 else if (!irccasecmp(type, KEY_EMAIL_CHANGE))
4310 cookie->type = EMAIL_CHANGE;
4311 else if (!irccasecmp(type, KEY_ALLOWAUTH))
4312 cookie->type = ALLOWAUTH;
4314 log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle);
4317 cookie->expires = strtoul(expires, NULL, 0);
4318 if (cookie->expires < now)
4321 cookie->data = strdup(data);
4322 safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie));
4326 nickserv_bake_cookie(cookie);
4328 nickserv_free_cookie(cookie);
4330 /* Read the "notes" sub-database (if it exists). */
4331 subdb = database_get_data(obj, KEY_NOTES, RECDB_OBJECT);
4334 struct handle_note *last_note;
4335 struct handle_note *note;
4338 for (it = dict_first(subdb); it; it = iter_next(it)) {
4339 const char *expires;
4343 const char *note_id;
4346 note_id = iter_key(it);
4347 notedb = GET_RECORD_OBJECT((struct record_data*)iter_data(it));
4349 log_module(NS_LOG, LOG_ERROR, "Malformed note %s for account %s; ignoring note.", note_id, hi->handle);
4352 expires = database_get_data(notedb, KEY_NOTE_EXPIRES, RECDB_QSTRING);
4353 setter = database_get_data(notedb, KEY_NOTE_SETTER, RECDB_QSTRING);
4354 text = database_get_data(notedb, KEY_NOTE_NOTE, RECDB_QSTRING);
4355 set = database_get_data(notedb, KEY_NOTE_SET, RECDB_QSTRING);
4356 if (!setter || !text || !set) {
4357 log_module(NS_LOG, LOG_ERROR, "Missing field(s) from note %s for account %s; ignoring note.", note_id, hi->handle);
4360 note = calloc(1, sizeof(*note) + strlen(text));
4362 note->expires = expires ? strtoul(expires, NULL, 10) : 0;
4363 note->set = strtoul(set, NULL, 10);
4364 note->id = strtoul(note_id, NULL, 10);
4365 safestrncpy(note->setter, setter, sizeof(note->setter));
4366 strcpy(note->note, text);
4368 last_note->next = note;
4374 if ((subdb = database_get_data(obj, KEY_AUTHLOG, RECDB_OBJECT)))
4375 dict_foreach(subdb, nickserv_db_read_authlog, hi);
4379 nickserv_saxdb_read(dict_t db) {
4381 struct record_data *rd;
4383 for (it=dict_first(db); it; it=iter_next(it)) {
4385 nickserv_db_read_handle(iter_key(it), rd->d.object);
4390 static NICKSERV_FUNC(cmd_mergedb)
4392 struct timeval start, stop;
4395 NICKSERV_MIN_PARMS(2);
4396 gettimeofday(&start, NULL);
4397 if (!(db = parse_database(argv[1]))) {
4398 reply("NSMSG_DB_UNREADABLE", argv[1]);
4401 nickserv_saxdb_read(db);
4403 gettimeofday(&stop, NULL);
4404 stop.tv_sec -= start.tv_sec;
4405 stop.tv_usec -= start.tv_usec;
4406 if (stop.tv_usec < 0) {
4408 stop.tv_usec += 1000000;
4410 reply("NSMSG_DB_MERGED", argv[1], (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000);
4415 expire_handles(UNUSED_ARG(void *data))
4417 dict_iterator_t it, next;
4418 unsigned long expiry;
4419 struct handle_info *hi;
4421 for (it=dict_first(nickserv_handle_dict); it; it=next) {
4422 next = iter_next(it);
4424 if ((hi->opserv_level > 0)
4426 || HANDLE_FLAGGED(hi, FROZEN)
4427 || HANDLE_FLAGGED(hi, NODELETE)) {
4430 expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay;
4431 if ((now - hi->lastseen) > expiry) {
4432 log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle);
4433 nickserv_unregister_handle(hi, NULL);
4437 if (nickserv_conf.handle_expire_frequency)
4438 timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL);
4442 nickserv_load_dict(const char *fname)
4446 if (!(file = fopen(fname, "r"))) {
4447 log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno));
4450 while (fgets(line, sizeof(line), file)) {
4453 if (line[strlen(line)-1] == '\n')
4454 line[strlen(line)-1] = 0;
4455 dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL);
4458 log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict));
4461 static enum reclaim_action
4462 reclaim_action_from_string(const char *str) {
4464 return RECLAIM_NONE;
4465 else if (!irccasecmp(str, "warn"))
4466 return RECLAIM_WARN;
4467 else if (!irccasecmp(str, "svsnick"))
4468 return RECLAIM_SVSNICK;
4469 else if (!irccasecmp(str, "kill"))
4470 return RECLAIM_KILL;
4472 return RECLAIM_NONE;
4476 nickserv_conf_read(void)
4478 dict_t conf_node, child;
4482 if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) {
4483 log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME);
4486 str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING);
4488 str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING);
4489 if (nickserv_conf.valid_handle_regex_set)
4490 regfree(&nickserv_conf.valid_handle_regex);
4492 int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4493 nickserv_conf.valid_handle_regex_set = !err;
4494 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err);
4496 nickserv_conf.valid_handle_regex_set = 0;
4498 str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING);
4499 if (nickserv_conf.valid_nick_regex_set)
4500 regfree(&nickserv_conf.valid_nick_regex);
4502 int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
4503 nickserv_conf.valid_nick_regex_set = !err;
4504 if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err);
4506 nickserv_conf.valid_nick_regex_set = 0;
4508 str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING);
4510 str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING);
4511 nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4;
4512 str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING);
4513 nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0;
4514 str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING);
4515 nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0;
4516 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING);
4517 nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0;
4518 str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING);
4519 nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0;
4520 str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING);
4521 nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0;
4522 str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING);
4523 nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0;
4524 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
4525 nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
4526 str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING);
4527 nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900;
4528 str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING);
4529 nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1;
4530 str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING);
4531 nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900;
4532 str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING);
4533 nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000;
4534 str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING);
4535 nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000;
4536 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING);
4538 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING);
4539 nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400;
4540 str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4542 str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4543 nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30;
4544 str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING);
4546 str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING);
4547 nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15;
4548 str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING);
4549 nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1;
4550 str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
4551 nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
4552 str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
4553 nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
4554 str = database_get_data(conf_node, KEY_MAX_AUTHLOG_LEN, RECDB_QSTRING);
4555 nickserv_conf.max_authlog_len = str ? strtoul(str, NULL, 0) : 30;
4556 str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
4557 nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
4558 str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
4561 nickserv_conf.ounregister_flags = 0;
4563 unsigned int pos = handle_inverse_flags[(unsigned char)*str];
4566 nickserv_conf.ounregister_flags |= 1 << (pos - 1);
4568 str = database_get_data(conf_node, KEY_HANDLE_TS_MODE, RECDB_QSTRING);
4570 nickserv_conf.handle_ts_mode = TS_IGNORE;
4571 else if (!irccasecmp(str, "ircu"))
4572 nickserv_conf.handle_ts_mode = TS_IRCU;
4574 nickserv_conf.handle_ts_mode = TS_IGNORE;
4575 if (!nickserv_conf.disable_nicks) {
4576 str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
4577 nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4578 str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING);
4579 nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0;
4580 str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING);
4581 nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
4582 str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING);
4583 nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0;
4585 child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT);
4586 for (it=dict_first(child); it; it=iter_next(it)) {
4587 const char *key = iter_key(it), *value;
4591 if (!strncasecmp(key, "uc_", 3))
4592 flag = toupper(key[3]);
4593 else if (!strncasecmp(key, "lc_", 3))
4594 flag = tolower(key[3]);
4598 if ((pos = handle_inverse_flags[flag])) {
4599 value = GET_RECORD_QSTRING((struct record_data*)iter_data(it));
4600 flag_access_levels[pos - 1] = strtoul(value, NULL, 0);
4603 if (nickserv_conf.weak_password_dict)
4604 dict_delete(nickserv_conf.weak_password_dict);
4605 nickserv_conf.weak_password_dict = dict_new();
4606 dict_set_free_keys(nickserv_conf.weak_password_dict, free);
4607 dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL);
4608 dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL);
4609 str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING);
4611 nickserv_load_dict(str);
4612 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
4613 if (nickserv && str)
4614 NickChange(nickserv, str, 0);
4615 str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING);
4616 nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1;
4617 str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING);
4618 nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800;
4619 str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING);
4620 nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800;
4621 str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING);
4622 nickserv_conf.email_enabled = str ? enabled_string(str) : 0;
4623 str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING);
4624 nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600;
4625 str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING);
4626 nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0;
4627 str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING);
4628 nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1;
4629 str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING);
4630 nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600;
4631 str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING);
4632 titlehost_suffix = str ? str : "example.net";
4633 str = conf_get_data("server/network", RECDB_QSTRING);
4634 nickserv_conf.network_name = str ? str : "some IRC network";
4635 if (!nickserv_conf.auth_policer_params) {
4636 nickserv_conf.auth_policer_params = policer_params_new();
4637 policer_params_set(nickserv_conf.auth_policer_params, "size", "5");
4638 policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05");
4640 child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT);
4641 for (it=dict_first(child); it; it=iter_next(it))
4642 set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params);
4646 nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) {
4648 char newnick[NICKLEN+1];
4657 send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle);
4659 case RECLAIM_SVSNICK:
4661 snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000);
4662 } while (GetUserH(newnick));
4663 irc_svsnick(nickserv, user, newnick);
4666 msg = user_find_message(user, "NSMSG_RECLAIM_KILL");
4667 DelUser(user, nickserv, 1, msg);
4673 nickserv_reclaim_p(void *data) {
4674 struct userNode *user = data;
4675 struct nick_info *ni = get_nick_info(user->nick);
4677 nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action);
4681 check_user_nick(struct userNode *user) {
4682 //check if this user is a pending LOC user
4683 struct pendingLOCUser *pending, *next, *prev = NULL;
4684 for(pending = pendingLOCUsers; pending; pending = next) {
4685 next = pending->next;
4686 if(!strcmp(user->numeric, pending->numeric)) {
4687 pending->authlog->user = user;
4691 pendingLOCUsers = next;
4694 if(now - pending->time > 10) {
4698 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);