]>
Commit | Line | Data |
---|---|---|
1 | /* nickserv.c - Nick/authentication service | |
2 | * Copyright 2000-2004 srvx Development Team | |
3 | * | |
4 | * This file is part of x3. | |
5 | * | |
6 | * x3 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. | |
10 | * | |
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. | |
15 | * | |
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. | |
19 | */ | |
20 | ||
21 | #include "chanserv.h" | |
22 | #include "conf.h" | |
23 | #include "global.h" | |
24 | #include "modcmd.h" | |
25 | #include "opserv.h" /* for gag_create(), opserv_bad_channel() */ | |
26 | #include "saxdb.h" | |
27 | #include "sendmail.h" | |
28 | #include "timeq.h" | |
29 | ||
30 | #include <regex.h> | |
31 | ||
32 | #define NICKSERV_CONF_NAME "services/nickserv" | |
33 | ||
34 | #define KEY_DISABLE_NICKS "disable_nicks" | |
35 | #define KEY_DEFAULT_HOSTMASK "default_hostmask" | |
36 | #define KEY_NICKS_PER_HANDLE "nicks_per_handle" | |
37 | #define KEY_NICKS_PER_ACCOUNT "nicks_per_account" | |
38 | #define KEY_PASSWORD_MIN_LENGTH "password_min_length" | |
39 | #define KEY_PASSWORD_MIN_DIGITS "password_min_digits" | |
40 | #define KEY_PASSWORD_MIN_UPPER "password_min_upper" | |
41 | #define KEY_PASSWORD_MIN_LOWER "password_min_lower" | |
42 | #define KEY_VALID_HANDLE_REGEX "valid_handle_regex" | |
43 | #define KEY_VALID_ACCOUNT_REGEX "valid_account_regex" | |
44 | #define KEY_VALID_NICK_REGEX "valid_nick_regex" | |
45 | #define KEY_VALID_FAKEHOST_REGEX "valid_fakehost_regex" | |
46 | #define KEY_DB_BACKUP_FREQ "db_backup_freq" | |
47 | #define KEY_MODOPER_LEVEL "modoper_level" | |
48 | #define KEY_SET_EPITHET_LEVEL "set_epithet_level" | |
49 | #define KEY_SET_TITLE_LEVEL "set_title_level" | |
50 | #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level" | |
51 | #define KEY_DENIED_FAKEHOST_WORDS "denied_fakehost_words" | |
52 | #define KEY_TITLEHOST_SUFFIX "titlehost_suffix" | |
53 | #define KEY_AUTO_OPER "auto_oper" | |
54 | #define KEY_AUTO_ADMIN "auto_admin" | |
55 | #define KEY_FLAG_LEVELS "flag_levels" | |
56 | #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq" | |
57 | #define KEY_ACCOUNT_EXPIRE_FREQ "account_expire_freq" | |
58 | #define KEY_HANDLE_EXPIRE_DELAY "handle_expire_delay" | |
59 | #define KEY_ACCOUNT_EXPIRE_DELAY "account_expire_delay" | |
60 | #define KEY_NOCHAN_HANDLE_EXPIRE_DELAY "nochan_handle_expire_delay" | |
61 | #define KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY "nochan_account_expire_delay" | |
62 | #define KEY_DICT_FILE "dict_file" | |
63 | #define KEY_NICK "nick" | |
64 | #define KEY_LANGUAGE "language" | |
65 | #define KEY_AUTOGAG_ENABLED "autogag_enabled" | |
66 | #define KEY_AUTOGAG_DURATION "autogag_duration" | |
67 | #define KEY_AUTH_POLICER "auth_policer" | |
68 | #define KEY_EMAIL_VISIBLE_LEVEL "email_visible_level" | |
69 | #define KEY_EMAIL_ENABLED "email_enabled" | |
70 | #define KEY_EMAIL_REQUIRED "email_required" | |
71 | #define KEY_SYNC_LOG "sync_log" | |
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_DEFAULT_STYLE "default_style" | |
76 | ||
77 | #define KEY_ID "id" | |
78 | #define KEY_PASSWD "passwd" | |
79 | #define KEY_NICKS "nicks" | |
80 | #define KEY_MASKS "masks" | |
81 | #define KEY_IGNORES "ignores" | |
82 | #define KEY_OPSERV_LEVEL "opserv_level" | |
83 | #define KEY_FLAGS "flags" | |
84 | #define KEY_REGISTER_ON "register" | |
85 | #define KEY_LAST_SEEN "lastseen" | |
86 | #define KEY_INFO "info" | |
87 | #define KEY_USERLIST_STYLE "user_style" | |
88 | #define KEY_SCREEN_WIDTH "screen_width" | |
89 | #define KEY_LAST_AUTHED_HOST "last_authed_host" | |
90 | #define KEY_LAST_QUIT_HOST "last_quit_host" | |
91 | #define KEY_EMAIL_ADDR "email_addr" | |
92 | #define KEY_COOKIE "cookie" | |
93 | #define KEY_COOKIE_DATA "data" | |
94 | #define KEY_COOKIE_TYPE "type" | |
95 | #define KEY_COOKIE_EXPIRES "expires" | |
96 | #define KEY_ACTIVATION "activation" | |
97 | #define KEY_PASSWORD_CHANGE "password change" | |
98 | #define KEY_EMAIL_CHANGE "email change" | |
99 | #define KEY_ALLOWAUTH "allowauth" | |
100 | #define KEY_EPITHET "epithet" | |
101 | #define KEY_TABLE_WIDTH "table_width" | |
102 | #define KEY_ANNOUNCEMENTS "announcements" | |
103 | #define KEY_MAXLOGINS "maxlogins" | |
104 | #define KEY_FAKEHOST "fakehost" | |
105 | #define KEY_NOTE_NOTE "note" | |
106 | #define KEY_NOTE_SETTER "setter" | |
107 | #define KEY_NOTE_DATE "date" | |
108 | ||
109 | ||
110 | #define NICKSERV_VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" | |
111 | ||
112 | #define NICKSERV_FUNC(NAME) MODCMD_FUNC(NAME) | |
113 | #define OPTION_FUNC(NAME) int NAME(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, UNUSED_ARG(unsigned int override), unsigned int argc, char *argv[]) | |
114 | typedef OPTION_FUNC(option_func_t); | |
115 | ||
116 | DEFINE_LIST(handle_info_list, struct handle_info*); | |
117 | ||
118 | #define NICKSERV_MIN_PARMS(N) do { \ | |
119 | if (argc < N) { \ | |
120 | reply("MSG_MISSING_PARAMS", argv[0]); \ | |
121 | svccmd_send_help_brief(user, nickserv, cmd); \ | |
122 | return 0; \ | |
123 | } } while (0) | |
124 | ||
125 | struct userNode *nickserv; | |
126 | struct userList curr_helpers; | |
127 | const char *handle_flags = HANDLE_FLAGS; | |
128 | ||
129 | extern struct string_list *autojoin_channels; | |
130 | static struct module *nickserv_module; | |
131 | static struct service *nickserv_service; | |
132 | static struct log_type *NS_LOG; | |
133 | static dict_t nickserv_handle_dict; /* contains struct handle_info* */ | |
134 | static dict_t nickserv_id_dict; /* contains struct handle_info* */ | |
135 | static dict_t nickserv_nick_dict; /* contains struct nick_info* */ | |
136 | static dict_t nickserv_opt_dict; /* contains option_func_t* */ | |
137 | static dict_t nickserv_allow_auth_dict; /* contains struct handle_info* */ | |
138 | static dict_t nickserv_email_dict; /* contains struct handle_info_list*, indexed by email addr */ | |
139 | static char handle_inverse_flags[256]; | |
140 | static unsigned int flag_access_levels[32]; | |
141 | static const struct message_entry msgtab[] = { | |
142 | { "NSMSG_HANDLE_EXISTS", "Account $b%s$b is already registered." }, | |
143 | { "NSMSG_HANDLE_TOLONG", "The account name %s is too long. Account names must be %lu charactors or less."}, | |
144 | { "NSMSG_PASSWORD_SHORT", "Your password must be at least %lu characters long." }, | |
145 | { "NSMSG_PASSWORD_ACCOUNT", "Your password may not be the same as your account name." }, | |
146 | { "NSMSG_PASSWORD_DICTIONARY", "Your password is too simple. You must choose a password that is not just a word or name." }, | |
147 | { "NSMSG_PASSWORD_READABLE", "Your password must have at least %lu digit(s), %lu capital letter(s), and %lu lower-case letter(s)." }, | |
148 | { "NSMSG_PARTIAL_REGISTER", "Account has been registered to you; nick was already registered to someone else." }, | |
149 | { "NSMSG_OREGISTER_VICTIM", "%s has registered a new account for you (named %s)." }, | |
150 | { "NSMSG_OREGISTER_H_SUCCESS", "Account has been registered." }, | |
151 | { "NSMSG_REGISTER_H_SUCCESS", "Account has been registered to you." }, | |
152 | { "NSMSG_REGISTER_HN_SUCCESS", "Account and nick have been registered to you." }, | |
153 | { "NSMSG_REQUIRE_OPER", "You must be an $bIRC Operator$b to register the first account." }, | |
154 | { "NSMSG_ROOT_HANDLE", "Account %s has been granted $broot-level privileges$b." }, | |
155 | { "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." }, | |
156 | { "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." }, | |
157 | { "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." }, | |
158 | { "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." }, | |
159 | { "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." }, | |
160 | { "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." }, | |
161 | { "NSMSG_EMAIL_UNACTIVATED", "That email address already has an unused cookie outstanding. Please use the cookie or wait for it to expire." }, | |
162 | { "NSMSG_NO_COOKIE", "Your account does not have any cookie issued right now." }, | |
163 | { "NSMSG_NO_COOKIE_FOREIGN", "The account $b%s$b does not have any cookie issued right now." }, | |
164 | { "NSMSG_CANNOT_COOKIE", "You cannot use that kind of cookie when you are logged in." }, | |
165 | { "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." }, | |
166 | { "NSMSG_HANDLE_ACTIVATED", "Your account is now activated (with the password you entered when you registered). You are now authenticated to your account." }, | |
167 | { "NSMSG_PASSWORD_CHANGED", "You have successfully changed your password to what you requested with the $bresetpass$b command." }, | |
168 | { "NSMSG_EMAIL_PROHIBITED", "%s may not be used as an email address: %s" }, | |
169 | { "NSMSG_EMAIL_OVERUSED", "That email address already has an account. Use RESETPASS if you forgot your password." }, | |
170 | { "NSMSG_EMAIL_SAME", "That is the email address already there; no need to change it." }, | |
171 | { "NSMSG_EMAIL_CHANGED", "You have successfully changed your email address." }, | |
172 | { "NSMSG_BAD_COOKIE_TYPE", "Your account had bad cookie type %d; sorry. I am confused. Please report this bug." }, | |
173 | { "NSMSG_MUST_TIME_OUT", "You must wait for cookies of that type to time out." }, | |
174 | { "NSMSG_ATE_COOKIE", "I ate the cookie for your account. You may now have another." }, | |
175 | { "NSMSG_ATE_FOREIGN_COOKIE", "I ate the cookie for account $b%s$b. It may now have another." }, | |
176 | { "NSMSG_USE_RENAME", "You are already authenticated to account $b%s$b -- contact the support staff to rename your account." }, | |
177 | { "NSMSG_ALREADY_REGISTERING", "You have already used $bREGISTER$b once this session; you may not use it again." }, | |
178 | { "NSMSG_REGISTER_BAD_NICKMASK", "You must provide a hostmask, or online nick to generate one automatically. (or set a default hostmask in the config such as *@*)." }, | |
179 | { "NSMSG_NICK_NOT_REGISTERED", "Nick $b%s$b has not been registered to any account." }, | |
180 | { "NSMSG_HANDLE_NOT_FOUND", "Could not find your account -- did you register yet?" }, | |
181 | { "NSMSG_ALREADY_AUTHED", "You are already authed to account $b%s$b; you must reconnect to auth to a different account." }, | |
182 | { "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)" }, | |
183 | { "NSMSG_HOSTMASK_INVALID", "Your hostmask is not valid for account $b%s$b." }, | |
184 | { "NSMSG_USER_IS_SERVICE", "$b%s$b is a network service; you can only use that command on real users." }, | |
185 | { "NSMSG_USER_PREV_AUTH", "$b%s$b is already authenticated." }, | |
186 | { "NSMSG_USER_PREV_STAMP", "$b%s$b has authenticated to an account once and cannot authenticate again." }, | |
187 | { "NSMSG_BAD_MAX_LOGINS", "MaxLogins must be at most %d." }, | |
188 | { "NSMSG_BAD_ADVANCED", "Advanced must be either 1 to enable it or 0 to disable it." }, | |
189 | { "NSMSG_LANGUAGE_NOT_FOUND", "Language $b%s$b is not supported; $b%s$b was the closest available match." }, | |
190 | { "NSMSG_MAX_LOGINS", "Your account already has its limit of %d user(s) logged in." }, | |
191 | { "NSMSG_STAMPED_REGISTER", "You have already authenticated to an account once this session; you may not register a new account." }, | |
192 | { "NSMSG_STAMPED_AUTH", "You have already authenticated to an account once this session; you may not authenticate to another." }, | |
193 | { "NSMSG_STAMPED_RESETPASS", "You have already authenticated to an account once this session; you may not reset your password to authenticate again." }, | |
194 | { "NSMSG_STAMPED_AUTHCOOKIE", "You have already authenticated to an account once this session; you may not use a cookie to authenticate to another account." }, | |
195 | { "NSMSG_TITLE_INVALID", "Titles may contain only a-z, A-Z, 0-9, and '-'. Please choose another." }, | |
196 | { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." }, | |
197 | { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." }, | |
198 | { "NSMSG_HANDLEINFO_ON", "$bAccount Information for %s$b" }, | |
199 | { "NSMSG_HANDLEINFO_END", "----------End of Account Info-----------" }, | |
200 | { "NSMSG_HANDLEINFO_ID", "Account ID: %lu" }, | |
201 | { "NSMSG_HANDLEINFO_REGGED", "Registered on: %s" }, | |
202 | { "NSMSG_HANDLEINFO_LASTSEEN", "Last seen: %s" }, | |
203 | { "NSMSG_HANDLEINFO_LASTSEEN_NOW", "Last seen: Right now!" }, | |
204 | { "NSMSG_HANDLEINFO_VACATION", "On vacation." }, | |
205 | { "NSMSG_HANDLEINFO_EMAIL_ADDR", "Email address: %s" }, | |
206 | { "NSMSG_HANDLEINFO_COOKIE_ACTIVATION", "Cookie: There is currently an activation cookie issued for this account" }, | |
207 | { "NSMSG_HANDLEINFO_COOKIE_PASSWORD", "Cookie: There is currently a password change cookie issued for this account" }, | |
208 | { "NSMSG_HANDLEINFO_COOKIE_EMAIL", "Cookie: There is currently an email change cookie issued for this account" }, | |
209 | { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", "Cookie: There is currently an allowauth cookie issued for this account" }, | |
210 | { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", "Cookie: There is currently an unknown cookie issued for this account" }, | |
211 | { "NSMSG_HANDLEINFO_INFOLINE", "Infoline: %s" }, | |
212 | { "NSMSG_HANDLEINFO_FLAGS", "Flags: %s" }, | |
213 | { "NSMSG_HANDLEINFO_EPITHET", "Epithet: %s" }, | |
214 | { "NSMSG_HANDLEINFO_NOTE", "Note (by %s on %s): %s " }, | |
215 | { "NSMSG_HANDLEINFO_FAKEHOST", "Fake host: %s" }, | |
216 | { "NSMSG_HANDLEINFO_LAST_HOST", "Last quit hostmask: %s" }, | |
217 | { "NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN", "Last quit hostmask: Unknown" }, | |
218 | { "NSMSG_HANDLEINFO_NICKS", "Nickname(s): %s" }, | |
219 | { "NSMSG_HANDLEINFO_MASKS", "Hostmask(s): %s" }, | |
220 | { "NSMSG_HANDLEINFO_IGNORES", "Ignore(s): %s" }, | |
221 | { "NSMSG_HANDLEINFO_CHANNELS", "Channel(s): %s" }, | |
222 | { "NSMSG_HANDLEINFO_CURRENT", "Current nickname(s): %s" }, | |
223 | { "NSMSG_HANDLEINFO_DNR", "Do-not-register (by %s): %s" }, | |
224 | { "NSMSG_USERINFO_AUTHED_AS", "$b%s$b is authenticated to account $b%s$b." }, | |
225 | { "NSMSG_USERINFO_NOT_AUTHED", "$b%s$b is not authenticated to any account." }, | |
226 | { "NSMSG_NICKINFO_OWNER", "Nick $b%s$b is owned by account $b%s$b." }, | |
227 | { "NSMSG_PASSWORD_INVALID", "Incorrect password; please try again." }, | |
228 | { "NSMSG_PLEASE_SET_EMAIL", "We now require email addresses for users. Please use the $bset email$b command to set your email address!" }, | |
229 | { "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)." }, | |
230 | { "NSMSG_HANDLE_SUSPENDED", "Your $b$N$b account has been suspended; you may not use it." }, | |
231 | { "NSMSG_AUTH_SUCCESS", "I recognize you." }, | |
232 | { "NSMSG_ALLOWAUTH_STAFF", "$b%s$b is a helper or oper; please use $bstaff$b after the account name to allowauth." }, | |
233 | { "NSMSG_AUTH_ALLOWED", "User $b%s$b may now authenticate to account $b%s$b." }, | |
234 | { "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." }, | |
235 | { "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." }, | |
236 | { "NSMSG_AUTH_NORMAL_ONLY", "User $b%s$b may now only authenticate to accounts with matching hostmasks." }, | |
237 | { "NSMSG_AUTH_UNSPECIAL", "User $b%s$b did not have any special auth allowance." }, | |
238 | { "NSMSG_MUST_AUTH", "You must be authenticated first." }, | |
239 | { "NSMSG_TOO_MANY_NICKS", "You have already registered the maximum permitted number of nicks." }, | |
240 | { "NSMSG_NICK_EXISTS", "Nick $b%s$b already registered." }, | |
241 | { "NSMSG_REGNICK_SUCCESS", "Nick $b%s$b has been registered to you." }, | |
242 | { "NSMSG_OREGNICK_SUCCESS", "Nick $b%s$b has been registered to account $b%s$b." }, | |
243 | { "NSMSG_PASS_SUCCESS", "Password changed." }, | |
244 | { "NSMSG_MASK_INVALID", "$b%s$b is an invalid hostmask." }, | |
245 | { "NSMSG_ADDMASK_ALREADY", "$b%s$b is already a hostmask in your account." }, | |
246 | { "NSMSG_ADDMASK_SUCCESS", "Hostmask %s added." }, | |
247 | { "NSMSG_ADDIGNORE_ALREADY", "$b%s$b is already an ignored hostmask in your account." }, | |
248 | { "NSMSG_ADDIGNORE_SUCCESS", "Hostmask %s added." }, | |
249 | { "NSMSG_DELMASK_NOTLAST", "You may not delete your last hostmask." }, | |
250 | { "NSMSG_DELMASK_SUCCESS", "Hostmask %s deleted." }, | |
251 | { "NSMSG_DELMASK_NOT_FOUND", "Unable to find mask to be deleted." }, | |
252 | { "NSMSG_OPSERV_LEVEL_BAD", "You may not promote another oper above your level." }, | |
253 | { "NSMSG_USE_CMD_PASS", "Please use the PASS command to change your password." }, | |
254 | { "NSMSG_UNKNOWN_NICK", "I know nothing about nick $b%s$b." }, | |
255 | { "NSMSG_NOT_YOUR_NICK", "The nick $b%s$b is not registered to you." }, | |
256 | { "NSMSG_NICK_USER_YOU", "I will not let you kill yourself." }, | |
257 | { "NSMSG_UNREGNICK_SUCCESS", "Nick $b%s$b has been unregistered." }, | |
258 | { "NSMSG_UNREGISTER_SUCCESS", "Account $b%s$b has been unregistered." }, | |
259 | { "NSMSG_UNREGISTER_NICKS_SUCCESS", "Account $b%s$b and all its nicks have been unregistered." }, | |
260 | { "NSMSG_HANDLE_STATS", "There are %d nicks registered to your account." }, | |
261 | { "NSMSG_HANDLE_NONE", "You are not authenticated against any account." }, | |
262 | { "NSMSG_GLOBAL_STATS", "There are %d accounts and %d nicks registered globally." }, | |
263 | { "NSMSG_GLOBAL_STATS_NONICK", "There are %d accounts registered." }, | |
264 | { "NSMSG_CANNOT_GHOST_SELF", "You may not ghost-kill yourself." }, | |
265 | { "NSMSG_CANNOT_GHOST_USER", "$b%s$b is not authed to your account; you may not ghost-kill them." }, | |
266 | { "NSMSG_GHOST_KILLED", "$b%s$b has been killed as a ghost." }, | |
267 | { "NSMSG_ON_VACATION", "You are now on vacation. Your account will be preserved until you authenticate again." }, | |
268 | { "NSMSG_NO_ACCESS", "Access denied." }, | |
269 | { "NSMSG_INVALID_FLAG", "$b%c$b is not a valid $N account flag." }, | |
270 | { "NSMSG_SET_FLAG", "Applied flags $b%s$b to %s's $N account." }, | |
271 | { "NSMSG_FLAG_PRIVILEGED", "You have insufficient access to set flag %c." }, | |
272 | { "NSMSG_DB_UNREADABLE", "Unable to read database file %s; check the log for more information." }, | |
273 | { "NSMSG_DB_MERGED", "$N merged DB from %s (in "FMT_TIME_T".%03lu seconds)." }, | |
274 | { "NSMSG_HANDLE_CHANGED", "$b%s$b's account name has been changed to $b%s$b." }, | |
275 | { "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." }, | |
276 | { "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." }, | |
277 | { "NSMSG_BAD_EMAIL_ADDR", "Please use a well-formed email address." }, | |
278 | { "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." }, | |
279 | { "NSMSG_ACCOUNT_SEARCH_RESULTS", "The following accounts were found:" }, | |
280 | { "NSMSG_SEARCH_MATCH", "Match: %s" }, | |
281 | { "NSMSG_INVALID_ACTION", "%s is an invalid search action." }, | |
282 | { "NSMSG_CANNOT_MERGE_SELF", "You cannot merge account $b%s$b with itself." }, | |
283 | { "NSMSG_HANDLES_MERGED", "Merged account $b%s$b into $b%s$b." }, | |
284 | { "NSMSG_RECLAIM_WARN", "%s is a registered nick - you must auth to account %s or change your nick." }, | |
285 | { "NSMSG_RECLAIM_KILL", "Unauthenticated user of nick." }, | |
286 | { "NSMSG_RECLAIMED_NONE", "You cannot manually reclaim a nick." }, | |
287 | { "NSMSG_RECLAIMED_WARN", "Sent a request for %s to change their nick." }, | |
288 | { "NSMSG_RECLAIMED_SVSNICK", "Forcibly changed %s's nick." }, | |
289 | { "NSMSG_RECLAIMED_KILL", "Disconnected %s from the network." }, | |
290 | { "NSMSG_CLONE_AUTH", "Warning: %s (%s@%s) authed to your account." }, | |
291 | { "NSMSG_SETTING_LIST", "$b$N account settings$b" }, | |
292 | { "NSMSG_SETTING_LIST_HEADER", "----------------------------------------" }, | |
293 | { "NSMSG_SETTING_LIST_END", "-------------End Of Settings------------" }, | |
294 | { "NSMSG_INVALID_OPTION", "$b%s$b is an invalid account setting." }, | |
295 | { "NSMSG_INVALID_ANNOUNCE", "$b%s$b is an invalid announcements value." }, | |
296 | { "NSMSG_SET_INFO", "$bINFO: $b%s" }, | |
297 | { "NSMSG_SET_WIDTH", "$bWIDTH: $b%d" }, | |
298 | { "NSMSG_SET_TABLEWIDTH", "$bTABLEWIDTH: $b%d" }, | |
299 | { "NSMSG_SET_COLOR", "$bCOLOR: $b%s" }, | |
300 | { "NSMSG_SET_PRIVMSG", "$bPRIVMSG: $b%s" }, | |
301 | { "NSMSG_SET_STYLE", "$bSTYLE: $b%s" }, | |
302 | { "NSMSG_SET_ANNOUNCEMENTS", "$bANNOUNCEMENTS: $b%s" }, | |
303 | { "NSMSG_SET_AUTOHIDE", "$bAUTOHIDE: $b%s" }, | |
304 | { "NSMSG_SET_PASSWORD", "$bPASSWORD: $b%s" }, | |
305 | { "NSMSG_SET_FLAGS", "$bFLAGS: $b%s" }, | |
306 | { "NSMSG_SET_EMAIL", "$bEMAIL: $b%s" }, | |
307 | { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS: $b%d" }, | |
308 | { "NSMSG_SET_ADVANCED", "$bADVANCED: $b%s" }, | |
309 | { "NSMSG_SET_LANGUAGE", "$bLANGUAGE: $b%s" }, | |
310 | { "NSMSG_SET_LEVEL", "$bLEVEL: $b%d" }, | |
311 | { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" }, | |
312 | { "NSMSG_SET_NOTE", "$bNOTE: $b%s"}, | |
313 | { "NSMSG_SET_TITLE", "$bTITLE: $b%s" }, | |
314 | { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" }, | |
315 | ||
316 | { "NSMSG_AUTO_OPER", "You have been auto-opered" }, | |
317 | { "NSMSG_AUTO_OPER_ADMIN", "You have been auto-admined" }, | |
318 | ||
319 | { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" }, | |
320 | { "NSEMAIL_ACTIVATION_BODY", | |
321 | "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" | |
322 | "%2$s\n" | |
323 | "To verify your email address and complete the account registration, log on to %1$s and type the following command:\n" | |
324 | "/msg %3$s@%4$s COOKIE %5$s %2$s\n" | |
325 | "This 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" | |
326 | "/msg %3$s@%4$s AUTH %5$s your-password\n" | |
327 | "(Please remember to fill in 'your-password' with the actual password you gave to us when you registered.)\n" | |
328 | "OR configure Login-On-Connect (see http://www.afternet.org/login-on-connect for instructions) to connect pre-logged in every time.\n" | |
329 | "\n" | |
330 | "If you did NOT request this account, you do not need to do anything.\n" | |
331 | "Please contact the %1$s staff if you have questions, and be sure to check our website." }, | |
332 | { "NSEMAIL_ACTIVATION_BODY_WEB", | |
333 | "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" | |
334 | "%2$s\n" | |
335 | "To verify your email address and complete the account registration, visit the following URL:\n" | |
336 | "http://www.afternet.org/index.php?option=com_registration&task=activate&username=%5$s&cookie=%2$s\n" | |
337 | "\n" | |
338 | "If you did NOT request this account, you do not need to do anything.\n" | |
339 | "Please contact the %1$s staff if you have questions, and be sure to check our website." }, | |
340 | { "NSEMAIL_PASSWORD_CHANGE_SUBJECT", "Password change verification on %s" }, | |
341 | { "NSEMAIL_PASSWORD_CHANGE_BODY", | |
342 | "This email has been sent to verify that you wish to change the password on your account %5$s. Your cookie is %2$s.\n" | |
343 | "To complete the password change, log on to %1$s and type the following command:\n" | |
344 | "/msg %3$s@%4$s COOKIE %5$s %2$s\n" | |
345 | "If you did NOT request your password to be changed, you do not need to do anything.\n" | |
346 | "Please contact the %1$s staff if you have questions." }, | |
347 | { "NSEMAIL_PASSWORD_CHANGE_BODY_WEB", | |
348 | "This email has been sent to verify that you wish to change the password on your account %5$s. Your cookie is %2$s.\n" | |
349 | "To complete the password change, click the following URL:\n" | |
350 | "http://www.afternet.org/index.php?option=com_registration&task=passcookie&username=%5$s&cookie=%2$s\n" | |
351 | "If you did NOT request your password to be changed, you do not need to do anything.\n" | |
352 | "Please contact the %1$s staff if you have questions." }, | |
353 | { "NSEMAIL_EMAIL_CHANGE_SUBJECT", "Email address change verification for %s" }, | |
354 | #ifdef stupid_verify_old_email | |
355 | { "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." }, | |
356 | { "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." }, | |
357 | #endif | |
358 | { "NSEMAIL_EMAIL_VERIFY_SUBJECT", "Email address verification for %s" }, | |
359 | { "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." }, | |
360 | { "NSEMAIL_ALLOWAUTH_SUBJECT", "Authentication allowed for %s" }, | |
361 | { "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." }, | |
362 | { "NSMSG_NOT_VALID_FAKEHOST_DOT", "$b%s$b is not a valid vhost. (needs at least one dot)" }, | |
363 | { "NSMSG_NOT_VALID_FAKEHOST_AT", "$b%s$b is not a valid vhost. (it can not have a '@')" }, | |
364 | { "NSMSG_DENIED_FAKEHOST_WORD", "Access denied because there's a prohibited word in $b%s$b (%s)." }, | |
365 | { "NSMSG_NOT_VALID_FAKEHOST_LEN", "$b%s$b is not a valid vhost. (can only be 63 characters)" }, | |
366 | { "NSMSG_NOT_VALID_FAKEHOST_TLD_LEN", "$b%s$b is not a valid vhost. (TLD can only be 4 characters and less)" }, | |
367 | { "NSMSG_NOT_VALID_FAKEHOST_REGEX", "$b%s$b is not allowed by the admin, consult the valid vhost regex pattern in the config file under nickserv/valid_fakehost_regex." }, | |
368 | { "CHECKPASS_YES", "Yes." }, | |
369 | { "CHECKPASS_NO", "No." }, | |
370 | { NULL, NULL } | |
371 | }; | |
372 | ||
373 | enum reclaim_action { | |
374 | RECLAIM_NONE, | |
375 | RECLAIM_WARN, | |
376 | RECLAIM_SVSNICK, | |
377 | RECLAIM_KILL | |
378 | }; | |
379 | static void nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action); | |
380 | static void nickserv_reclaim_p(void *data); | |
381 | ||
382 | static struct { | |
383 | unsigned int disable_nicks : 1; | |
384 | unsigned int valid_handle_regex_set : 1; | |
385 | unsigned int valid_nick_regex_set : 1; | |
386 | unsigned int valid_fakehost_regex_set : 1; | |
387 | unsigned int autogag_enabled : 1; | |
388 | unsigned int email_enabled : 1; | |
389 | unsigned int email_required : 1; | |
390 | unsigned int default_hostmask : 1; | |
391 | unsigned int warn_nick_owned : 1; | |
392 | unsigned int warn_clone_auth : 1; | |
393 | unsigned int sync_log : 1; | |
394 | unsigned long nicks_per_handle; | |
395 | unsigned long password_min_length; | |
396 | unsigned long password_min_digits; | |
397 | unsigned long password_min_upper; | |
398 | unsigned long password_min_lower; | |
399 | unsigned long db_backup_frequency; | |
400 | unsigned long handle_expire_frequency; | |
401 | unsigned long autogag_duration; | |
402 | unsigned long email_visible_level; | |
403 | unsigned long cookie_timeout; | |
404 | unsigned long handle_expire_delay; | |
405 | unsigned long nochan_handle_expire_delay; | |
406 | unsigned long modoper_level; | |
407 | unsigned long set_epithet_level; | |
408 | unsigned long set_title_level; | |
409 | unsigned long set_fakehost_level; | |
410 | unsigned long handles_per_email; | |
411 | unsigned long email_search_level; | |
412 | const char *network_name; | |
413 | const char *titlehost_suffix; | |
414 | regex_t valid_handle_regex; | |
415 | regex_t valid_nick_regex; | |
416 | regex_t valid_fakehost_regex; | |
417 | dict_t weak_password_dict; | |
418 | struct policer_params *auth_policer_params; | |
419 | enum reclaim_action reclaim_action; | |
420 | enum reclaim_action auto_reclaim_action; | |
421 | unsigned long auto_reclaim_delay; | |
422 | unsigned char default_maxlogins; | |
423 | unsigned char hard_maxlogins; | |
424 | const char *auto_oper; | |
425 | const char *auto_admin; | |
426 | char default_style; | |
427 | struct string_list *denied_fakehost_words; | |
428 | } nickserv_conf; | |
429 | ||
430 | /* We have 2^32 unique account IDs to use. */ | |
431 | unsigned long int highest_id = 0; | |
432 | ||
433 | static char * | |
434 | canonicalize_hostmask(char *mask) | |
435 | { | |
436 | char *out = mask, *temp; | |
437 | if ((temp = strchr(mask, '!'))) { | |
438 | temp++; | |
439 | while (*temp) *out++ = *temp++; | |
440 | *out++ = 0; | |
441 | } | |
442 | return mask; | |
443 | } | |
444 | ||
445 | static struct handle_note * | |
446 | nickserv_add_note(const char *setter, time_t date, const char *text) | |
447 | { | |
448 | struct handle_note *note = calloc(1, sizeof(*note) + strlen(text)); | |
449 | ||
450 | strncpy(note->setter, setter, sizeof(note->setter)-1); | |
451 | note->date = date; | |
452 | memcpy(note->note, text, strlen(text)); | |
453 | return note; | |
454 | } | |
455 | ||
456 | static struct handle_info * | |
457 | register_handle(const char *handle, const char *passwd, UNUSED_ARG(unsigned long id)) | |
458 | { | |
459 | struct handle_info *hi; | |
460 | ||
461 | #ifdef WITH_PROTOCOL_BAHAMUT | |
462 | char id_base64[IDLEN + 1]; | |
463 | do | |
464 | { | |
465 | /* Assign a unique account ID to the account; note that 0 is | |
466 | an invalid account ID. 1 is therefore the first account ID. */ | |
467 | if (!id) { | |
468 | id = 1 + highest_id++; | |
469 | } else { | |
470 | /* Note: highest_id is and must always be the highest ID. */ | |
471 | if(id > highest_id) { | |
472 | highest_id = id; | |
473 | } | |
474 | } | |
475 | inttobase64(id_base64, id, IDLEN); | |
476 | ||
477 | /* Make sure an account with the same ID doesn't exist. If a | |
478 | duplicate is found, log some details and assign a new one. | |
479 | This should be impossible, but it never hurts to expect it. */ | |
480 | if ((hi = dict_find(nickserv_id_dict, id_base64, NULL))) { | |
481 | log_module(NS_LOG, LOG_WARNING, "Duplicated account ID %lu (%s) found belonging to %s while inserting %s.", id, id_base64, hi->handle, handle); | |
482 | id = 0; | |
483 | } | |
484 | } while(!id); | |
485 | #endif | |
486 | ||
487 | hi = calloc(1, sizeof(*hi)); | |
488 | hi->userlist_style = HI_DEFAULT_STYLE; | |
489 | hi->announcements = '?'; | |
490 | hi->handle = strdup(handle); | |
491 | safestrncpy(hi->passwd, passwd, sizeof(hi->passwd)); | |
492 | hi->infoline = NULL; | |
493 | dict_insert(nickserv_handle_dict, hi->handle, hi); | |
494 | ||
495 | #ifdef WITH_PROTOCOL_BAHAMUT | |
496 | hi->id = id; | |
497 | dict_insert(nickserv_id_dict, strdup(id_base64), hi); | |
498 | #endif | |
499 | ||
500 | return hi; | |
501 | } | |
502 | ||
503 | static void | |
504 | register_nick(const char *nick, struct handle_info *owner) | |
505 | { | |
506 | struct nick_info *ni; | |
507 | ni = malloc(sizeof(struct nick_info)); | |
508 | safestrncpy(ni->nick, nick, sizeof(ni->nick)); | |
509 | ni->owner = owner; | |
510 | ni->next = owner->nicks; | |
511 | owner->nicks = ni; | |
512 | dict_insert(nickserv_nick_dict, ni->nick, ni); | |
513 | } | |
514 | ||
515 | static void | |
516 | delete_nick(struct nick_info *ni) | |
517 | { | |
518 | struct nick_info *last, *next; | |
519 | struct userNode *user; | |
520 | /* Check to see if we should mark a user as unregistered. */ | |
521 | if ((user = GetUserH(ni->nick)) && IsReggedNick(user)) { | |
522 | user->modes &= ~FLAGS_REGNICK; | |
523 | irc_regnick(user); | |
524 | } | |
525 | /* Remove ni from the nick_info linked list. */ | |
526 | if (ni == ni->owner->nicks) { | |
527 | ni->owner->nicks = ni->next; | |
528 | } else { | |
529 | last = ni->owner->nicks; | |
530 | next = last->next; | |
531 | while (next != ni) { | |
532 | last = next; | |
533 | next = last->next; | |
534 | } | |
535 | last->next = next->next; | |
536 | } | |
537 | dict_remove(nickserv_nick_dict, ni->nick); | |
538 | } | |
539 | ||
540 | static unreg_func_t *unreg_func_list; | |
541 | static unsigned int unreg_func_size = 0, unreg_func_used = 0; | |
542 | ||
543 | void | |
544 | reg_unreg_func(unreg_func_t func) | |
545 | { | |
546 | if (unreg_func_used == unreg_func_size) { | |
547 | if (unreg_func_size) { | |
548 | unreg_func_size <<= 1; | |
549 | unreg_func_list = realloc(unreg_func_list, unreg_func_size*sizeof(unreg_func_t)); | |
550 | } else { | |
551 | unreg_func_size = 8; | |
552 | unreg_func_list = malloc(unreg_func_size*sizeof(unreg_func_t)); | |
553 | } | |
554 | } | |
555 | unreg_func_list[unreg_func_used++] = func; | |
556 | } | |
557 | ||
558 | static void | |
559 | nickserv_free_cookie(void *data) | |
560 | { | |
561 | struct handle_cookie *cookie = data; | |
562 | if (cookie->hi) cookie->hi->cookie = NULL; | |
563 | if (cookie->data) free(cookie->data); | |
564 | free(cookie); | |
565 | } | |
566 | ||
567 | static void | |
568 | free_handle_info(void *vhi) | |
569 | { | |
570 | struct handle_info *hi = vhi; | |
571 | ||
572 | #ifdef WITH_PROTOCOL_BAHAMUT | |
573 | char id[IDLEN + 1]; | |
574 | ||
575 | inttobase64(id, hi->id, IDLEN); | |
576 | dict_remove(nickserv_id_dict, id); | |
577 | #endif | |
578 | ||
579 | free_string_list(hi->masks); | |
580 | free_string_list(hi->ignores); | |
581 | assert(!hi->users); | |
582 | ||
583 | while (hi->nicks) | |
584 | delete_nick(hi->nicks); | |
585 | free(hi->infoline); | |
586 | free(hi->epithet); | |
587 | free(hi->note); | |
588 | free(hi->fakehost); | |
589 | if (hi->cookie) { | |
590 | timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0); | |
591 | nickserv_free_cookie(hi->cookie); | |
592 | } | |
593 | if (hi->email_addr) { | |
594 | struct handle_info_list *hil = dict_find(nickserv_email_dict, hi->email_addr, NULL); | |
595 | handle_info_list_remove(hil, hi); | |
596 | if (!hil->used) | |
597 | dict_remove(nickserv_email_dict, hi->email_addr); | |
598 | } | |
599 | free(hi); | |
600 | } | |
601 | ||
602 | static void set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp); | |
603 | ||
604 | static void | |
605 | nickserv_unregister_handle(struct handle_info *hi, struct userNode *notify, struct userNode *bot) | |
606 | { | |
607 | unsigned int n; | |
608 | struct userNode *uNode; | |
609 | ||
610 | for (n=0; n<unreg_func_used; n++) | |
611 | unreg_func_list[n](notify, hi); | |
612 | while (hi->users) { | |
613 | if (nickserv_conf.sync_log) { | |
614 | uNode = GetUserH(hi->users->nick); | |
615 | if (uNode) | |
616 | irc_delete(uNode); | |
617 | } | |
618 | set_user_handle_info(hi->users, NULL, 0); | |
619 | } | |
620 | if (notify) { | |
621 | if (nickserv_conf.disable_nicks) | |
622 | send_message(notify, bot, "NSMSG_UNREGISTER_SUCCESS", hi->handle); | |
623 | else | |
624 | send_message(notify, bot, "NSMSG_UNREGISTER_NICKS_SUCCESS", hi->handle); | |
625 | } | |
626 | ||
627 | if (nickserv_conf.sync_log) | |
628 | SyncLog("UNREGISTER %s", hi->handle); | |
629 | ||
630 | dict_remove(nickserv_handle_dict, hi->handle); | |
631 | } | |
632 | ||
633 | struct handle_info* | |
634 | get_handle_info(const char *handle) | |
635 | { | |
636 | return dict_find(nickserv_handle_dict, handle, 0); | |
637 | } | |
638 | ||
639 | struct nick_info* | |
640 | get_nick_info(const char *nick) | |
641 | { | |
642 | return nickserv_conf.disable_nicks ? 0 : dict_find(nickserv_nick_dict, nick, 0); | |
643 | } | |
644 | ||
645 | struct modeNode * | |
646 | find_handle_in_channel(struct chanNode *channel, struct handle_info *handle, struct userNode *except) | |
647 | { | |
648 | unsigned int nn; | |
649 | struct modeNode *mn; | |
650 | ||
651 | for (nn=0; nn<channel->members.used; ++nn) { | |
652 | mn = channel->members.list[nn]; | |
653 | if ((mn->user != except) && (mn->user->handle_info == handle)) | |
654 | return mn; | |
655 | } | |
656 | return NULL; | |
657 | } | |
658 | ||
659 | int | |
660 | oper_has_access(struct userNode *user, struct userNode *bot, unsigned int min_level, unsigned int quiet) { | |
661 | if (!user->handle_info) { | |
662 | if (!quiet) | |
663 | send_message(user, bot, "MSG_AUTHENTICATE"); | |
664 | return 0; | |
665 | } | |
666 | ||
667 | if (!IsOper(user) && (!IsHelping(user) || min_level)) { | |
668 | if (!quiet) | |
669 | send_message(user, bot, "NSMSG_NO_ACCESS"); | |
670 | return 0; | |
671 | } | |
672 | ||
673 | if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) { | |
674 | if (!quiet) | |
675 | send_message(user, bot, "MSG_OPER_SUSPENDED"); | |
676 | return 0; | |
677 | } | |
678 | ||
679 | if (user->handle_info->opserv_level < min_level) { | |
680 | if (!quiet) | |
681 | send_message(user, bot, "NSMSG_NO_ACCESS"); | |
682 | return 0; | |
683 | } | |
684 | ||
685 | return 1; | |
686 | } | |
687 | ||
688 | static int | |
689 | is_valid_handle(const char *handle) | |
690 | { | |
691 | struct userNode *user; | |
692 | /* cant register a juped nick/service nick as handle, to prevent confusion */ | |
693 | user = GetUserH(handle); | |
694 | if (user && IsLocal(user)) | |
695 | return 0; | |
696 | /* check against maximum length */ | |
697 | if (strlen(handle) > NICKSERV_HANDLE_LEN) | |
698 | return 0; | |
699 | /* for consistency, only allow account names that could be nicks */ | |
700 | if (!is_valid_nick(handle)) | |
701 | return 0; | |
702 | /* disallow account names that look like bad words */ | |
703 | if (opserv_bad_channel(handle)) | |
704 | return 0; | |
705 | /* test either regex or containing all valid chars */ | |
706 | if (nickserv_conf.valid_handle_regex_set) { | |
707 | int err = regexec(&nickserv_conf.valid_handle_regex, handle, 0, 0, 0); | |
708 | if (err) { | |
709 | char buff[256]; | |
710 | buff[regerror(err, &nickserv_conf.valid_handle_regex, buff, sizeof(buff))] = 0; | |
711 | log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err); | |
712 | } | |
713 | return !err; | |
714 | } else { | |
715 | return !handle[strspn(handle, NICKSERV_VALID_CHARS)]; | |
716 | } | |
717 | } | |
718 | ||
719 | static int | |
720 | is_registerable_nick(const char *nick) | |
721 | { | |
722 | /* make sure it could be used as an account name */ | |
723 | if (!is_valid_handle(nick)) | |
724 | return 0; | |
725 | /* check length */ | |
726 | if (strlen(nick) > NICKLEN) | |
727 | return 0; | |
728 | /* test either regex or as valid handle */ | |
729 | if (nickserv_conf.valid_nick_regex_set) { | |
730 | int err = regexec(&nickserv_conf.valid_nick_regex, nick, 0, 0, 0); | |
731 | if (err) { | |
732 | char buff[256]; | |
733 | buff[regerror(err, &nickserv_conf.valid_nick_regex, buff, sizeof(buff))] = 0; | |
734 | log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err); | |
735 | } | |
736 | return !err; | |
737 | } | |
738 | return 1; | |
739 | } | |
740 | /* this has been replaced with one in tools.c | |
741 | ||
742 | static int | |
743 | is_valid_email_addr(const char *email) | |
744 | { | |
745 | return strchr(email, '@') != NULL; | |
746 | } | |
747 | ||
748 | */ | |
749 | ||
750 | static const char * | |
751 | visible_email_addr(struct userNode *user, struct handle_info *hi) | |
752 | { | |
753 | if (hi->email_addr) { | |
754 | if (oper_has_access(user, nickserv, nickserv_conf.email_visible_level, 1)) { | |
755 | return hi->email_addr; | |
756 | } else { | |
757 | return "Set."; | |
758 | } | |
759 | } else { | |
760 | return "Not set."; | |
761 | } | |
762 | } | |
763 | ||
764 | struct handle_info * | |
765 | smart_get_handle_info(struct userNode *service, struct userNode *user, const char *name) | |
766 | { | |
767 | struct handle_info *hi; | |
768 | struct userNode *target; | |
769 | ||
770 | switch (*name) { | |
771 | case '*': | |
772 | if (!(hi = get_handle_info(++name))) { | |
773 | send_message(user, service, "MSG_HANDLE_UNKNOWN", name); | |
774 | return 0; | |
775 | } | |
776 | return hi; | |
777 | default: | |
778 | if (!(target = GetUserH(name))) { | |
779 | send_message(user, service, "MSG_NICK_UNKNOWN", name); | |
780 | return 0; | |
781 | } | |
782 | if (IsLocal(target)) { | |
783 | if (IsService(target)) | |
784 | send_message(user, service, "NSMSG_USER_IS_SERVICE", target->nick); | |
785 | else | |
786 | send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick); | |
787 | return 0; | |
788 | } | |
789 | if (!(hi = target->handle_info)) { | |
790 | send_message(user, service, "MSG_USER_AUTHENTICATE", target->nick); | |
791 | return 0; | |
792 | } | |
793 | return hi; | |
794 | } | |
795 | } | |
796 | ||
797 | int | |
798 | oper_outranks(struct svccmd *cmd, struct userNode *user, struct handle_info *hi) { | |
799 | if (user->handle_info->opserv_level > hi->opserv_level) | |
800 | return 1; | |
801 | if (user->handle_info->opserv_level == hi->opserv_level) { | |
802 | if ((user->handle_info->opserv_level == 1000) | |
803 | || (user->handle_info == hi) | |
804 | || ((user->handle_info->opserv_level == 0) | |
805 | && !(HANDLE_FLAGGED(hi, SUPPORT_HELPER) || HANDLE_FLAGGED(hi, NETWORK_HELPER)) | |
806 | && HANDLE_FLAGGED(user->handle_info, HELPING))) { | |
807 | return 1; | |
808 | } | |
809 | } | |
810 | reply("MSG_USER_OUTRANKED", hi->handle); | |
811 | return 0; | |
812 | } | |
813 | ||
814 | struct handle_info * | |
815 | get_victim_oper(struct svccmd *cmd, struct userNode *user, const char *target) | |
816 | { | |
817 | struct handle_info *hi; | |
818 | if (!(hi = smart_get_handle_info(nickserv, user, target))) | |
819 | return 0; | |
820 | if (HANDLE_FLAGGED(user->handle_info, OPER_SUSPENDED)) { | |
821 | send_message(user, nickserv, "MSG_OPER_SUSPENDED"); | |
822 | return 0; | |
823 | } | |
824 | return oper_outranks(cmd, user, hi) ? hi : NULL; | |
825 | } | |
826 | ||
827 | static int | |
828 | valid_user_for(struct userNode *user, struct handle_info *hi) | |
829 | { | |
830 | unsigned int ii; | |
831 | ||
832 | /* If no hostmasks on the account, allow it. */ | |
833 | if (!hi->masks->used) | |
834 | return 1; | |
835 | /* If any hostmask matches, allow it. */ | |
836 | for (ii=0; ii<hi->masks->used; ii++) | |
837 | if (user_matches_glob(user, hi->masks->list[ii], 0)) | |
838 | return 1; | |
839 | /* If they are allowauthed to this account, allow it (removing the aa). */ | |
840 | if (dict_find(nickserv_allow_auth_dict, user->nick, NULL) == hi) { | |
841 | dict_remove(nickserv_allow_auth_dict, user->nick); | |
842 | return 2; | |
843 | } | |
844 | /* The user is not allowed to use this account. */ | |
845 | return 0; | |
846 | } | |
847 | ||
848 | static int | |
849 | is_secure_password(const char *handle, const char *pass, struct userNode *user) | |
850 | { | |
851 | unsigned int i, len; | |
852 | unsigned int cnt_digits = 0, cnt_upper = 0, cnt_lower = 0; | |
853 | int p; | |
854 | ||
855 | len = strlen(pass); | |
856 | if (len < nickserv_conf.password_min_length) { | |
857 | if (user) | |
858 | send_message(user, nickserv, "NSMSG_PASSWORD_SHORT", nickserv_conf.password_min_length); | |
859 | return 0; | |
860 | } | |
861 | if (!irccasecmp(pass, handle)) { | |
862 | if (user) | |
863 | send_message(user, nickserv, "NSMSG_PASSWORD_ACCOUNT"); | |
864 | return 0; | |
865 | } | |
866 | dict_find(nickserv_conf.weak_password_dict, pass, &p); | |
867 | if (p) { | |
868 | if (user) | |
869 | send_message(user, nickserv, "NSMSG_PASSWORD_DICTIONARY"); | |
870 | return 0; | |
871 | } | |
872 | for (i=0; i<len; i++) { | |
873 | if (isdigit(pass[i])) | |
874 | cnt_digits++; | |
875 | if (isupper(pass[i])) | |
876 | cnt_upper++; | |
877 | if (islower(pass[i])) | |
878 | cnt_lower++; | |
879 | } | |
880 | if ((cnt_lower < nickserv_conf.password_min_lower) | |
881 | || (cnt_upper < nickserv_conf.password_min_upper) | |
882 | || (cnt_digits < nickserv_conf.password_min_digits)) { | |
883 | if (user) | |
884 | send_message(user, nickserv, "NSMSG_PASSWORD_READABLE", nickserv_conf.password_min_digits, nickserv_conf.password_min_upper, nickserv_conf.password_min_lower); | |
885 | return 0; | |
886 | } | |
887 | return 1; | |
888 | } | |
889 | ||
890 | static auth_func_t *auth_func_list; | |
891 | static unsigned int auth_func_size = 0, auth_func_used = 0; | |
892 | ||
893 | void | |
894 | reg_auth_func(auth_func_t func) | |
895 | { | |
896 | if (auth_func_used == auth_func_size) { | |
897 | if (auth_func_size) { | |
898 | auth_func_size <<= 1; | |
899 | auth_func_list = realloc(auth_func_list, auth_func_size*sizeof(auth_func_t)); | |
900 | } else { | |
901 | auth_func_size = 8; | |
902 | auth_func_list = malloc(auth_func_size*sizeof(auth_func_t)); | |
903 | } | |
904 | } | |
905 | auth_func_list[auth_func_used++] = func; | |
906 | } | |
907 | ||
908 | static handle_rename_func_t *rf_list; | |
909 | static unsigned int rf_list_size, rf_list_used; | |
910 | ||
911 | void | |
912 | reg_handle_rename_func(handle_rename_func_t func) | |
913 | { | |
914 | if (rf_list_used == rf_list_size) { | |
915 | if (rf_list_size) { | |
916 | rf_list_size <<= 1; | |
917 | rf_list = realloc(rf_list, rf_list_size*sizeof(rf_list[0])); | |
918 | } else { | |
919 | rf_list_size = 8; | |
920 | rf_list = malloc(rf_list_size*sizeof(rf_list[0])); | |
921 | } | |
922 | } | |
923 | rf_list[rf_list_used++] = func; | |
924 | } | |
925 | ||
926 | static char * | |
927 | generate_fakehost(struct handle_info *handle) | |
928 | { | |
929 | extern const char *hidden_host_suffix; | |
930 | static char buffer[HOSTLEN+1]; | |
931 | ||
932 | if (!handle->fakehost) { | |
933 | snprintf(buffer, sizeof(buffer), "%s.%s", handle->handle, hidden_host_suffix); | |
934 | return buffer; | |
935 | } else if (handle->fakehost[0] == '.') { | |
936 | /* A leading dot indicates the stored value is actually a title. */ | |
937 | snprintf(buffer, sizeof(buffer), "%s.%s.%s", handle->handle, handle->fakehost+1, nickserv_conf.titlehost_suffix); | |
938 | return buffer; | |
939 | } | |
940 | return handle->fakehost; | |
941 | } | |
942 | ||
943 | static void | |
944 | apply_fakehost(struct handle_info *handle) | |
945 | { | |
946 | struct userNode *target; | |
947 | char *fake; | |
948 | ||
949 | if (!handle->users) | |
950 | return; | |
951 | fake = generate_fakehost(handle); | |
952 | for (target = handle->users; target; target = target->next_authed) | |
953 | assign_fakehost(target, fake, 1); | |
954 | } | |
955 | ||
956 | void send_func_list(struct userNode *user) | |
957 | { | |
958 | unsigned int n; | |
959 | struct handle_info *old_info; | |
960 | ||
961 | old_info = user->handle_info; | |
962 | ||
963 | for (n=0; n<auth_func_used; n++) | |
964 | auth_func_list[n](user, old_info); | |
965 | } | |
966 | ||
967 | static void | |
968 | set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp) | |
969 | { | |
970 | unsigned int n; | |
971 | struct handle_info *old_info; | |
972 | ||
973 | /* This can happen if somebody uses COOKIE while authed, or if | |
974 | * they re-auth to their current handle (which is silly, but users | |
975 | * are like that). */ | |
976 | if (user->handle_info == hi) | |
977 | return; | |
978 | ||
979 | if (user->handle_info) { | |
980 | struct userNode *other; | |
981 | ||
982 | if (IsHelper(user)) | |
983 | userList_remove(&curr_helpers, user); | |
984 | ||
985 | /* remove from next_authed linked list */ | |
986 | if (user->handle_info->users == user) { | |
987 | user->handle_info->users = user->next_authed; | |
988 | } else { | |
989 | for (other = user->handle_info->users; | |
990 | other->next_authed != user; | |
991 | other = other->next_authed) ; | |
992 | other->next_authed = user->next_authed; | |
993 | } | |
994 | /* if nobody left on old handle, and they're not an oper, remove !god */ | |
995 | if (!user->handle_info->users && !user->handle_info->opserv_level) | |
996 | HANDLE_CLEAR_FLAG(user->handle_info, HELPING); | |
997 | /* record them as being last seen at this time */ | |
998 | user->handle_info->lastseen = now; | |
999 | /* and record their hostmask */ | |
1000 | snprintf(user->handle_info->last_quit_host, sizeof(user->handle_info->last_quit_host), "%s@%s", user->ident, user->hostname); | |
1001 | } | |
1002 | old_info = user->handle_info; | |
1003 | user->handle_info = hi; | |
1004 | if (hi && !hi->users && !hi->opserv_level) | |
1005 | HANDLE_CLEAR_FLAG(hi, HELPING); | |
1006 | ||
1007 | if (GetUserH(user->nick)) { | |
1008 | for (n=0; n<auth_func_used; n++) | |
1009 | auth_func_list[n](user, old_info); | |
1010 | } else | |
1011 | user->loc = 1; | |
1012 | ||
1013 | if (hi) { | |
1014 | struct nick_info *ni; | |
1015 | ||
1016 | HANDLE_CLEAR_FLAG(hi, FROZEN); | |
1017 | if (nickserv_conf.warn_clone_auth) { | |
1018 | struct userNode *other; | |
1019 | for (other = hi->users; other; other = other->next_authed) | |
1020 | send_message(other, nickserv, "NSMSG_CLONE_AUTH", user->nick, user->ident, user->hostname); | |
1021 | } | |
1022 | ||
1023 | user->next_authed = hi->users; | |
1024 | hi->users = user; | |
1025 | hi->lastseen = now; | |
1026 | if (IsHelper(user)) | |
1027 | userList_append(&curr_helpers, user); | |
1028 | ||
1029 | if (hi->fakehost || old_info) | |
1030 | apply_fakehost(hi); | |
1031 | ||
1032 | if (stamp) { | |
1033 | #ifdef WITH_PROTOCOL_BAHAMUT | |
1034 | /* Stamp users with their account ID. */ | |
1035 | char id[IDLEN + 1]; | |
1036 | inttobase64(id, hi->id, IDLEN); | |
1037 | #elif WITH_PROTOCOL_P10 | |
1038 | /* Stamp users with their account name. */ | |
1039 | char *id = hi->handle; | |
1040 | #else | |
1041 | const char *id = "???"; | |
1042 | #endif | |
1043 | if (!nickserv_conf.disable_nicks) { | |
1044 | struct nick_info *ni; | |
1045 | for (ni = hi->nicks; ni; ni = ni->next) { | |
1046 | if (!irccasecmp(user->nick, ni->nick)) { | |
1047 | user->modes |= FLAGS_REGNICK; | |
1048 | break; | |
1049 | } | |
1050 | } | |
1051 | } | |
1052 | StampUser(user, id, hi->registered); | |
1053 | } | |
1054 | ||
1055 | if ((ni = get_nick_info(user->nick)) && (ni->owner == hi)) | |
1056 | timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN); | |
1057 | } else { | |
1058 | /* We cannot clear the user's account ID, unfortunately. */ | |
1059 | user->next_authed = NULL; | |
1060 | } | |
1061 | } | |
1062 | ||
1063 | static struct handle_info* | |
1064 | nickserv_register(struct userNode *user, struct userNode *settee, const char *handle, const char *passwd, int no_auth) | |
1065 | { | |
1066 | struct handle_info *hi; | |
1067 | struct nick_info *ni; | |
1068 | char crypted[MD5_CRYPT_LENGTH]; | |
1069 | ||
1070 | if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) { | |
1071 | send_message(user, nickserv, "NSMSG_HANDLE_EXISTS", handle); | |
1072 | return 0; | |
1073 | } | |
1074 | ||
1075 | if(strlen(handle) > 15) | |
1076 | { | |
1077 | send_message(user, nickserv, "NSMSG_HANDLE_TOLONG", handle, 15); | |
1078 | return 0; | |
1079 | } | |
1080 | ||
1081 | if (!is_secure_password(handle, passwd, user)) | |
1082 | return 0; | |
1083 | ||
1084 | cryptpass(passwd, crypted); | |
1085 | hi = register_handle(handle, crypted, 0); | |
1086 | hi->masks = alloc_string_list(1); | |
1087 | hi->ignores = alloc_string_list(1); | |
1088 | hi->users = NULL; | |
1089 | hi->language = lang_C; | |
1090 | hi->registered = now; | |
1091 | hi->lastseen = now; | |
1092 | hi->flags = HI_DEFAULT_FLAGS; | |
1093 | if (settee && !no_auth) | |
1094 | set_user_handle_info(settee, hi, 1); | |
1095 | ||
1096 | if (user != settee) | |
1097 | send_message(user, nickserv, "NSMSG_OREGISTER_H_SUCCESS"); | |
1098 | else if (nickserv_conf.disable_nicks) | |
1099 | send_message(user, nickserv, "NSMSG_REGISTER_H_SUCCESS"); | |
1100 | else if ((ni = dict_find(nickserv_nick_dict, user->nick, NULL))) | |
1101 | send_message(user, nickserv, "NSMSG_PARTIAL_REGISTER"); | |
1102 | else { | |
1103 | register_nick(user->nick, hi); | |
1104 | send_message(user, nickserv, "NSMSG_REGISTER_HN_SUCCESS"); | |
1105 | } | |
1106 | if (settee && (user != settee)) | |
1107 | send_message(settee, nickserv, "NSMSG_OREGISTER_VICTIM", user->nick, hi->handle); | |
1108 | return hi; | |
1109 | } | |
1110 | ||
1111 | static void | |
1112 | nickserv_bake_cookie(struct handle_cookie *cookie) | |
1113 | { | |
1114 | cookie->hi->cookie = cookie; | |
1115 | timeq_add(cookie->expires, nickserv_free_cookie, cookie); | |
1116 | } | |
1117 | ||
1118 | /* Contributed by the great sneep of afternet ;) */ | |
1119 | /* Since this gets used in a URL, we want to avoid stuff that confuses | |
1120 | * email clients such as ] and ?. a-z, 0-9 only. | |
1121 | */ | |
1122 | void genpass(char *str, int len) | |
1123 | { | |
1124 | int i = 0; | |
1125 | char c = 0; | |
1126 | ||
1127 | for(i = 0; i < len; i++) | |
1128 | { | |
1129 | do | |
1130 | { | |
1131 | c = (char)((float)rand() / (float)RAND_MAX * (float)256); | |
1132 | } while(!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))); | |
1133 | str[i] = c; | |
1134 | } | |
1135 | str[i] = '\0'; | |
1136 | return; | |
1137 | } | |
1138 | ||
1139 | static void | |
1140 | nickserv_make_cookie(struct userNode *user, struct handle_info *hi, enum cookie_type type, const char *cookie_data, int weblink) | |
1141 | { | |
1142 | struct handle_cookie *cookie; | |
1143 | char subject[128], body[4096], *misc; | |
1144 | const char *netname, *fmt; | |
1145 | int first_time = 0; | |
1146 | ||
1147 | if (hi->cookie) { | |
1148 | send_message(user, nickserv, "NSMSG_COOKIE_LIVE", hi->handle); | |
1149 | return; | |
1150 | } | |
1151 | ||
1152 | cookie = calloc(1, sizeof(*cookie)); | |
1153 | cookie->hi = hi; | |
1154 | cookie->type = type; | |
1155 | cookie->data = cookie_data ? strdup(cookie_data) : NULL; | |
1156 | ||
1157 | cookie->expires = now + nickserv_conf.cookie_timeout; | |
1158 | /* Adding dedicated password gen function for more control -Rubin */ | |
1159 | genpass(cookie->cookie, 10); | |
1160 | /* | |
1161 | *inttobase64(cookie->cookie, rand(), 5); | |
1162 | *inttobase64(cookie->cookie+5, rand(), 5); | |
1163 | */ | |
1164 | ||
1165 | netname = nickserv_conf.network_name; | |
1166 | subject[0] = 0; | |
1167 | ||
1168 | switch (cookie->type) { | |
1169 | case ACTIVATION: | |
1170 | hi->passwd[0] = 0; /* invalidate password */ | |
1171 | send_message(user, nickserv, "NSMSG_USE_COOKIE_REGISTER"); | |
1172 | fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_SUBJECT"); | |
1173 | snprintf(subject, sizeof(subject), fmt, netname); | |
1174 | ||
1175 | if(weblink) | |
1176 | fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY_WEB"); | |
1177 | else | |
1178 | fmt = handle_find_message(hi, "NSEMAIL_ACTIVATION_BODY"); | |
1179 | ||
1180 | snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle); | |
1181 | first_time = 1; | |
1182 | break; | |
1183 | case PASSWORD_CHANGE: | |
1184 | send_message(user, nickserv, "NSMSG_USE_COOKIE_RESETPASS"); | |
1185 | fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_SUBJECT"); | |
1186 | snprintf(subject, sizeof(subject), fmt, netname); | |
1187 | if(weblink) | |
1188 | fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY_WEB"); | |
1189 | else | |
1190 | fmt = handle_find_message(hi, "NSEMAIL_PASSWORD_CHANGE_BODY"); | |
1191 | snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle); | |
1192 | break; | |
1193 | case EMAIL_CHANGE: | |
1194 | misc = hi->email_addr; | |
1195 | hi->email_addr = cookie->data; | |
1196 | #ifdef stupid_verify_old_email | |
1197 | if (misc) { | |
1198 | send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_2"); | |
1199 | fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_SUBJECT"); | |
1200 | snprintf(subject, sizeof(subject), fmt, netname); | |
1201 | fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_NEW"); | |
1202 | snprintf(body, sizeof(body), fmt, netname, cookie->cookie+COOKIELEN/2, nickserv->nick, self->name, hi->handle, COOKIELEN/2); | |
1203 | sendmail(nickserv, hi, subject, body, 1); | |
1204 | fmt = handle_find_message(hi, "NSEMAIL_EMAIL_CHANGE_BODY_OLD"); | |
1205 | snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle, COOKIELEN/2, hi->email_addr); | |
1206 | } else { | |
1207 | #endif | |
1208 | send_message(user, nickserv, "NSMSG_USE_COOKIE_EMAIL_1"); | |
1209 | fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_SUBJECT"); | |
1210 | snprintf(subject, sizeof(subject), fmt, netname); | |
1211 | fmt = handle_find_message(hi, "NSEMAIL_EMAIL_VERIFY_BODY"); | |
1212 | snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle); | |
1213 | sendmail(nickserv, hi, subject, body, 1); | |
1214 | subject[0] = 0; | |
1215 | #ifdef stupid_verify_old_email | |
1216 | } | |
1217 | #endif | |
1218 | hi->email_addr = misc; | |
1219 | break; | |
1220 | case ALLOWAUTH: | |
1221 | fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_SUBJECT"); | |
1222 | snprintf(subject, sizeof(subject), fmt, netname); | |
1223 | fmt = handle_find_message(hi, "NSEMAIL_ALLOWAUTH_BODY"); | |
1224 | snprintf(body, sizeof(body), fmt, netname, cookie->cookie, nickserv->nick, self->name, hi->handle); | |
1225 | send_message(user, nickserv, "NSMSG_USE_COOKIE_AUTH"); | |
1226 | break; | |
1227 | default: | |
1228 | log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d in nickserv_make_cookie.", cookie->type); | |
1229 | break; | |
1230 | } | |
1231 | if (subject[0]) | |
1232 | sendmail(nickserv, hi, subject, body, first_time); | |
1233 | nickserv_bake_cookie(cookie); | |
1234 | } | |
1235 | ||
1236 | static void | |
1237 | nickserv_eat_cookie(struct handle_cookie *cookie) | |
1238 | { | |
1239 | cookie->hi->cookie = NULL; | |
1240 | timeq_del(cookie->expires, nickserv_free_cookie, cookie, 0); | |
1241 | nickserv_free_cookie(cookie); | |
1242 | } | |
1243 | ||
1244 | static void | |
1245 | nickserv_free_email_addr(void *data) | |
1246 | { | |
1247 | handle_info_list_clean(data); | |
1248 | free(data); | |
1249 | } | |
1250 | ||
1251 | static void | |
1252 | nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr) | |
1253 | { | |
1254 | struct handle_info_list *hil; | |
1255 | /* Remove from old handle_info_list ... */ | |
1256 | if (hi->email_addr && (hil = dict_find(nickserv_email_dict, hi->email_addr, 0))) { | |
1257 | handle_info_list_remove(hil, hi); | |
1258 | if (!hil->used) dict_remove(nickserv_email_dict, hil->tag); | |
1259 | hi->email_addr = NULL; | |
1260 | } | |
1261 | /* Add to the new list.. */ | |
1262 | if (new_email_addr) { | |
1263 | if (!(hil = dict_find(nickserv_email_dict, new_email_addr, 0))) { | |
1264 | hil = calloc(1, sizeof(*hil)); | |
1265 | hil->tag = strdup(new_email_addr); | |
1266 | handle_info_list_init(hil); | |
1267 | dict_insert(nickserv_email_dict, hil->tag, hil); | |
1268 | } | |
1269 | handle_info_list_append(hil, hi); | |
1270 | hi->email_addr = hil->tag; | |
1271 | } | |
1272 | } | |
1273 | ||
1274 | static NICKSERV_FUNC(cmd_register) | |
1275 | { | |
1276 | irc_in_addr_t ip; | |
1277 | struct handle_info *hi; | |
1278 | const char *email_addr, *password; | |
1279 | char syncpass[MD5_CRYPT_LENGTH]; | |
1280 | int no_auth, weblink; | |
1281 | ||
1282 | if (!IsOper(user) && !dict_size(nickserv_handle_dict)) { | |
1283 | /* Require the first handle registered to belong to someone +o. */ | |
1284 | reply("NSMSG_REQUIRE_OPER"); | |
1285 | return 0; | |
1286 | } | |
1287 | ||
1288 | if (user->handle_info) { | |
1289 | reply("NSMSG_USE_RENAME", user->handle_info->handle); | |
1290 | return 0; | |
1291 | } | |
1292 | ||
1293 | if (IsRegistering(user)) { | |
1294 | reply("NSMSG_ALREADY_REGISTERING"); | |
1295 | return 0; | |
1296 | } | |
1297 | ||
1298 | if (IsStamped(user)) { | |
1299 | /* Unauthenticated users might still have been stamped | |
1300 | previously and could therefore have a hidden host; | |
1301 | do not allow them to register a new account. */ | |
1302 | reply("NSMSG_STAMPED_REGISTER"); | |
1303 | return 0; | |
1304 | } | |
1305 | ||
1306 | NICKSERV_MIN_PARMS((unsigned)3 + nickserv_conf.email_required); | |
1307 | ||
1308 | if (!is_valid_handle(argv[1])) { | |
1309 | reply("NSMSG_BAD_HANDLE", argv[1]); | |
1310 | return 0; | |
1311 | } | |
1312 | ||
1313 | ||
1314 | if ((argc >= 4) && nickserv_conf.email_enabled) { | |
1315 | struct handle_info_list *hil; | |
1316 | const char *str; | |
1317 | ||
1318 | /* Remember email address. */ | |
1319 | email_addr = argv[3]; | |
1320 | ||
1321 | /* Check that the email address looks valid.. */ | |
1322 | if (!valid_email(email_addr)) { | |
1323 | reply("NSMSG_BAD_EMAIL_ADDR"); | |
1324 | return 0; | |
1325 | } | |
1326 | ||
1327 | /* .. and that we are allowed to send to it. */ | |
1328 | if ((str = sendmail_prohibited_address(email_addr))) { | |
1329 | reply("NSMSG_EMAIL_PROHIBITED", email_addr, str); | |
1330 | return 0; | |
1331 | } | |
1332 | ||
1333 | /* If we do email verify, make sure we don't spam the address. */ | |
1334 | if ((hil = dict_find(nickserv_email_dict, email_addr, NULL))) { | |
1335 | unsigned int nn; | |
1336 | for (nn=0; nn<hil->used; nn++) { | |
1337 | if (hil->list[nn]->cookie) { | |
1338 | reply("NSMSG_EMAIL_UNACTIVATED"); | |
1339 | return 0; | |
1340 | } | |
1341 | } | |
1342 | if (hil->used >= nickserv_conf.handles_per_email) { | |
1343 | reply("NSMSG_EMAIL_OVERUSED"); | |
1344 | return 0; | |
1345 | } | |
1346 | } | |
1347 | ||
1348 | no_auth = 1; | |
1349 | } else { | |
1350 | email_addr = 0; | |
1351 | no_auth = 0; | |
1352 | } | |
1353 | ||
1354 | password = argv[2]; | |
1355 | argv[2] = "****"; | |
1356 | /* Webregister hack - send URL instead of IRC cookie | |
1357 | * commands in email | |
1358 | */ | |
1359 | if((argc >= 5) && !strcmp(argv[4],"WEBLINK")) | |
1360 | weblink = 1; | |
1361 | else | |
1362 | weblink = 0; | |
1363 | if (!(hi = nickserv_register(user, user, argv[1], password, no_auth))) | |
1364 | return 0; | |
1365 | /* Add any masks they should get. */ | |
1366 | if (nickserv_conf.default_hostmask) { | |
1367 | string_list_append(hi->masks, strdup("*@*")); | |
1368 | } else { | |
1369 | string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT)); | |
1370 | if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname)) | |
1371 | string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT)); | |
1372 | } | |
1373 | ||
1374 | /* If they're the first to register, give them level 1000. */ | |
1375 | if (dict_size(nickserv_handle_dict) == 1) { | |
1376 | hi->opserv_level = 1000; | |
1377 | reply("NSMSG_ROOT_HANDLE", argv[1]); | |
1378 | } | |
1379 | ||
1380 | /* Set their email address. */ | |
1381 | if (email_addr) | |
1382 | nickserv_set_email_addr(hi, email_addr); | |
1383 | ||
1384 | /* If they need to do email verification, tell them. */ | |
1385 | if (no_auth) | |
1386 | nickserv_make_cookie(user, hi, ACTIVATION, hi->passwd, weblink); | |
1387 | ||
1388 | /* Set registering flag.. */ | |
1389 | user->modes |= FLAGS_REGISTERING; | |
1390 | ||
1391 | if (nickserv_conf.sync_log) { | |
1392 | cryptpass(password, syncpass); | |
1393 | /* | |
1394 | * An 0 is only sent if theres no email address. Thios should only happen if email functions are | |
1395 | * disabled which they wont be for us. Email Required MUST be set on if you are using this. | |
1396 | * -SiRVulcaN | |
1397 | */ | |
1398 | SyncLog("REGISTER %s %s %s %s", hi->handle, syncpass, email_addr ? email_addr : "0", user->info); | |
1399 | } | |
1400 | ||
1401 | /* this wont work if email is required .. */ | |
1402 | process_adduser_pending(user); | |
1403 | ||
1404 | return 1; | |
1405 | } | |
1406 | ||
1407 | static NICKSERV_FUNC(cmd_oregister) | |
1408 | { | |
1409 | struct userNode *settee = NULL; | |
1410 | struct handle_info *hi; | |
1411 | char* account = NULL; | |
1412 | char* pass = NULL; | |
1413 | char* email = NULL; | |
1414 | char* mask = NULL; | |
1415 | char* nick = NULL; | |
1416 | ||
1417 | NICKSERV_MIN_PARMS(3); | |
1418 | ||
1419 | account = argv[1]; | |
1420 | pass = argv[2]; | |
1421 | if (nickserv_conf.email_required) { | |
1422 | NICKSERV_MIN_PARMS(4); | |
1423 | email = argv[3]; | |
1424 | if (argc >= 5) {/* take: "acct pass email mask nick" or "acct pass email mask" or "acct pass email nick" */ | |
1425 | if (strchr(argv[4], '@') || argc >= 6) /* If @, its mask not nick */ | |
1426 | mask = argv[4]; | |
1427 | else | |
1428 | nick = argv[4]; | |
1429 | } | |
1430 | if (argc >= 6) { | |
1431 | nick = argv[5]; | |
1432 | } | |
1433 | } | |
1434 | else { | |
1435 | if (argc >= 4) {/* take: "account pass mask nick" or "account pass mask" or "account pass nick" */ | |
1436 | if (strchr(argv[3], '@') || argc >= 5) /* If @, its mask not nick */ | |
1437 | mask = argv[3]; | |
1438 | else | |
1439 | nick = argv[3]; | |
1440 | } | |
1441 | if (argc >= 5) { | |
1442 | nick = argv[4]; | |
1443 | } | |
1444 | } | |
1445 | /* If they passed a nick, look for that user.. */ | |
1446 | if (nick && !(settee = GetUserH(nick))) { | |
1447 | reply("MSG_NICK_UNKNOWN", argv[4]); | |
1448 | return 0; | |
1449 | } | |
1450 | /* If the setee is already authed, we cant add a 2nd account for them.. */ | |
1451 | if (settee && settee->handle_info) { | |
1452 | reply("NSMSG_USER_PREV_AUTH", settee->nick); | |
1453 | return 0; | |
1454 | } | |
1455 | /* If there is no default mask in the conf, and they didn't pass a mask, | |
1456 | * but we did find a user by nick, generate the mask */ | |
1457 | if (!mask) { | |
1458 | if (nickserv_conf.default_hostmask) | |
1459 | mask = "*@*"; | |
1460 | else if (settee) | |
1461 | mask = generate_hostmask(settee, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT); | |
1462 | else { | |
1463 | reply("NSMSG_REGISTER_BAD_NICKMASK"); | |
1464 | return 0; | |
1465 | } | |
1466 | } | |
1467 | ||
1468 | if (!(hi = nickserv_register(user, settee, account, pass, 0))) { | |
1469 | return 0; /* error reply handled by above */ | |
1470 | } | |
1471 | if (email) { | |
1472 | nickserv_set_email_addr(hi, email); | |
1473 | } | |
1474 | if (mask) { | |
1475 | char* mask_canonicalized = canonicalize_hostmask(strdup(mask)); | |
1476 | string_list_append(hi->masks, mask_canonicalized); | |
1477 | } | |
1478 | ||
1479 | if (nickserv_conf.sync_log) | |
1480 | SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, email ? email : "@", user->info); /* Send just @ for email if none */ | |
1481 | return 1; | |
1482 | } | |
1483 | ||
1484 | static int | |
1485 | nickserv_ignore(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, char *mask) | |
1486 | { | |
1487 | unsigned int i; | |
1488 | struct userNode *target; | |
1489 | char *new_mask = strdup(pretty_mask(mask)); | |
1490 | for (i=0; i<hi->ignores->used; i++) { | |
1491 | if (!irccasecmp(new_mask, hi->ignores->list[i])) { | |
1492 | reply("NSMSG_ADDIGNORE_ALREADY", new_mask); | |
1493 | free(new_mask); | |
1494 | return 0; | |
1495 | } | |
1496 | } | |
1497 | string_list_append(hi->ignores, new_mask); | |
1498 | reply("NSMSG_ADDIGNORE_SUCCESS", new_mask); | |
1499 | ||
1500 | for (target = hi->users; target; target = target->next_authed) { | |
1501 | irc_silence(target, new_mask, 1); | |
1502 | } | |
1503 | return 1; | |
1504 | } | |
1505 | ||
1506 | static NICKSERV_FUNC(cmd_addignore) | |
1507 | { | |
1508 | NICKSERV_MIN_PARMS(2); | |
1509 | ||
1510 | return nickserv_ignore(cmd, user, user->handle_info, argv[1]); | |
1511 | } | |
1512 | ||
1513 | static NICKSERV_FUNC(cmd_oaddignore) | |
1514 | { | |
1515 | struct handle_info *hi; | |
1516 | ||
1517 | NICKSERV_MIN_PARMS(3); | |
1518 | if (!(hi = get_victim_oper(cmd, user, argv[1]))) | |
1519 | return 0; | |
1520 | ||
1521 | return nickserv_ignore(cmd, user, hi, argv[2]); | |
1522 | } | |
1523 | ||
1524 | static int | |
1525 | nickserv_delignore(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, char *del_mask) | |
1526 | { | |
1527 | unsigned int i; | |
1528 | struct userNode *target; | |
1529 | char *pmask = strdup(pretty_mask(del_mask)); | |
1530 | for (i=0; i<hi->ignores->used; i++) { | |
1531 | if (!strcmp(pmask, hi->ignores->list[i]) || !strcmp(del_mask, hi->ignores->list[i])) { | |
1532 | char *old_mask = hi->ignores->list[i]; | |
1533 | hi->ignores->list[i] = hi->ignores->list[--hi->ignores->used]; | |
1534 | reply("NSMSG_DELMASK_SUCCESS", old_mask); | |
1535 | for (target = hi->users; target; target = target->next_authed) { | |
1536 | irc_silence(target, old_mask, 0); | |
1537 | } | |
1538 | free(old_mask); | |
1539 | free(pmask); | |
1540 | return 1; | |
1541 | } | |
1542 | } | |
1543 | reply("NSMSG_DELMASK_NOT_FOUND"); | |
1544 | return 0; | |
1545 | } | |
1546 | ||
1547 | static NICKSERV_FUNC(cmd_delignore) | |
1548 | { | |
1549 | NICKSERV_MIN_PARMS(2); | |
1550 | return nickserv_delignore(cmd, user, user->handle_info, argv[1]); | |
1551 | } | |
1552 | ||
1553 | static NICKSERV_FUNC(cmd_odelignore) | |
1554 | { | |
1555 | struct handle_info *hi; | |
1556 | NICKSERV_MIN_PARMS(3); | |
1557 | if (!(hi = get_victim_oper(cmd, user, argv[1]))) | |
1558 | return 0; | |
1559 | return nickserv_delignore(cmd, user, hi, argv[2]); | |
1560 | } | |
1561 | ||
1562 | static NICKSERV_FUNC(cmd_handleinfo) | |
1563 | { | |
1564 | char buff[400]; | |
1565 | unsigned int i, pos=0, herelen; | |
1566 | struct userNode *target, *next_un; | |
1567 | struct handle_info *hi; | |
1568 | const char *nsmsg_none; | |
1569 | ||
1570 | if (argc < 2) { | |
1571 | if (!(hi = user->handle_info)) { | |
1572 | reply("NSMSG_MUST_AUTH"); | |
1573 | return 0; | |
1574 | } | |
1575 | } else if (!(hi = modcmd_get_handle_info(user, argv[1]))) { | |
1576 | return 0; | |
1577 | } | |
1578 | ||
1579 | nsmsg_none = handle_find_message(hi, "MSG_NONE"); | |
1580 | reply("NSMSG_HANDLEINFO_ON", hi->handle); | |
1581 | reply("MSG_BAR"); | |
1582 | #ifdef WITH_PROTOCOL_BAHAMUT | |
1583 | reply("NSMSG_HANDLEINFO_ID", hi->id); | |
1584 | #endif | |
1585 | reply("NSMSG_HANDLEINFO_REGGED", ctime(&hi->registered)); | |
1586 | ||
1587 | if (!hi->users) { | |
1588 | intervalString(buff, now - hi->lastseen, user->handle_info); | |
1589 | reply("NSMSG_HANDLEINFO_LASTSEEN", buff); | |
1590 | } else { | |
1591 | reply("NSMSG_HANDLEINFO_LASTSEEN_NOW"); | |
1592 | } | |
1593 | ||
1594 | reply("NSMSG_HANDLEINFO_INFOLINE", (hi->infoline ? hi->infoline : nsmsg_none)); | |
1595 | if (HANDLE_FLAGGED(hi, FROZEN)) | |
1596 | reply("NSMSG_HANDLEINFO_VACATION"); | |
1597 | ||
1598 | if (oper_has_access(user, cmd->parent->bot, 0, 1)) { | |
1599 | struct do_not_register *dnr; | |
1600 | if ((dnr = chanserv_is_dnr(NULL, hi))) | |
1601 | reply("NSMSG_HANDLEINFO_DNR", dnr->setter, dnr->reason); | |
1602 | if (!oper_outranks(cmd, user, hi)) | |
1603 | return 1; | |
1604 | } else if (hi != user->handle_info) { | |
1605 | reply("NSMSG_HANDLEINFO_END"); | |
1606 | return 1; | |
1607 | } | |
1608 | ||
1609 | if (nickserv_conf.email_enabled) | |
1610 | reply("NSMSG_HANDLEINFO_EMAIL_ADDR", visible_email_addr(user, hi)); | |
1611 | ||
1612 | if (hi->cookie) { | |
1613 | const char *type; | |
1614 | switch (hi->cookie->type) { | |
1615 | case ACTIVATION: type = "NSMSG_HANDLEINFO_COOKIE_ACTIVATION"; break; | |
1616 | case PASSWORD_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_PASSWORD"; break; | |
1617 | case EMAIL_CHANGE: type = "NSMSG_HANDLEINFO_COOKIE_EMAIL"; break; | |
1618 | case ALLOWAUTH: type = "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH"; break; | |
1619 | default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break; | |
1620 | } | |
1621 | reply(type); | |
1622 | } | |
1623 | ||
1624 | if (hi->flags) { | |
1625 | unsigned long flen = 1; | |
1626 | char flags[34]; /* 32 bits possible plus '+' and '\0' */ | |
1627 | flags[0] = '+'; | |
1628 | for (i=0, flen=1; handle_flags[i]; i++) | |
1629 | if (hi->flags & 1 << i) | |
1630 | flags[flen++] = handle_flags[i]; | |
1631 | flags[flen] = 0; | |
1632 | reply("NSMSG_HANDLEINFO_FLAGS", flags); | |
1633 | } else { | |
1634 | reply("NSMSG_HANDLEINFO_FLAGS", nsmsg_none); | |
1635 | } | |
1636 | ||
1637 | if (HANDLE_FLAGGED(hi, SUPPORT_HELPER) | |
1638 | || HANDLE_FLAGGED(hi, NETWORK_HELPER) | |
1639 | || (hi->opserv_level > 0)) { | |
1640 | reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none)); | |
1641 | } | |
1642 | ||
1643 | if (IsHelping(user) || IsOper(user)) | |
1644 | { | |
1645 | if (hi->note) | |
1646 | { | |
1647 | char date[64]; | |
1648 | strftime(date, 64, "%b %d %Y", localtime(&hi->note->date)); | |
1649 | reply("NSMSG_HANDLEINFO_NOTE", hi->note->setter, date, hi->note->note); | |
1650 | } | |
1651 | } | |
1652 | ||
1653 | if (hi->fakehost) | |
1654 | reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE"))); | |
1655 | ||
1656 | if (hi->last_quit_host[0]) | |
1657 | reply("NSMSG_HANDLEINFO_LAST_HOST", hi->last_quit_host); | |
1658 | else | |
1659 | reply("NSMSG_HANDLEINFO_LAST_HOST_UNKNOWN"); | |
1660 | ||
1661 | if (nickserv_conf.disable_nicks) { | |
1662 | /* nicks disabled; don't show anything about registered nicks */ | |
1663 | } else if (hi->nicks) { | |
1664 | struct nick_info *ni, *next_ni; | |
1665 | for (ni = hi->nicks; ni; ni = next_ni) { | |
1666 | herelen = strlen(ni->nick); | |
1667 | if (pos + herelen + 1 > ArrayLength(buff)) { | |
1668 | next_ni = ni; | |
1669 | goto print_nicks_buff; | |
1670 | } else { | |
1671 | next_ni = ni->next; | |
1672 | } | |
1673 | memcpy(buff+pos, ni->nick, herelen); | |
1674 | pos += herelen; buff[pos++] = ' '; | |
1675 | if (!next_ni) { | |
1676 | print_nicks_buff: | |
1677 | buff[pos-1] = 0; | |
1678 | reply("NSMSG_HANDLEINFO_NICKS", buff); | |
1679 | pos = 0; | |
1680 | } | |
1681 | } | |
1682 | } else { | |
1683 | reply("NSMSG_HANDLEINFO_NICKS", nsmsg_none); | |
1684 | } | |
1685 | ||
1686 | if (hi->masks->used) { | |
1687 | for (i=0; i < hi->masks->used; i++) { | |
1688 | herelen = strlen(hi->masks->list[i]); | |
1689 | if (pos + herelen + 1 > ArrayLength(buff)) { | |
1690 | i--; | |
1691 | goto print_mask_buff; | |
1692 | } | |
1693 | memcpy(buff+pos, hi->masks->list[i], herelen); | |
1694 | pos += herelen; buff[pos++] = ' '; | |
1695 | if (i+1 == hi->masks->used) { | |
1696 | print_mask_buff: | |
1697 | buff[pos-1] = 0; | |
1698 | reply("NSMSG_HANDLEINFO_MASKS", buff); | |
1699 | pos = 0; | |
1700 | } | |
1701 | } | |
1702 | } else { | |
1703 | reply("NSMSG_HANDLEINFO_MASKS", nsmsg_none); | |
1704 | } | |
1705 | ||
1706 | if (hi->ignores->used) { | |
1707 | for (i=0; i < hi->ignores->used; i++) { | |
1708 | herelen = strlen(hi->ignores->list[i]); | |
1709 | if (pos + herelen + 1 > ArrayLength(buff)) { | |
1710 | i--; | |
1711 | goto print_ignore_buff; | |
1712 | } | |
1713 | memcpy(buff+pos, hi->ignores->list[i], herelen); | |
1714 | pos += herelen; buff[pos++] = ' '; | |
1715 | if (i+1 == hi->ignores->used) { | |
1716 | print_ignore_buff: | |
1717 | buff[pos-1] = 0; | |
1718 | reply("NSMSG_HANDLEINFO_IGNORES", buff); | |
1719 | pos = 0; | |
1720 | } | |
1721 | } | |
1722 | } else { | |
1723 | reply("NSMSG_HANDLEINFO_IGNORES", nsmsg_none); | |
1724 | } | |
1725 | ||
1726 | if (hi->channels) { | |
1727 | struct userData *channel, *next; | |
1728 | char *name; | |
1729 | ||
1730 | for (channel = hi->channels; channel; channel = next) { | |
1731 | next = channel->u_next; | |
1732 | name = channel->channel->channel->name; | |
1733 | herelen = strlen(name); | |
1734 | if (pos + herelen + 7 > ArrayLength(buff)) { | |
1735 | next = channel; | |
1736 | goto print_chans_buff; | |
1737 | } | |
1738 | if (IsUserSuspended(channel)) | |
1739 | buff[pos++] = '-'; | |
1740 | pos += sprintf(buff+pos, "%s:%s ", user_level_name_from_level(channel->access), name); | |
1741 | if (next == NULL) { | |
1742 | print_chans_buff: | |
1743 | buff[pos-1] = 0; | |
1744 | reply("NSMSG_HANDLEINFO_CHANNELS", buff); | |
1745 | pos = 0; | |
1746 | } | |
1747 | } | |
1748 | } else { | |
1749 | reply("NSMSG_HANDLEINFO_CHANNELS", nsmsg_none); | |
1750 | } | |
1751 | ||
1752 | for (target = hi->users; target; target = next_un) { | |
1753 | herelen = strlen(target->nick); | |
1754 | if (pos + herelen + 1 > ArrayLength(buff)) { | |
1755 | next_un = target; | |
1756 | goto print_cnick_buff; | |
1757 | } else { | |
1758 | next_un = target->next_authed; | |
1759 | } | |
1760 | memcpy(buff+pos, target->nick, herelen); | |
1761 | pos += herelen; buff[pos++] = ' '; | |
1762 | if (!next_un) { | |
1763 | print_cnick_buff: | |
1764 | buff[pos-1] = 0; | |
1765 | reply("NSMSG_HANDLEINFO_CURRENT", buff); | |
1766 | pos = 0; | |
1767 | } | |
1768 | } | |
1769 | ||
1770 | reply("NSMSG_HANDLEINFO_END"); | |
1771 | return 1; | |
1772 | } | |
1773 | ||
1774 | static NICKSERV_FUNC(cmd_userinfo) | |
1775 | { | |
1776 | struct userNode *target; | |
1777 | ||
1778 | NICKSERV_MIN_PARMS(2); | |
1779 | if (!(target = GetUserH(argv[1]))) { | |
1780 | reply("MSG_NICK_UNKNOWN", argv[1]); | |
1781 | return 0; | |
1782 | } | |
1783 | if (target->handle_info) | |
1784 | reply("NSMSG_USERINFO_AUTHED_AS", target->nick, target->handle_info->handle); | |
1785 | else | |
1786 | reply("NSMSG_USERINFO_NOT_AUTHED", target->nick); | |
1787 | return 1; | |
1788 | } | |
1789 | ||
1790 | static NICKSERV_FUNC(cmd_nickinfo) | |
1791 | { | |
1792 | struct nick_info *ni; | |
1793 | ||
1794 | NICKSERV_MIN_PARMS(2); | |
1795 | if (!(ni = get_nick_info(argv[1]))) { | |
1796 | reply("MSG_NICK_UNKNOWN", argv[1]); | |
1797 | return 0; | |
1798 | } | |
1799 | reply("NSMSG_NICKINFO_OWNER", ni->nick, ni->owner->handle); | |
1800 | return 1; | |
1801 | } | |
1802 | ||
1803 | static NICKSERV_FUNC(cmd_rename_handle) | |
1804 | { | |
1805 | struct handle_info *hi; | |
1806 | struct userNode *uNode; | |
1807 | char msgbuf[MAXLEN], *old_handle; | |
1808 | unsigned int nn; | |
1809 | ||
1810 | NICKSERV_MIN_PARMS(3); | |
1811 | if (!(hi = get_victim_oper(cmd, user, argv[1]))) | |
1812 | return 0; | |
1813 | if (!is_valid_handle(argv[2])) { | |
1814 | reply("NSMSG_FAIL_RENAME", argv[1], argv[2]); | |
1815 | return 0; | |
1816 | } | |
1817 | if (get_handle_info(argv[2])) { | |
1818 | reply("NSMSG_HANDLE_EXISTS", argv[2]); | |
1819 | return 0; | |
1820 | } | |
1821 | if(strlen(argv[2]) > 15) | |
1822 | { | |
1823 | reply("NMSG_HANDLE_TOLONG", argv[2], 15); | |
1824 | return 0; | |
1825 | } | |
1826 | ||
1827 | dict_remove2(nickserv_handle_dict, old_handle = hi->handle, 1); | |
1828 | hi->handle = strdup(argv[2]); | |
1829 | dict_insert(nickserv_handle_dict, hi->handle, hi); | |
1830 | for (nn=0; nn<rf_list_used; nn++) | |
1831 | rf_list[nn](hi, old_handle); | |
1832 | snprintf(msgbuf, sizeof(msgbuf), "%s renamed account %s to %s.", user->handle_info->handle, old_handle, hi->handle); | |
1833 | ||
1834 | ||
1835 | if (nickserv_conf.sync_log) { | |
1836 | for (uNode = hi->users; uNode; uNode = uNode->next_authed) | |
1837 | irc_rename(uNode, hi->handle); | |
1838 | ||
1839 | SyncLog("RENAME %s %s", old_handle, hi->handle); | |
1840 | } | |
1841 | ||
1842 | reply("NSMSG_HANDLE_CHANGED", old_handle, hi->handle); | |
1843 | global_message(MESSAGE_RECIPIENT_STAFF, msgbuf); | |
1844 | free(old_handle); | |
1845 | return 1; | |
1846 | } | |
1847 | ||
1848 | static failpw_func_t *failpw_func_list; | |
1849 | static unsigned int failpw_func_size = 0, failpw_func_used = 0; | |
1850 | ||
1851 | void | |
1852 | reg_failpw_func(failpw_func_t func) | |
1853 | { | |
1854 | if (failpw_func_used == failpw_func_size) { | |
1855 | if (failpw_func_size) { | |
1856 | failpw_func_size <<= 1; | |
1857 | failpw_func_list = realloc(failpw_func_list, failpw_func_size*sizeof(failpw_func_t)); | |
1858 | } else { | |
1859 | failpw_func_size = 8; | |
1860 | failpw_func_list = malloc(failpw_func_size*sizeof(failpw_func_t)); | |
1861 | } | |
1862 | } | |
1863 | failpw_func_list[failpw_func_used++] = func; | |
1864 | } | |
1865 | ||
1866 | /* | |
1867 | * Return hi if the handle/pass pair matches, NULL if it doesnt. | |
1868 | * | |
1869 | * called by nefariouses enhanced AC login-on-connect code | |
1870 | * | |
1871 | */ | |
1872 | struct handle_info *loc_auth(char *handle, char *password) | |
1873 | { | |
1874 | int pw_arg, used, maxlogins; | |
1875 | unsigned int ii; | |
1876 | int wildmask = 0; | |
1877 | struct handle_info *hi; | |
1878 | struct userNode *other; | |
1879 | ||
1880 | hi = dict_find(nickserv_handle_dict, handle, NULL); | |
1881 | pw_arg = 2; | |
1882 | if (!hi) { | |
1883 | return NULL; | |
1884 | } | |
1885 | ||
1886 | /* We don't know the users hostname, or anything because they | |
1887 | * havn't registered yet. So we can only allow LOC if your | |
1888 | * account has *@* as a hostmask. | |
1889 | */ | |
1890 | for (ii=0; ii<hi->masks->used; ii++) | |
1891 | { | |
1892 | if (!strcmp(hi->masks->list[ii], "*@*")) | |
1893 | { | |
1894 | wildmask++; | |
1895 | break; | |
1896 | } | |
1897 | } | |
1898 | if(wildmask < 1) | |
1899 | return NULL; | |
1900 | ||
1901 | /* Responses from here on look up the language used by the handle they asked about. */ | |
1902 | if (!checkpass(password, hi->passwd)) { | |
1903 | return NULL; | |
1904 | } | |
1905 | if (HANDLE_FLAGGED(hi, SUSPENDED)) { | |
1906 | return NULL; | |
1907 | } | |
1908 | maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins; | |
1909 | for (used = 0, other = hi->users; other; other = other->next_authed) { | |
1910 | if (++used >= maxlogins) { | |
1911 | return NULL; | |
1912 | } | |
1913 | } | |
1914 | return hi; | |
1915 | } | |
1916 | ||
1917 | static NICKSERV_FUNC(cmd_auth) | |
1918 | { | |
1919 | int pw_arg, used, maxlogins; | |
1920 | struct handle_info *hi; | |
1921 | const char *passwd; | |
1922 | struct userNode *other; | |
1923 | ||
1924 | if (user->handle_info) { | |
1925 | reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle); | |
1926 | return 0; | |
1927 | } | |
1928 | if (IsStamped(user)) { | |
1929 | /* Unauthenticated users might still have been stamped | |
1930 | previously and could therefore have a hidden host; | |
1931 | do not allow them to authenticate. */ | |
1932 | reply("NSMSG_STAMPED_AUTH"); | |
1933 | return 0; | |
1934 | } | |
1935 | if (argc == 3) { | |
1936 | hi = dict_find(nickserv_handle_dict, argv[1], NULL); | |
1937 | pw_arg = 2; | |
1938 | } else if (argc == 2) { | |
1939 | if (nickserv_conf.disable_nicks) { | |
1940 | if (!(hi = get_handle_info(user->nick))) { | |
1941 | reply("NSMSG_HANDLE_NOT_FOUND"); | |
1942 | return 0; | |
1943 | } | |
1944 | } else { | |
1945 | /* try to look up their handle from their nick */ | |
1946 | struct nick_info *ni; | |
1947 | ni = get_nick_info(user->nick); | |
1948 | if (!ni) { | |
1949 | reply("NSMSG_NICK_NOT_REGISTERED", user->nick); | |
1950 | return 0; | |
1951 | } | |
1952 | hi = ni->owner; | |
1953 | } | |
1954 | pw_arg = 1; | |
1955 | } else { | |
1956 | reply("MSG_MISSING_PARAMS", argv[0]); | |
1957 | svccmd_send_help_brief(user, nickserv, cmd); | |
1958 | return 0; | |
1959 | } | |
1960 | if (!hi) { | |
1961 | reply("NSMSG_HANDLE_NOT_FOUND"); | |
1962 | return 0; | |
1963 | } | |
1964 | /* Responses from here on look up the language used by the handle they asked about. */ | |
1965 | passwd = argv[pw_arg]; | |
1966 | if (!valid_user_for(user, hi)) { | |
1967 | if (hi->email_addr && nickserv_conf.email_enabled) | |
1968 | send_message_type(4, user, cmd->parent->bot, | |
1969 | handle_find_message(hi, "NSMSG_USE_AUTHCOOKIE"), | |
1970 | hi->handle); | |
1971 | else | |
1972 | send_message_type(4, user, cmd->parent->bot, | |
1973 | handle_find_message(hi, "NSMSG_HOSTMASK_INVALID"), | |
1974 | hi->handle); | |
1975 | argv[pw_arg] = "BADMASK"; | |
1976 | return 1; | |
1977 | } | |
1978 | if (!checkpass(passwd, hi->passwd)) { | |
1979 | unsigned int n; | |
1980 | send_message_type(4, user, cmd->parent->bot, | |
1981 | handle_find_message(hi, "NSMSG_PASSWORD_INVALID")); | |
1982 | argv[pw_arg] = "BADPASS"; | |
1983 | for (n=0; n<failpw_func_used; n++) failpw_func_list[n](user, hi); | |
1984 | if (nickserv_conf.autogag_enabled) { | |
1985 | if (!user->auth_policer.params) { | |
1986 | user->auth_policer.last_req = now; | |
1987 | user->auth_policer.params = nickserv_conf.auth_policer_params; | |
1988 | } | |
1989 | if (!policer_conforms(&user->auth_policer, now, 1.0)) { | |
1990 | char *hostmask; | |
1991 | hostmask = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_BYIP|GENMASK_NO_HIDING); | |
1992 | log_module(NS_LOG, LOG_INFO, "%s auto-gagged for repeated password guessing.", hostmask); | |
1993 | gag_create(hostmask, nickserv->nick, "Repeated password guessing.", now+nickserv_conf.autogag_duration); | |
1994 | free(hostmask); | |
1995 | argv[pw_arg] = "GAGGED"; | |
1996 | } | |
1997 | } | |
1998 | return 1; | |
1999 | } | |
2000 | if (HANDLE_FLAGGED(hi, SUSPENDED)) { | |
2001 | send_message_type(4, user, cmd->parent->bot, | |
2002 | handle_find_message(hi, "NSMSG_HANDLE_SUSPENDED")); | |
2003 | argv[pw_arg] = "SUSPENDED"; | |
2004 | return 1; | |
2005 | } | |
2006 | maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins; | |
2007 | for (used = 0, other = hi->users; other; other = other->next_authed) { | |
2008 | if (++used >= maxlogins) { | |
2009 | send_message_type(4, user, cmd->parent->bot, | |
2010 | handle_find_message(hi, "NSMSG_MAX_LOGINS"), | |
2011 | maxlogins); | |
2012 | argv[pw_arg] = "MAXLOGINS"; | |
2013 | return 1; | |
2014 | } | |
2015 | } | |
2016 | ||
2017 | set_user_handle_info(user, hi, 1); | |
2018 | if (nickserv_conf.email_required && !hi->email_addr) | |
2019 | reply("NSMSG_PLEASE_SET_EMAIL"); | |
2020 | if (!is_secure_password(hi->handle, passwd, NULL)) | |
2021 | reply("NSMSG_WEAK_PASSWORD"); | |
2022 | if (hi->passwd[0] != '$') | |
2023 | cryptpass(passwd, hi->passwd); | |
2024 | ||
2025 | /* If a channel was waiting for this user to auth, | |
2026 | * finish adding them */ | |
2027 | process_adduser_pending(user); | |
2028 | ||
2029 | reply("NSMSG_AUTH_SUCCESS"); | |
2030 | ||
2031 | ||
2032 | /* Set +x if autohide is on */ | |
2033 | if(HANDLE_FLAGGED(hi, AUTOHIDE)) | |
2034 | irc_umode(user, "+x"); | |
2035 | ||
2036 | if(!IsOper(user)) /* If they arnt already opered.. */ | |
2037 | { | |
2038 | /* Auto Oper users with Opserv access -Life4Christ 8-10-2005 */ | |
2039 | if( nickserv_conf.auto_admin[0] && hi->opserv_level >= opserv_conf_admin_level()) | |
2040 | { | |
2041 | irc_umode(user,nickserv_conf.auto_admin); | |
2042 | reply("NSMSG_AUTO_OPER_ADMIN"); | |
2043 | } | |
2044 | else if (nickserv_conf.auto_oper[0] && hi->opserv_level > 0) | |
2045 | { | |
2046 | irc_umode(user,nickserv_conf.auto_oper); | |
2047 | reply("NSMSG_AUTO_OPER"); | |
2048 | } | |
2049 | } | |
2050 | ||
2051 | /* Wipe out the pass for the logs */ | |
2052 | argv[pw_arg] = "****"; | |
2053 | return 1; | |
2054 | } | |
2055 | ||
2056 | static allowauth_func_t *allowauth_func_list; | |
2057 | static unsigned int allowauth_func_size = 0, allowauth_func_used = 0; | |
2058 | ||
2059 | void | |
2060 | reg_allowauth_func(allowauth_func_t func) | |
2061 | { | |
2062 | if (allowauth_func_used == allowauth_func_size) { | |
2063 | if (allowauth_func_size) { | |
2064 | allowauth_func_size <<= 1; | |
2065 | allowauth_func_list = realloc(allowauth_func_list, allowauth_func_size*sizeof(allowauth_func_t)); | |
2066 | } else { | |
2067 | allowauth_func_size = 8; | |
2068 | allowauth_func_list = malloc(allowauth_func_size*sizeof(allowauth_func_t)); | |
2069 | } | |
2070 | } | |
2071 | allowauth_func_list[allowauth_func_used++] = func; | |
2072 | } | |
2073 | ||
2074 | static NICKSERV_FUNC(cmd_allowauth) | |
2075 | { | |
2076 | struct userNode *target; | |
2077 | struct handle_info *hi; | |
2078 | unsigned int n; | |
2079 | ||
2080 | NICKSERV_MIN_PARMS(2); | |
2081 | if (!(target = GetUserH(argv[1]))) { | |
2082 | reply("MSG_NICK_UNKNOWN", argv[1]); | |
2083 | return 0; | |
2084 | } | |
2085 | if (target->handle_info) { | |
2086 | reply("NSMSG_USER_PREV_AUTH", target->nick); | |
2087 | return 0; | |
2088 | } | |
2089 | if (IsStamped(target)) { | |
2090 | /* Unauthenticated users might still have been stamped | |
2091 | previously and could therefore have a hidden host; | |
2092 | do not allow them to authenticate to an account. */ | |
2093 | reply("NSMSG_USER_PREV_STAMP", target->nick); | |
2094 | return 0; | |
2095 | } | |
2096 | if (argc == 2) | |
2097 | hi = NULL; | |
2098 | else if (!(hi = get_handle_info(argv[2]))) { | |
2099 | reply("MSG_HANDLE_UNKNOWN", argv[2]); | |
2100 | return 0; | |
2101 | } | |
2102 | if (hi) { | |
2103 | if (hi->opserv_level > user->handle_info->opserv_level) { | |
2104 | reply("MSG_USER_OUTRANKED", hi->handle); | |
2105 | return 0; | |
2106 | } | |
2107 | if (((hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER)) | |
2108 | || (hi->opserv_level > 0)) | |
2109 | && ((argc < 4) || irccasecmp(argv[3], "staff"))) { | |
2110 | reply("NSMSG_ALLOWAUTH_STAFF", hi->handle); | |
2111 | return 0; | |
2112 | } | |
2113 | dict_insert(nickserv_allow_auth_dict, target->nick, hi); | |
2114 | reply("NSMSG_AUTH_ALLOWED", target->nick, hi->handle); | |
2115 | send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_MSG", hi->handle, hi->handle); | |
2116 | if (nickserv_conf.email_enabled) | |
2117 | send_message(target, nickserv, "NSMSG_AUTH_ALLOWED_EMAIL"); | |
2118 | } else { | |
2119 | if (dict_remove(nickserv_allow_auth_dict, target->nick)) | |
2120 | reply("NSMSG_AUTH_NORMAL_ONLY", target->nick); | |
2121 | else | |
2122 | reply("NSMSG_AUTH_UNSPECIAL", target->nick); | |
2123 | } | |
2124 | for (n=0; n<allowauth_func_used; n++) | |
2125 | allowauth_func_list[n](user, target, hi); | |
2126 | return 1; | |
2127 | } | |
2128 | ||
2129 | static NICKSERV_FUNC(cmd_authcookie) | |
2130 | { | |
2131 | struct handle_info *hi; | |
2132 | ||
2133 | NICKSERV_MIN_PARMS(2); | |
2134 | if (user->handle_info) { | |
2135 | reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle); | |
2136 | return 0; | |
2137 | } | |
2138 | if (IsStamped(user)) { | |
2139 | /* Unauthenticated users might still have been stamped | |
2140 | previously and could therefore have a hidden host; | |
2141 | do not allow them to authenticate to an account. */ | |
2142 | reply("NSMSG_STAMPED_AUTHCOOKIE"); | |
2143 | return 0; | |
2144 | } | |
2145 | if (!(hi = get_handle_info(argv[1]))) { | |
2146 | reply("MSG_HANDLE_UNKNOWN", argv[1]); | |
2147 | return 0; | |
2148 | } | |
2149 | if (!hi->email_addr) { | |
2150 | reply("MSG_SET_EMAIL_ADDR"); | |
2151 | return 0; | |
2152 | } | |
2153 | nickserv_make_cookie(user, hi, ALLOWAUTH, NULL, 0); | |
2154 | return 1; | |
2155 | } | |
2156 | ||
2157 | static NICKSERV_FUNC(cmd_delcookie) | |
2158 | { | |
2159 | struct handle_info *hi; | |
2160 | ||
2161 | hi = user->handle_info; | |
2162 | if (!hi->cookie) { | |
2163 | reply("NSMSG_NO_COOKIE"); | |
2164 | return 0; | |
2165 | } | |
2166 | switch (hi->cookie->type) { | |
2167 | case ACTIVATION: | |
2168 | case EMAIL_CHANGE: | |
2169 | reply("NSMSG_MUST_TIME_OUT"); | |
2170 | break; | |
2171 | default: | |
2172 | nickserv_eat_cookie(hi->cookie); | |
2173 | reply("NSMSG_ATE_COOKIE"); | |
2174 | break; | |
2175 | } | |
2176 | return 1; | |
2177 | } | |
2178 | ||
2179 | static NICKSERV_FUNC(cmd_odelcookie) | |
2180 | { | |
2181 | struct handle_info *hi; | |
2182 | ||
2183 | NICKSERV_MIN_PARMS(2); | |
2184 | ||
2185 | if (!(hi = get_victim_oper(cmd, user, argv[1]))) | |
2186 | return 0; | |
2187 | ||
2188 | if (!hi->cookie) { | |
2189 | reply("NSMSG_NO_COOKIE_FOREIGN", hi->handle); | |
2190 | return 0; | |
2191 | } | |
2192 | ||
2193 | nickserv_eat_cookie(hi->cookie); | |
2194 | reply("NSMSG_ATE_FOREIGN_COOKIE", hi->handle); | |
2195 | ||
2196 | return 1; | |
2197 | } | |
2198 | ||
2199 | static NICKSERV_FUNC(cmd_resetpass) | |
2200 | { | |
2201 | struct handle_info *hi; | |
2202 | char crypted[MD5_CRYPT_LENGTH]; | |
2203 | int weblink; | |
2204 | ||
2205 | NICKSERV_MIN_PARMS(3); | |
2206 | if(argc >= 4 && !strcmp(argv[3], "WEBLINK")) | |
2207 | weblink = 1; | |
2208 | else | |
2209 | weblink = 0; | |
2210 | if (user->handle_info) { | |
2211 | reply("NSMSG_ALREADY_AUTHED", user->handle_info->handle); | |
2212 | return 0; | |
2213 | } | |
2214 | if (IsStamped(user)) { | |
2215 | /* Unauthenticated users might still have been stamped | |
2216 | previously and could therefore have a hidden host; | |
2217 | do not allow them to activate an account. */ | |
2218 | reply("NSMSG_STAMPED_RESETPASS"); | |
2219 | return 0; | |
2220 | } | |
2221 | if (!(hi = get_handle_info(argv[1]))) { | |
2222 | reply("MSG_HANDLE_UNKNOWN", argv[1]); | |
2223 | return 0; | |
2224 | } | |
2225 | if (!hi->email_addr) { | |
2226 | reply("MSG_SET_EMAIL_ADDR"); | |
2227 | return 0; | |
2228 | } | |
2229 | cryptpass(argv[2], crypted); | |
2230 | argv[2] = "****"; | |
2231 | nickserv_make_cookie(user, hi, PASSWORD_CHANGE, crypted, weblink); | |
2232 | return 1; | |
2233 | } | |
2234 | ||
2235 | static NICKSERV_FUNC(cmd_cookie) | |
2236 | { | |
2237 | struct handle_info *hi; | |
2238 | const char *cookie; | |
2239 | ||
2240 | if ((argc == 2) && (hi = user->handle_info) && hi->cookie && (hi->cookie->type == EMAIL_CHANGE)) { | |
2241 | cookie = argv[1]; | |
2242 | } else { | |
2243 | NICKSERV_MIN_PARMS(3); | |
2244 | if (!(hi = get_handle_info(argv[1]))) { | |
2245 | reply("MSG_HANDLE_UNKNOWN", argv[1]); | |
2246 | return 0; | |
2247 | } | |
2248 | cookie = argv[2]; | |
2249 | } | |
2250 | ||
2251 | if (HANDLE_FLAGGED(hi, SUSPENDED)) { | |
2252 | reply("NSMSG_HANDLE_SUSPENDED"); | |
2253 | return 0; | |
2254 | } | |
2255 | ||
2256 | if (!hi->cookie) { | |
2257 | reply("NSMSG_NO_COOKIE"); | |
2258 | return 0; | |
2259 | } | |
2260 | ||
2261 | /* Check validity of operation before comparing cookie to | |
2262 | * prohibit guessing by authed users. */ | |
2263 | if (user->handle_info | |
2264 | && (hi->cookie->type != EMAIL_CHANGE) | |
2265 | && (hi->cookie->type != PASSWORD_CHANGE)) { | |
2266 | reply("NSMSG_CANNOT_COOKIE"); | |
2267 | return 0; | |
2268 | } | |
2269 | ||
2270 | if (strcmp(cookie, hi->cookie->cookie)) { | |
2271 | reply("NSMSG_BAD_COOKIE"); | |
2272 | return 0; | |
2273 | } | |
2274 | ||
2275 | switch (hi->cookie->type) { | |
2276 | case ACTIVATION: | |
2277 | safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd)); | |
2278 | set_user_handle_info(user, hi, 1); | |
2279 | reply("NSMSG_HANDLE_ACTIVATED"); | |
2280 | if (nickserv_conf.sync_log) | |
2281 | SyncLog("ACCOUNTACC %s", hi->handle); | |
2282 | break; | |
2283 | case PASSWORD_CHANGE: | |
2284 | set_user_handle_info(user, hi, 1); | |
2285 | safestrncpy(hi->passwd, hi->cookie->data, sizeof(hi->passwd)); | |
2286 | reply("NSMSG_PASSWORD_CHANGED"); | |
2287 | if (nickserv_conf.sync_log) | |
2288 | SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd); | |
2289 | break; | |
2290 | case EMAIL_CHANGE: | |
2291 | if (!hi->email_addr && nickserv_conf.sync_log) { | |
2292 | /* | |
2293 | * This should only happen if an OREGISTER was sent. Require | |
2294 | * email must be enabled! - SiRVulcaN | |
2295 | */ | |
2296 | if (nickserv_conf.sync_log) | |
2297 | SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, hi->cookie->data, user->info); | |
2298 | } | |
2299 | nickserv_set_email_addr(hi, hi->cookie->data); | |
2300 | reply("NSMSG_EMAIL_CHANGED"); | |
2301 | if (nickserv_conf.sync_log) | |
2302 | SyncLog("EMAILCHANGE %s %s", hi->handle, hi->cookie->data); | |
2303 | break; | |
2304 | case ALLOWAUTH: | |
2305 | set_user_handle_info(user, hi, 1); | |
2306 | reply("NSMSG_AUTH_SUCCESS"); | |
2307 | break; | |
2308 | default: | |
2309 | reply("NSMSG_BAD_COOKIE_TYPE", hi->cookie->type); | |
2310 | log_module(NS_LOG, LOG_ERROR, "Bad cookie type %d for account %s.", hi->cookie->type, hi->handle); | |
2311 | break; | |
2312 | } | |
2313 | ||
2314 | nickserv_eat_cookie(hi->cookie); | |
2315 | ||
2316 | process_adduser_pending(user); | |
2317 | ||
2318 | return 1; | |
2319 | } | |
2320 | ||
2321 | static NICKSERV_FUNC(cmd_oregnick) { | |
2322 | const char *nick; | |
2323 | struct handle_info *target; | |
2324 | struct nick_info *ni; | |
2325 | ||
2326 | NICKSERV_MIN_PARMS(3); | |
2327 | if (!(target = modcmd_get_handle_info(user, argv[1]))) | |
2328 | return 0; | |
2329 | nick = argv[2]; | |
2330 | if (!is_registerable_nick(nick)) { | |
2331 | reply("NSMSG_BAD_NICK", nick); | |
2332 | return 0; | |
2333 | } | |
2334 | ni = dict_find(nickserv_nick_dict, nick, NULL); | |
2335 | if (ni) { | |
2336 | reply("NSMSG_NICK_EXISTS", nick); | |
2337 | return 0; | |
2338 | } | |
2339 | register_nick(nick, target); | |
2340 | reply("NSMSG_OREGNICK_SUCCESS", nick, target->handle); | |
2341 | return 1; | |
2342 | } | |
2343 | ||
2344 | static NICKSERV_FUNC(cmd_regnick) { | |
2345 | unsigned n; | |
2346 | struct nick_info *ni; | |
2347 | ||
2348 | if (!is_registerable_nick(user->nick)) { | |
2349 | reply("NSMSG_BAD_NICK", user->nick); | |
2350 | return 0; | |
2351 | } | |
2352 | /* count their nicks, see if it's too many */ | |
2353 | for (n=0,ni=user->handle_info->nicks; ni; n++,ni=ni->next) ; | |
2354 | if (n >= nickserv_conf.nicks_per_handle) { | |
2355 | reply("NSMSG_TOO_MANY_NICKS"); | |
2356 | return 0; | |
2357 | } | |
2358 | ni = dict_find(nickserv_nick_dict, user->nick, NULL); | |
2359 | if (ni) { | |
2360 | reply("NSMSG_NICK_EXISTS", user->nick); | |
2361 | return 0; | |
2362 | } | |
2363 | register_nick(user->nick, user->handle_info); | |
2364 | reply("NSMSG_REGNICK_SUCCESS", user->nick); | |
2365 | return 1; | |
2366 | } | |
2367 | ||
2368 | static NICKSERV_FUNC(cmd_pass) | |
2369 | { | |
2370 | struct handle_info *hi; | |
2371 | const char *old_pass, *new_pass; | |
2372 | ||
2373 | NICKSERV_MIN_PARMS(3); | |
2374 | hi = user->handle_info; | |
2375 | old_pass = argv[1]; | |
2376 | new_pass = argv[2]; | |
2377 | argv[2] = "****"; | |
2378 | if (!is_secure_password(hi->handle, new_pass, user)) return 0; | |
2379 | if (!checkpass(old_pass, hi->passwd)) { | |
2380 | argv[1] = "BADPASS"; | |
2381 | reply("NSMSG_PASSWORD_INVALID"); | |
2382 | return 0; | |
2383 | } | |
2384 | cryptpass(new_pass, hi->passwd); | |
2385 | if (nickserv_conf.sync_log) | |
2386 | SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd); | |
2387 | argv[1] = "****"; | |
2388 | reply("NSMSG_PASS_SUCCESS"); | |
2389 | return 1; | |
2390 | } | |
2391 | ||
2392 | static int | |
2393 | nickserv_addmask(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, const char *mask) | |
2394 | { | |
2395 | unsigned int i; | |
2396 | char *new_mask = canonicalize_hostmask(strdup(mask)); | |
2397 | for (i=0; i<hi->masks->used; i++) { | |
2398 | if (!irccasecmp(new_mask, hi->masks->list[i])) { | |
2399 | reply("NSMSG_ADDMASK_ALREADY", new_mask); | |
2400 | free(new_mask); | |
2401 | return 0; | |
2402 | } | |
2403 | } | |
2404 | string_list_append(hi->masks, new_mask); | |
2405 | reply("NSMSG_ADDMASK_SUCCESS", new_mask); | |
2406 | return 1; | |
2407 | } | |
2408 | ||
2409 | static NICKSERV_FUNC(cmd_addmask) | |
2410 | { | |
2411 | if (argc < 2) { | |
2412 | char *mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT); | |
2413 | int res = nickserv_addmask(cmd, user, user->handle_info, mask); | |
2414 | free(mask); | |
2415 | return res; | |
2416 | } else { | |
2417 | if (!is_gline(argv[1])) { | |
2418 | reply("NSMSG_MASK_INVALID", argv[1]); | |
2419 | return 0; | |
2420 | } | |
2421 | return nickserv_addmask(cmd, user, user->handle_info, argv[1]); | |
2422 | } | |
2423 | } | |
2424 | ||
2425 | static NICKSERV_FUNC(cmd_oaddmask) | |
2426 | { | |
2427 | struct handle_info *hi; | |
2428 | ||
2429 | NICKSERV_MIN_PARMS(3); | |
2430 | if (!(hi = get_victim_oper(cmd, user, argv[1]))) | |
2431 | return 0; | |
2432 | return nickserv_addmask(cmd, user, hi, argv[2]); | |
2433 | } | |
2434 | ||
2435 | static int | |
2436 | nickserv_delmask(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, const char *del_mask) | |
2437 | { | |
2438 | unsigned int i; | |
2439 | for (i=0; i<hi->masks->used; i++) { | |
2440 | if (!strcmp(del_mask, hi->masks->list[i])) { | |
2441 | char *old_mask = hi->masks->list[i]; | |
2442 | if (hi->masks->used == 1) { | |
2443 | reply("NSMSG_DELMASK_NOTLAST"); | |
2444 | return 0; | |
2445 | } | |
2446 | hi->masks->list[i] = hi->masks->list[--hi->masks->used]; | |
2447 | reply("NSMSG_DELMASK_SUCCESS", old_mask); | |
2448 | free(old_mask); | |
2449 | return 1; | |
2450 | } | |
2451 | } | |
2452 | reply("NSMSG_DELMASK_NOT_FOUND"); | |
2453 | return 0; | |
2454 | } | |
2455 | ||
2456 | static NICKSERV_FUNC(cmd_delmask) | |
2457 | { | |
2458 | NICKSERV_MIN_PARMS(2); | |
2459 | return nickserv_delmask(cmd, user, user->handle_info, argv[1]); | |
2460 | } | |
2461 | ||
2462 | static NICKSERV_FUNC(cmd_odelmask) | |
2463 | { | |
2464 | struct handle_info *hi; | |
2465 | NICKSERV_MIN_PARMS(3); | |
2466 | if (!(hi = get_victim_oper(cmd, user, argv[1]))) | |
2467 | return 0; | |
2468 | return nickserv_delmask(cmd, user, hi, argv[2]); | |
2469 | } | |
2470 | ||
2471 | int | |
2472 | nickserv_modify_handle_flags(struct userNode *user, struct userNode *bot, const char *str, unsigned long *padded, unsigned long *premoved) { | |
2473 | unsigned int nn, add = 1, pos; | |
2474 | unsigned long added, removed, flag; | |
2475 | ||
2476 | for (added=removed=nn=0; str[nn]; nn++) { | |
2477 | switch (str[nn]) { | |
2478 | case '+': add = 1; break; | |
2479 | case '-': add = 0; break; | |
2480 | default: | |
2481 | if (!(pos = handle_inverse_flags[(unsigned char)str[nn]])) { | |
2482 | send_message(user, bot, "NSMSG_INVALID_FLAG", str[nn]); | |
2483 | return 0; | |
2484 | } | |
2485 | if (user && (user->handle_info->opserv_level < flag_access_levels[pos-1])) { | |
2486 | /* cheesy avoidance of looking up the flag name.. */ | |
2487 | send_message(user, bot, "NSMSG_FLAG_PRIVILEGED", str[nn]); | |
2488 | return 0; | |
2489 | } | |
2490 | flag = 1 << (pos - 1); | |
2491 | if (add) | |
2492 | added |= flag, removed &= ~flag; | |
2493 | else | |
2494 | removed |= flag, added &= ~flag; | |
2495 | break; | |
2496 | } | |
2497 | } | |
2498 | *padded = added; | |
2499 | *premoved = removed; | |
2500 | return 1; | |
2501 | } | |
2502 | ||
2503 | static int | |
2504 | nickserv_apply_flags(struct userNode *user, struct handle_info *hi, const char *flags) | |
2505 | { | |
2506 | unsigned long before, after, added, removed; | |
2507 | struct userNode *uNode; | |
2508 | ||
2509 | before = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER); | |
2510 | if (!nickserv_modify_handle_flags(user, nickserv, flags, &added, &removed)) | |
2511 | return 0; | |
2512 | hi->flags = (hi->flags | added) & ~removed; | |
2513 | after = hi->flags & (HI_FLAG_SUPPORT_HELPER|HI_FLAG_NETWORK_HELPER); | |
2514 | ||
2515 | /* Strip helping flag if they're only a support helper and not | |
2516 | * currently in #support. */ | |
2517 | if (HANDLE_FLAGGED(hi, HELPING) && (after == HI_FLAG_SUPPORT_HELPER)) { | |
2518 | struct channelList *schannels; | |
2519 | unsigned int ii; | |
2520 | schannels = chanserv_support_channels(); | |
2521 | for (uNode = hi->users; uNode; uNode = uNode->next_authed) { | |
2522 | for (ii = 0; ii < schannels->used; ++ii) | |
2523 | if (GetUserMode(schannels->list[ii], uNode)) | |
2524 | break; | |
2525 | if (ii < schannels->used) | |
2526 | break; | |
2527 | } | |
2528 | if (!uNode) | |
2529 | HANDLE_CLEAR_FLAG(hi, HELPING); | |
2530 | } | |
2531 | ||
2532 | if (after && !before) { | |
2533 | /* Add user to current helper list. */ | |
2534 | for (uNode = hi->users; uNode; uNode = uNode->next_authed) | |
2535 | userList_append(&curr_helpers, uNode); | |
2536 | } else if (!after && before) { | |
2537 | /* Remove user from current helper list. */ | |
2538 | for (uNode = hi->users; uNode; uNode = uNode->next_authed) | |
2539 | userList_remove(&curr_helpers, uNode); | |
2540 | } | |
2541 | ||
2542 | return 1; | |
2543 | } | |
2544 | ||
2545 | static void | |
2546 | set_list(struct svccmd *cmd, struct userNode *user, struct handle_info *hi, int override) | |
2547 | { | |
2548 | option_func_t *opt; | |
2549 | unsigned int i; | |
2550 | char *set_display[] = { | |
2551 | "INFO", "WIDTH", "TABLEWIDTH", "COLOR", "PRIVMSG", "STYLE", | |
2552 | "EMAIL", "ANNOUNCEMENTS", "AUTOHIDE", "MAXLOGINS", "LANGUAGE", | |
2553 | "FAKEHOST", "TITLE", "EPITHET", "ADVANCED" | |
2554 | }; | |
2555 | ||
2556 | reply("NSMSG_SETTING_LIST"); | |
2557 | reply("NSMSG_SETTING_LIST_HEADER"); | |
2558 | ||
2559 | /* Do this so options are presented in a consistent order. */ | |
2560 | for (i = 0; i < ArrayLength(set_display); ++i) | |
2561 | if ((opt = dict_find(nickserv_opt_dict, set_display[i], NULL))) | |
2562 | opt(cmd, user, hi, override, 0, NULL); | |
2563 | reply("NSMSG_SETTING_LIST_END"); | |
2564 | } | |
2565 | ||
2566 | static NICKSERV_FUNC(cmd_set) | |
2567 | { | |
2568 | struct handle_info *hi; | |
2569 | option_func_t *opt; | |
2570 | ||
2571 | hi = user->handle_info; | |
2572 | if (argc < 2) { | |
2573 | set_list(cmd, user, hi, 0); | |
2574 | return 1; | |
2575 | } | |
2576 | if (!(opt = dict_find(nickserv_opt_dict, argv[1], NULL))) { | |
2577 | reply("NSMSG_INVALID_OPTION", argv[1]); | |
2578 | return 0; | |
2579 | } | |
2580 | return opt(cmd, user, hi, 0, argc-1, argv+1); | |
2581 | } | |
2582 | ||
2583 | static NICKSERV_FUNC(cmd_oset) | |
2584 | { | |
2585 | struct handle_info *hi; | |
2586 | option_func_t *opt; | |
2587 | ||
2588 | NICKSERV_MIN_PARMS(2); | |
2589 | ||
2590 | if (!(hi = get_victim_oper(cmd, user, argv[1]))) | |
2591 | return 0; | |
2592 | ||
2593 | if (argc < 3) { | |
2594 | set_list(cmd, user, hi, 0); | |
2595 | return 1; | |
2596 | } | |
2597 | ||
2598 | if (!(opt = dict_find(nickserv_opt_dict, argv[2], NULL))) { | |
2599 | reply("NSMSG_INVALID_OPTION", argv[2]); | |
2600 | return 0; | |
2601 | } | |
2602 | ||
2603 | return opt(cmd, user, hi, 1, argc-2, argv+2); | |
2604 | } | |
2605 | ||
2606 | static OPTION_FUNC(opt_info) | |
2607 | { | |
2608 | const char *info; | |
2609 | if (argc > 1) { | |
2610 | if ((argv[1][0] == '*') && (argv[1][1] == 0)) { | |
2611 | free(hi->infoline); | |
2612 | hi->infoline = NULL; | |
2613 | } else { | |
2614 | hi->infoline = strdup(unsplit_string(argv+1, argc-1, NULL)); | |
2615 | } | |
2616 | } | |
2617 | ||
2618 | info = hi->infoline ? hi->infoline : user_find_message(user, "MSG_NONE"); | |
2619 | reply("NSMSG_SET_INFO", info); | |
2620 | return 1; | |
2621 | } | |
2622 | ||
2623 | static OPTION_FUNC(opt_width) | |
2624 | { | |
2625 | if (argc > 1) | |
2626 | hi->screen_width = strtoul(argv[1], NULL, 0); | |
2627 | ||
2628 | if ((hi->screen_width > 0) && (hi->screen_width < MIN_LINE_SIZE)) | |
2629 | hi->screen_width = MIN_LINE_SIZE; | |
2630 | else if (hi->screen_width > MAX_LINE_SIZE) | |
2631 | hi->screen_width = MAX_LINE_SIZE; | |
2632 | ||
2633 | reply("NSMSG_SET_WIDTH", hi->screen_width); | |
2634 | return 1; | |
2635 | } | |
2636 | ||
2637 | static OPTION_FUNC(opt_tablewidth) | |
2638 | { | |
2639 | if (argc > 1) | |
2640 | hi->table_width = strtoul(argv[1], NULL, 0); | |
2641 | ||
2642 | if ((hi->table_width > 0) && (hi->table_width < MIN_LINE_SIZE)) | |
2643 | hi->table_width = MIN_LINE_SIZE; | |
2644 | else if (hi->screen_width > MAX_LINE_SIZE) | |
2645 | hi->table_width = MAX_LINE_SIZE; | |
2646 | ||
2647 | reply("NSMSG_SET_TABLEWIDTH", hi->table_width); | |
2648 | return 1; | |
2649 | } | |
2650 | ||
2651 | static OPTION_FUNC(opt_color) | |
2652 | { | |
2653 | if (argc > 1) { | |
2654 | if (enabled_string(argv[1])) | |
2655 | HANDLE_SET_FLAG(hi, MIRC_COLOR); | |
2656 | else if (disabled_string(argv[1])) | |
2657 | HANDLE_CLEAR_FLAG(hi, MIRC_COLOR); | |
2658 | else { | |
2659 | reply("MSG_INVALID_BINARY", argv[1]); | |
2660 | return 0; | |
2661 | } | |
2662 | } | |
2663 | ||
2664 | reply("NSMSG_SET_COLOR", user_find_message(user, HANDLE_FLAGGED(hi, MIRC_COLOR) ? "MSG_ON" : "MSG_OFF")); | |
2665 | return 1; | |
2666 | } | |
2667 | ||
2668 | static OPTION_FUNC(opt_privmsg) | |
2669 | { | |
2670 | if (argc > 1) { | |
2671 | if (enabled_string(argv[1])) | |
2672 | HANDLE_SET_FLAG(hi, USE_PRIVMSG); | |
2673 | else if (disabled_string(argv[1])) | |
2674 | HANDLE_CLEAR_FLAG(hi, USE_PRIVMSG); | |
2675 | else { | |
2676 | reply("MSG_INVALID_BINARY", argv[1]); | |
2677 | return 0; | |
2678 | } | |
2679 | } | |
2680 | ||
2681 | reply("NSMSG_SET_PRIVMSG", user_find_message(user, HANDLE_FLAGGED(hi, USE_PRIVMSG) ? "MSG_ON" : "MSG_OFF")); | |
2682 | return 1; | |
2683 | } | |
2684 | ||
2685 | static OPTION_FUNC(opt_autohide) | |
2686 | { | |
2687 | if (argc > 1) { | |
2688 | if (enabled_string(argv[1])) | |
2689 | HANDLE_SET_FLAG(hi, AUTOHIDE); | |
2690 | else if (disabled_string(argv[1])) | |
2691 | HANDLE_CLEAR_FLAG(hi, AUTOHIDE); | |
2692 | else { | |
2693 | reply("MSG_INVALID_BINARY", argv[1]); | |
2694 | return 0; | |
2695 | } | |
2696 | } | |
2697 | ||
2698 | reply("NSMSG_SET_AUTOHIDE", user_find_message(user, HANDLE_FLAGGED(hi, AUTOHIDE) ? "MSG_ON" : "MSG_OFF")); | |
2699 | return 1; | |
2700 | } | |
2701 | ||
2702 | static OPTION_FUNC(opt_style) | |
2703 | { | |
2704 | char *style; | |
2705 | ||
2706 | if (argc > 1) { | |
2707 | if (!irccasecmp(argv[1], "Clean")) | |
2708 | hi->userlist_style = HI_STYLE_CLEAN; | |
2709 | else if (!irccasecmp(argv[1], "Advanced")) | |
2710 | hi->userlist_style = HI_STYLE_ADVANCED; | |
2711 | else if (!irccasecmp(argv[1], "Classic")) | |
2712 | hi->userlist_style = HI_STYLE_CLASSIC; | |
2713 | else /* Default to normal */ | |
2714 | hi->userlist_style = HI_STYLE_NORMAL; | |
2715 | } /* TODO: give error if unknow style is chosen */ | |
2716 | ||
2717 | switch (hi->userlist_style) { | |
2718 | case HI_STYLE_ADVANCED: | |
2719 | style = "Advanced"; | |
2720 | break; | |
2721 | case HI_STYLE_CLASSIC: | |
2722 | style = "Classic"; | |
2723 | break; | |
2724 | case HI_STYLE_CLEAN: | |
2725 | style = "Clean"; | |
2726 | break; | |
2727 | case HI_STYLE_NORMAL: | |
2728 | default: | |
2729 | style = "Normal"; | |
2730 | } | |
2731 | ||
2732 | reply("NSMSG_SET_STYLE", style); | |
2733 | return 1; | |
2734 | } | |
2735 | ||
2736 | static OPTION_FUNC(opt_announcements) | |
2737 | { | |
2738 | const char *choice; | |
2739 | ||
2740 | if (argc > 1) { | |
2741 | if (enabled_string(argv[1])) | |
2742 | hi->announcements = 'y'; | |
2743 | else if (disabled_string(argv[1])) | |
2744 | hi->announcements = 'n'; | |
2745 | else if (!strcmp(argv[1], "?") || !irccasecmp(argv[1], "default")) | |
2746 | hi->announcements = '?'; | |
2747 | else { | |
2748 | reply("NSMSG_INVALID_ANNOUNCE", argv[1]); | |
2749 | return 0; | |
2750 | } | |
2751 | } | |
2752 | ||
2753 | switch (hi->announcements) { | |
2754 | case 'y': choice = user_find_message(user, "MSG_ON"); break; | |
2755 | case 'n': choice = user_find_message(user, "MSG_OFF"); break; | |
2756 | case '?': choice = "default"; break; | |
2757 | default: choice = "unknown"; break; | |
2758 | } | |
2759 | reply("NSMSG_SET_ANNOUNCEMENTS", choice); | |
2760 | return 1; | |
2761 | } | |
2762 | ||
2763 | static OPTION_FUNC(opt_password) | |
2764 | { | |
2765 | if (!override) { | |
2766 | reply("NSMSG_USE_CMD_PASS"); | |
2767 | return 0; | |
2768 | } | |
2769 | ||
2770 | if (argc > 1) | |
2771 | cryptpass(argv[1], hi->passwd); | |
2772 | ||
2773 | if (nickserv_conf.sync_log) | |
2774 | SyncLog("PASSCHANGE %s %s", hi->handle, hi->passwd); | |
2775 | ||
2776 | reply("NSMSG_SET_PASSWORD", "***"); | |
2777 | return 1; | |
2778 | } | |
2779 | ||
2780 | static OPTION_FUNC(opt_flags) | |
2781 | { | |
2782 | char flags[33]; | |
2783 | unsigned int ii, flen; | |
2784 | ||
2785 | if (!override) { | |
2786 | reply("MSG_SETTING_PRIVILEGED", argv[0]); | |
2787 | return 0; | |
2788 | } | |
2789 | ||
2790 | if (argc > 1) | |
2791 | nickserv_apply_flags(user, hi, argv[1]); | |
2792 | ||
2793 | for (ii = flen = 0; handle_flags[ii]; ii++) | |
2794 | if (hi->flags & (1 << ii)) | |
2795 | flags[flen++] = handle_flags[ii]; | |
2796 | flags[flen] = '\0'; | |
2797 | if (hi->flags) | |
2798 | reply("NSMSG_SET_FLAGS", flags); | |
2799 | else | |
2800 | reply("NSMSG_SET_FLAGS", user_find_message(user, "MSG_NONE")); | |
2801 | return 1; | |
2802 | } | |
2803 | ||
2804 | static OPTION_FUNC(opt_email) | |
2805 | { | |
2806 | if (argc > 1) { | |
2807 | const char *str; | |
2808 | if (!valid_email(argv[1])) { | |
2809 | reply("NSMSG_BAD_EMAIL_ADDR"); | |
2810 | return 0; | |
2811 | } | |
2812 | if ((str = sendmail_prohibited_address(argv[1]))) { | |
2813 | reply("NSMSG_EMAIL_PROHIBITED", argv[1], str); | |
2814 | return 0; | |
2815 | } | |
2816 | if (hi->email_addr && !irccasecmp(hi->email_addr, argv[1])) | |
2817 | reply("NSMSG_EMAIL_SAME"); | |
2818 | else if (!override) | |
2819 | nickserv_make_cookie(user, hi, EMAIL_CHANGE, argv[1], 0); | |
2820 | else { | |
2821 | nickserv_set_email_addr(hi, argv[1]); | |
2822 | if (hi->cookie) | |
2823 | nickserv_eat_cookie(hi->cookie); | |
2824 | reply("NSMSG_SET_EMAIL", visible_email_addr(user, hi)); | |
2825 | } | |
2826 | } else | |
2827 | reply("NSMSG_SET_EMAIL", visible_email_addr(user, hi)); | |
2828 | return 1; | |
2829 | } | |
2830 | ||
2831 | static OPTION_FUNC(opt_maxlogins) | |
2832 | { | |
2833 | unsigned char maxlogins; | |
2834 | if (argc > 1) { | |
2835 | maxlogins = strtoul(argv[1], NULL, 0); | |
2836 | if ((maxlogins > nickserv_conf.hard_maxlogins) && !override) { | |
2837 | reply("NSMSG_BAD_MAX_LOGINS", nickserv_conf.hard_maxlogins); | |
2838 | return 0; | |
2839 | } | |
2840 | hi->maxlogins = maxlogins; | |
2841 | } | |
2842 | maxlogins = hi->maxlogins ? hi->maxlogins : nickserv_conf.default_maxlogins; | |
2843 | reply("NSMSG_SET_MAXLOGINS", maxlogins); | |
2844 | return 1; | |
2845 | } | |
2846 | ||
2847 | static OPTION_FUNC(opt_advanced) | |
2848 | { | |
2849 | if (argc > 1) { | |
2850 | if (enabled_string(argv[1])) | |
2851 | HANDLE_SET_FLAG(hi, ADVANCED); | |
2852 | else if (disabled_string(argv[1])) | |
2853 | HANDLE_CLEAR_FLAG(hi, ADVANCED); | |
2854 | else { | |
2855 | reply("MSG_INVALID_BINARY", argv[1]); | |
2856 | return 0; | |
2857 | } | |
2858 | } | |
2859 | ||
2860 | reply("NSMSG_SET_ADVANCED", user_find_message(user, HANDLE_FLAGGED(hi, ADVANCED) ? "MSG_ON" : "MSG_OFF")); | |
2861 | return 1; | |
2862 | } | |
2863 | ||
2864 | static OPTION_FUNC(opt_language) | |
2865 | { | |
2866 | struct language *lang; | |
2867 | if (argc > 1) { | |
2868 | lang = language_find(argv[1]); | |
2869 | if (irccasecmp(lang->name, argv[1])) | |
2870 | reply("NSMSG_LANGUAGE_NOT_FOUND", argv[1], lang->name); | |
2871 | hi->language = lang; | |
2872 | } | |
2873 | reply("NSMSG_SET_LANGUAGE", hi->language->name); | |
2874 | return 1; | |
2875 | } | |
2876 | ||
2877 | int | |
2878 | oper_try_set_access(struct userNode *user, struct userNode *bot, struct handle_info *target, unsigned int new_level) { | |
2879 | if (!oper_has_access(user, bot, nickserv_conf.modoper_level, 0)) | |
2880 | return 0; | |
2881 | if ((user->handle_info->opserv_level < target->opserv_level) | |
2882 | || ((user->handle_info->opserv_level == target->opserv_level) | |
2883 | && (user->handle_info->opserv_level < 1000))) { | |
2884 | send_message(user, bot, "MSG_USER_OUTRANKED", target->handle); | |
2885 | return 0; | |
2886 | } | |
2887 | if ((user->handle_info->opserv_level < new_level) | |
2888 | || ((user->handle_info->opserv_level == new_level) | |
2889 | && (user->handle_info->opserv_level < 1000))) { | |
2890 | send_message(user, bot, "NSMSG_OPSERV_LEVEL_BAD"); | |
2891 | return 0; | |
2892 | } | |
2893 | if (user->handle_info == target) { | |
2894 | send_message(user, bot, "MSG_STUPID_ACCESS_CHANGE"); | |
2895 | return 0; | |
2896 | } | |
2897 | if (target->opserv_level == new_level) | |
2898 | return 0; | |
2899 | log_module(NS_LOG, LOG_INFO, "Account %s setting oper level for account %s to %d (from %d).", | |
2900 | user->handle_info->handle, target->handle, new_level, target->opserv_level); | |
2901 | target->opserv_level = new_level; | |
2902 | return 1; | |
2903 | } | |
2904 | ||
2905 | static OPTION_FUNC(opt_level) | |
2906 | { | |
2907 | int res; | |
2908 | ||
2909 | if (!override) { | |
2910 | reply("MSG_SETTING_PRIVILEGED", argv[0]); | |
2911 | return 0; | |
2912 | } | |
2913 | ||
2914 | res = (argc > 1) ? oper_try_set_access(user, nickserv, hi, strtoul(argv[1], NULL, 0)) : 0; | |
2915 | reply("NSMSG_SET_LEVEL", hi->opserv_level); | |
2916 | return res; | |
2917 | } | |
2918 | ||
2919 | static OPTION_FUNC(opt_epithet) | |
2920 | { | |
2921 | if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_epithet_level, 0)) { | |
2922 | char *epithet; | |
2923 | struct userNode *target, *next_un; | |
2924 | ||
2925 | if (!override) { | |
2926 | reply("MSG_SETTING_PRIVILEGED", argv[0]); | |
2927 | return 0; | |
2928 | } | |
2929 | ||
2930 | epithet = unsplit_string(argv+1, argc-1, NULL); | |
2931 | ||
2932 | if (hi->epithet) | |
2933 | free(hi->epithet); | |
2934 | if ((epithet[0] == '*') && !epithet[1]) | |
2935 | hi->epithet = NULL; | |
2936 | else | |
2937 | hi->epithet = strdup(epithet); | |
2938 | ||
2939 | for (target = hi->users; target; target = next_un) { | |
2940 | irc_swhois(nickserv, target, hi->epithet); | |
2941 | ||
2942 | next_un = target->next_authed; | |
2943 | } | |
2944 | } | |
2945 | ||
2946 | if (hi->epithet) | |
2947 | reply("NSMSG_SET_EPITHET", hi->epithet); | |
2948 | else | |
2949 | reply("NSMSG_SET_EPITHET", user_find_message(user, "MSG_NONE")); | |
2950 | return 1; | |
2951 | } | |
2952 | ||
2953 | static OPTION_FUNC(opt_title) | |
2954 | { | |
2955 | char *title; | |
2956 | const char *none; | |
2957 | char *sptr; | |
2958 | ||
2959 | if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_title_level, 0)) { | |
2960 | if (!override) { | |
2961 | reply("MSG_SETTING_PRIVILEGED", argv[0]); | |
2962 | return 0; | |
2963 | } | |
2964 | ||
2965 | title = argv[1]; | |
2966 | if(!strcmp(title, "*")) { | |
2967 | free(hi->fakehost); | |
2968 | hi->fakehost = NULL; | |
2969 | } | |
2970 | else { | |
2971 | if (strchr(title, '.')) { | |
2972 | reply("NSMSG_TITLE_INVALID"); | |
2973 | return 0; | |
2974 | } | |
2975 | /* Alphanumeric titles only. */ | |
2976 | for(sptr = title; *sptr; sptr++) { | |
2977 | if(!isalnum(*sptr) && *sptr != '-') { | |
2978 | reply("NSMSG_TITLE_INVALID"); | |
2979 | return 0; | |
2980 | } | |
2981 | } | |
2982 | if ((strlen(user->handle_info->handle) + strlen(title) + | |
2983 | strlen(nickserv_conf.titlehost_suffix) + 2) > HOSTLEN) { | |
2984 | reply("NSMSG_TITLE_TRUNCATED"); | |
2985 | return 0; | |
2986 | } | |
2987 | free(hi->fakehost); | |
2988 | hi->fakehost = malloc(strlen(title)+2); | |
2989 | hi->fakehost[0] = '.'; | |
2990 | strcpy(hi->fakehost+1, title); | |
2991 | } | |
2992 | apply_fakehost(hi); | |
2993 | } else if (hi->fakehost && (hi->fakehost[0] == '.')) | |
2994 | title = hi->fakehost + 1; | |
2995 | else { | |
2996 | /* If theres no title set then the default title will therefore | |
2997 | be the first part of hidden_host in x3.conf.example, so for | |
2998 | consistency with opt_fakehost we will print this here */ | |
2999 | char *hs, *hidden_suffix, *rest; | |
3000 | ||
3001 | hs = conf_get_data("server/hidden_host", RECDB_QSTRING); | |
3002 | hidden_suffix = strdup(hs); | |
3003 | ||
3004 | /* Yes we do this twice */ | |
3005 | rest = strrchr(hidden_suffix, '.'); | |
3006 | *rest++ = '\0'; | |
3007 | rest = strrchr(hidden_suffix, '.'); | |
3008 | *rest++ = '\0'; | |
3009 | ||
3010 | title = hidden_suffix; | |
3011 | } | |
3012 | ||
3013 | if (!title) | |
3014 | none = user_find_message(user, "MSG_NONE"); | |
3015 | send_message(user, nickserv, "NSMSG_SET_TITLE", title ? title : none); | |
3016 | return 1; | |
3017 | } | |
3018 | ||
3019 | int | |
3020 | check_vhost(char *vhost, struct userNode *user, struct svccmd *cmd) | |
3021 | { | |
3022 | unsigned int y; | |
3023 | ||
3024 | // check for a dot in the vhost | |
3025 | if(strchr(vhost, '.') == NULL) { | |
3026 | reply("NSMSG_NOT_VALID_FAKEHOST_DOT", vhost); | |
3027 | return 0; | |
3028 | } | |
3029 | ||
3030 | // check for a @ in the vhost | |
3031 | if(strchr(vhost, '@') != NULL) { | |
3032 | reply("NSMSG_NOT_VALID_FAKEHOST_AT", vhost); | |
3033 | return 0; | |
3034 | } | |
3035 | ||
3036 | // check for denied words, inspired by monk at paki.sex | |
3037 | for(y = 0; y < nickserv_conf.denied_fakehost_words->used; y++) { | |
3038 | if(strstr(vhost, nickserv_conf.denied_fakehost_words->list[y]) != NULL) { | |
3039 | reply("NSMSG_DENIED_FAKEHOST_WORD", vhost, nickserv_conf.denied_fakehost_words->list[y]); | |
3040 | return 0; | |
3041 | } | |
3042 | } | |
3043 | ||
3044 | // check for ircu's HOSTLEN length. | |
3045 | if(strlen(vhost) >= HOSTLEN) { | |
3046 | reply("NSMSG_NOT_VALID_FAKEHOST_LEN", vhost); | |
3047 | return 0; | |
3048 | } | |
3049 | ||
3050 | /* This can be handled by the regex now if desired. | |
3051 | if (vhost[strspn(vhost, "0123456789.")]) { | |
3052 | hostname = vhost + strlen(vhost); | |
3053 | for (depth = 1; depth && (hostname > vhost); depth--) { | |
3054 | hostname--; | |
3055 | while ((hostname > vhost) && (*hostname != '.')) hostname--; | |
3056 | } | |
3057 | ||
3058 | if (*hostname == '.') hostname++; * advance past last dot we saw * | |
3059 | if(strlen(hostname) > 4) { | |
3060 | reply("NSMSG_NOT_VALID_FAKEHOST_TLD_LEN", vhost); | |
3061 | return 0; | |
3062 | } | |
3063 | } | |
3064 | */ | |
3065 | /* test either regex or as valid handle */ | |
3066 | if (nickserv_conf.valid_fakehost_regex_set) { | |
3067 | int err = regexec(&nickserv_conf.valid_fakehost_regex, vhost, 0, 0, 0); | |
3068 | if (err) { | |
3069 | char buff[256]; | |
3070 | buff[regerror(err, &nickserv_conf.valid_fakehost_regex, buff, sizeof(buff))] = 0; | |
3071 | log_module(NS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err); | |
3072 | } | |
3073 | if(err == REG_NOMATCH) { | |
3074 | reply("NSMSG_NOT_VALID_FAKEHOST_REGEX", vhost); | |
3075 | return 0; | |
3076 | } | |
3077 | } | |
3078 | ||
3079 | ||
3080 | return 1; | |
3081 | } | |
3082 | ||
3083 | static OPTION_FUNC(opt_fakehost) | |
3084 | { | |
3085 | const char *fake; | |
3086 | ||
3087 | if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) { | |
3088 | if (!override) { | |
3089 | reply("MSG_SETTING_PRIVILEGED", argv[0]); | |
3090 | return 0; | |
3091 | } | |
3092 | ||
3093 | fake = argv[1]; | |
3094 | if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) { | |
3095 | reply("NSMSG_FAKEHOST_INVALID", HOSTLEN); | |
3096 | return 0; | |
3097 | } | |
3098 | if (!strcmp(fake, "*")) { | |
3099 | if(hi->fakehost) { | |
3100 | free(hi->fakehost); | |
3101 | hi->fakehost = NULL; | |
3102 | } | |
3103 | } | |
3104 | else if (!check_vhost(argv[1], user, cmd)) { | |
3105 | /* check_vhost takes care of error reply */ | |
3106 | return 0; | |
3107 | } | |
3108 | else { | |
3109 | if(hi->fakehost) | |
3110 | free(hi->fakehost); | |
3111 | hi->fakehost = strdup(fake); | |
3112 | } | |
3113 | apply_fakehost(hi); | |
3114 | fake = hi->fakehost; | |
3115 | } else | |
3116 | fake = generate_fakehost(hi); | |
3117 | ||
3118 | /* Tell them we set the host */ | |
3119 | if (!fake) | |
3120 | fake = user_find_message(user, "MSG_NONE"); | |
3121 | reply("NSMSG_SET_FAKEHOST", fake); | |
3122 | return 1; | |
3123 | } | |
3124 | ||
3125 | static OPTION_FUNC(opt_note) | |
3126 | { | |
3127 | if (!override) { | |
3128 | reply("MSG_SETTING_PRIVILEGED", argv[0]); | |
3129 | return 0; | |
3130 | } | |
3131 | ||
3132 | if (argc > 1) { | |
3133 | char *text = unsplit_string(argv + 1, argc - 1, NULL); | |
3134 | ||
3135 | if (hi->note) | |
3136 | free(hi->note); | |
3137 | ||
3138 | if ((text[0] == '*') && !text[1]) | |
3139 | hi->note = NULL; | |
3140 | else { | |
3141 | if (!(hi->note = nickserv_add_note(user->handle_info->handle, now, text))) | |
3142 | hi->note = NULL; | |
3143 | } | |
3144 | } | |
3145 | ||
3146 | reply("NSMSG_SET_NOTE", hi->note->note); | |
3147 | return 1; | |
3148 | } | |
3149 | ||
3150 | static NICKSERV_FUNC(cmd_reclaim) | |
3151 | { | |
3152 | struct handle_info *hi; | |
3153 | struct nick_info *ni; | |
3154 | struct userNode *victim; | |
3155 | ||
3156 | NICKSERV_MIN_PARMS(2); | |
3157 | hi = user->handle_info; | |
3158 | ni = dict_find(nickserv_nick_dict, argv[1], 0); | |
3159 | if (!ni) { | |
3160 | reply("NSMSG_UNKNOWN_NICK", argv[1]); | |
3161 | return 0; | |
3162 | } | |
3163 | if (ni->owner != user->handle_info) { | |
3164 | reply("NSMSG_NOT_YOUR_NICK", ni->nick); | |
3165 | return 0; | |
3166 | } | |
3167 | victim = GetUserH(ni->nick); | |
3168 | if (!victim) { | |
3169 | reply("MSG_NICK_UNKNOWN", ni->nick); | |
3170 | return 0; | |
3171 | } | |
3172 | if (victim == user) { | |
3173 | reply("NSMSG_NICK_USER_YOU"); | |
3174 | return 0; | |
3175 | } | |
3176 | nickserv_reclaim(victim, ni, nickserv_conf.reclaim_action); | |
3177 | switch (nickserv_conf.reclaim_action) { | |
3178 | case RECLAIM_NONE: reply("NSMSG_RECLAIMED_NONE"); break; | |
3179 | case RECLAIM_WARN: reply("NSMSG_RECLAIMED_WARN", victim->nick); break; | |
3180 | case RECLAIM_SVSNICK: reply("NSMSG_RECLAIMED_SVSNICK", victim->nick); break; | |
3181 | case RECLAIM_KILL: reply("NSMSG_RECLAIMED_KILL", victim->nick); break; | |
3182 | } | |
3183 | return 1; | |
3184 | } | |
3185 | ||
3186 | static NICKSERV_FUNC(cmd_unregnick) | |
3187 | { | |
3188 | const char *nick; | |
3189 | struct handle_info *hi; | |
3190 | struct nick_info *ni; | |
3191 | ||
3192 | hi = user->handle_info; | |
3193 | nick = (argc < 2) ? user->nick : (const char*)argv[1]; | |
3194 | ni = dict_find(nickserv_nick_dict, nick, NULL); | |
3195 | if (!ni) { | |
3196 | reply("NSMSG_UNKNOWN_NICK", nick); | |
3197 | return 0; | |
3198 | } | |
3199 | if (hi != ni->owner) { | |
3200 | reply("NSMSG_NOT_YOUR_NICK", nick); | |
3201 | return 0; | |
3202 | } | |
3203 | reply("NSMSG_UNREGNICK_SUCCESS", ni->nick); | |
3204 | delete_nick(ni); | |
3205 | return 1; | |
3206 | } | |
3207 | ||
3208 | static NICKSERV_FUNC(cmd_ounregnick) | |
3209 | { | |
3210 | struct nick_info *ni; | |
3211 | ||
3212 | NICKSERV_MIN_PARMS(2); | |
3213 | if (!(ni = get_nick_info(argv[1]))) { | |
3214 | reply("NSMSG_NICK_NOT_REGISTERED", argv[1]); | |
3215 | return 0; | |
3216 | } | |
3217 | if (ni->owner->opserv_level >= user->handle_info->opserv_level) { | |
3218 | reply("MSG_USER_OUTRANKED", ni->nick); | |
3219 | return 0; | |
3220 | } | |
3221 | reply("NSMSG_UNREGNICK_SUCCESS", ni->nick); | |
3222 | delete_nick(ni); | |
3223 | return 1; | |
3224 | } | |
3225 | ||
3226 | static NICKSERV_FUNC(cmd_unregister) | |
3227 | { | |
3228 | struct handle_info *hi; | |
3229 | char *passwd; | |
3230 | ||
3231 | NICKSERV_MIN_PARMS(2); | |
3232 | hi = user->handle_info; | |
3233 | passwd = argv[1]; | |
3234 | argv[1] = "****"; | |
3235 | if (checkpass(passwd, hi->passwd)) { | |
3236 | nickserv_unregister_handle(hi, user, cmd->parent->bot); | |
3237 | return 1; | |
3238 | } else { | |
3239 | log_module(NS_LOG, LOG_INFO, "Account '%s' tried to unregister with the wrong password.", hi->handle); | |
3240 | reply("NSMSG_PASSWORD_INVALID"); | |
3241 | return 0; | |
3242 | } | |
3243 | } | |
3244 | ||
3245 | static NICKSERV_FUNC(cmd_ounregister) | |
3246 | { | |
3247 | struct handle_info *hi; | |
3248 | ||
3249 | NICKSERV_MIN_PARMS(2); | |
3250 | if (!(hi = get_victim_oper(cmd, user, argv[1]))) | |
3251 | return 0; | |
3252 | nickserv_unregister_handle(hi, user, cmd->parent->bot); | |
3253 | return 1; | |
3254 | } | |
3255 | ||
3256 | static NICKSERV_FUNC(cmd_status) | |
3257 | { | |
3258 | if (nickserv_conf.disable_nicks) { | |
3259 | reply("NSMSG_GLOBAL_STATS_NONICK", | |
3260 | dict_size(nickserv_handle_dict)); | |
3261 | } else { | |
3262 | if (user->handle_info) { | |
3263 | int cnt=0; | |
3264 | struct nick_info *ni; | |
3265 | for (ni=user->handle_info->nicks; ni; ni=ni->next) cnt++; | |
3266 | reply("NSMSG_HANDLE_STATS", cnt); | |
3267 | } else { | |
3268 | reply("NSMSG_HANDLE_NONE"); | |
3269 | } | |
3270 | reply("NSMSG_GLOBAL_STATS", | |
3271 | dict_size(nickserv_handle_dict), | |
3272 | dict_size(nickserv_nick_dict)); | |
3273 | } | |
3274 | return 1; | |
3275 | } | |
3276 | ||
3277 | static NICKSERV_FUNC(cmd_ghost) | |
3278 | { | |
3279 | struct userNode *target; | |
3280 | char reason[MAXLEN]; | |
3281 | ||
3282 | NICKSERV_MIN_PARMS(2); | |
3283 | if (!(target = GetUserH(argv[1]))) { | |
3284 | reply("MSG_NICK_UNKNOWN", argv[1]); | |
3285 | return 0; | |
3286 | } | |
3287 | if (target == user) { | |
3288 | reply("NSMSG_CANNOT_GHOST_SELF"); | |
3289 | return 0; | |
3290 | } | |
3291 | if (!target->handle_info || (target->handle_info != user->handle_info)) { | |
3292 | reply("NSMSG_CANNOT_GHOST_USER", target->nick); | |
3293 | return 0; | |
3294 | } | |
3295 | snprintf(reason, sizeof(reason), "Ghost kill on account %s (requested by %s).", target->handle_info->handle, user->nick); | |
3296 | DelUser(target, nickserv, 1, reason); | |
3297 | reply("NSMSG_GHOST_KILLED", argv[1]); | |
3298 | return 1; | |
3299 | } | |
3300 | ||
3301 | static NICKSERV_FUNC(cmd_vacation) | |
3302 | { | |
3303 | HANDLE_SET_FLAG(user->handle_info, FROZEN); | |
3304 | reply("NSMSG_ON_VACATION"); | |
3305 | return 1; | |
3306 | } | |
3307 | ||
3308 | static int | |
3309 | nickserv_saxdb_write(struct saxdb_context *ctx) { | |
3310 | dict_iterator_t it; | |
3311 | struct handle_info *hi; | |
3312 | char flags[33]; | |
3313 | ||
3314 | for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) { | |
3315 | hi = iter_data(it); | |
3316 | #ifdef WITH_PROTOCOL_BAHAMUT | |
3317 | assert(hi->id); | |
3318 | #endif | |
3319 | saxdb_start_record(ctx, iter_key(it), 0); | |
3320 | if (hi->announcements != '?') { | |
3321 | flags[0] = hi->announcements; | |
3322 | flags[1] = 0; | |
3323 | saxdb_write_string(ctx, KEY_ANNOUNCEMENTS, flags); | |
3324 | } | |
3325 | if (hi->cookie) { | |
3326 | struct handle_cookie *cookie = hi->cookie; | |
3327 | char *type; | |
3328 | ||
3329 | switch (cookie->type) { | |
3330 | case ACTIVATION: type = KEY_ACTIVATION; break; | |
3331 | case PASSWORD_CHANGE: type = KEY_PASSWORD_CHANGE; break; | |
3332 | case EMAIL_CHANGE: type = KEY_EMAIL_CHANGE; break; | |
3333 | case ALLOWAUTH: type = KEY_ALLOWAUTH; break; | |
3334 | default: type = NULL; break; | |
3335 | } | |
3336 | if (type) { | |
3337 | saxdb_start_record(ctx, KEY_COOKIE, 0); | |
3338 | saxdb_write_string(ctx, KEY_COOKIE_TYPE, type); | |
3339 | saxdb_write_int(ctx, KEY_COOKIE_EXPIRES, cookie->expires); | |
3340 | if (cookie->data) | |
3341 | saxdb_write_string(ctx, KEY_COOKIE_DATA, cookie->data); | |
3342 | saxdb_write_string(ctx, KEY_COOKIE, cookie->cookie); | |
3343 | saxdb_end_record(ctx); | |
3344 | } | |
3345 | } | |
3346 | if (hi->email_addr) | |
3347 | saxdb_write_string(ctx, KEY_EMAIL_ADDR, hi->email_addr); | |
3348 | if (hi->epithet) | |
3349 | saxdb_write_string(ctx, KEY_EPITHET, hi->epithet); | |
3350 | if (hi->note) { | |
3351 | saxdb_start_record(ctx, KEY_NOTE_NOTE, 0); | |
3352 | saxdb_write_string(ctx, KEY_NOTE_SETTER, hi->note->setter); | |
3353 | saxdb_write_int(ctx, KEY_NOTE_DATE, hi->note->date); | |
3354 | saxdb_write_string(ctx, KEY_NOTE_NOTE, hi->note->note); | |
3355 | saxdb_end_record(ctx); | |
3356 | } | |
3357 | ||
3358 | if (hi->fakehost) | |
3359 | saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost); | |
3360 | if (hi->flags) { | |
3361 | int ii, flen; | |
3362 | ||
3363 | for (ii=flen=0; handle_flags[ii]; ++ii) | |
3364 | if (hi->flags & (1 << ii)) | |
3365 | flags[flen++] = handle_flags[ii]; | |
3366 | flags[flen] = 0; | |
3367 | saxdb_write_string(ctx, KEY_FLAGS, flags); | |
3368 | } | |
3369 | #ifdef WITH_PROTOCOL_BAHAMUT | |
3370 | saxdb_write_int(ctx, KEY_ID, hi->id); | |
3371 | #endif | |
3372 | if (hi->infoline) | |
3373 | saxdb_write_string(ctx, KEY_INFO, hi->infoline); | |
3374 | if (hi->last_quit_host[0]) | |
3375 | saxdb_write_string(ctx, KEY_LAST_QUIT_HOST, hi->last_quit_host); | |
3376 | saxdb_write_int(ctx, KEY_LAST_SEEN, hi->lastseen); | |
3377 | if (hi->masks->used) | |
3378 | saxdb_write_string_list(ctx, KEY_MASKS, hi->masks); | |
3379 | if (hi->ignores->used) | |
3380 | saxdb_write_string_list(ctx, KEY_IGNORES, hi->ignores); | |
3381 | if (hi->maxlogins) | |
3382 | saxdb_write_int(ctx, KEY_MAXLOGINS, hi->maxlogins); | |
3383 | if (hi->nicks) { | |
3384 | struct string_list *slist; | |
3385 | struct nick_info *ni; | |
3386 | ||
3387 | slist = alloc_string_list(nickserv_conf.nicks_per_handle); | |
3388 | for (ni = hi->nicks; ni; ni = ni->next) string_list_append(slist, ni->nick); | |
3389 | saxdb_write_string_list(ctx, KEY_NICKS, slist); | |
3390 | free(slist->list); | |
3391 | free(slist); | |
3392 | } | |
3393 | if (hi->opserv_level) | |
3394 | saxdb_write_int(ctx, KEY_OPSERV_LEVEL, hi->opserv_level); | |
3395 | if (hi->language != lang_C) | |
3396 | saxdb_write_string(ctx, KEY_LANGUAGE, hi->language->name); | |
3397 | saxdb_write_string(ctx, KEY_PASSWD, hi->passwd); | |
3398 | saxdb_write_int(ctx, KEY_REGISTER_ON, hi->registered); | |
3399 | if (hi->screen_width) | |
3400 | saxdb_write_int(ctx, KEY_SCREEN_WIDTH, hi->screen_width); | |
3401 | if (hi->table_width) | |
3402 | saxdb_write_int(ctx, KEY_TABLE_WIDTH, hi->table_width); | |
3403 | flags[0] = hi->userlist_style; | |
3404 | flags[1] = 0; | |
3405 | saxdb_write_string(ctx, KEY_USERLIST_STYLE, flags); | |
3406 | saxdb_end_record(ctx); | |
3407 | } | |
3408 | ||
3409 | return 0; | |
3410 | } | |
3411 | ||
3412 | static handle_merge_func_t *handle_merge_func_list; | |
3413 | static unsigned int handle_merge_func_size = 0, handle_merge_func_used = 0; | |
3414 | ||
3415 | void | |
3416 | reg_handle_merge_func(handle_merge_func_t func) | |
3417 | { | |
3418 | if (handle_merge_func_used == handle_merge_func_size) { | |
3419 | if (handle_merge_func_size) { | |
3420 | handle_merge_func_size <<= 1; | |
3421 | handle_merge_func_list = realloc(handle_merge_func_list, handle_merge_func_size*sizeof(handle_merge_func_t)); | |
3422 | } else { | |
3423 | handle_merge_func_size = 8; | |
3424 | handle_merge_func_list = malloc(handle_merge_func_size*sizeof(handle_merge_func_t)); | |
3425 | } | |
3426 | } | |
3427 | handle_merge_func_list[handle_merge_func_used++] = func; | |
3428 | } | |
3429 | ||
3430 | static NICKSERV_FUNC(cmd_merge) | |
3431 | { | |
3432 | struct handle_info *hi_from, *hi_to; | |
3433 | struct userNode *last_user; | |
3434 | struct userData *cList, *cListNext; | |
3435 | unsigned int ii, jj, n; | |
3436 | char buffer[MAXLEN]; | |
3437 | ||
3438 | NICKSERV_MIN_PARMS(3); | |
3439 | ||
3440 | if (!(hi_from = get_victim_oper(cmd, user, argv[1]))) | |
3441 | return 0; | |
3442 | if (!(hi_to = get_victim_oper(cmd, user, argv[2]))) | |
3443 | return 0; | |
3444 | if (hi_to == hi_from) { | |
3445 | reply("NSMSG_CANNOT_MERGE_SELF", hi_to->handle); | |
3446 | return 0; | |
3447 | } | |
3448 | ||
3449 | for (n=0; n<handle_merge_func_used; n++) | |
3450 | handle_merge_func_list[n](user, hi_to, hi_from); | |
3451 | ||
3452 | /* Append "from" handle's nicks to "to" handle's nick list. */ | |
3453 | if (hi_to->nicks) { | |
3454 | struct nick_info *last_ni; | |
3455 | for (last_ni=hi_to->nicks; last_ni->next; last_ni=last_ni->next) ; | |
3456 | last_ni->next = hi_from->nicks; | |
3457 | } | |
3458 | while (hi_from->nicks) { | |
3459 | hi_from->nicks->owner = hi_to; | |
3460 | hi_from->nicks = hi_from->nicks->next; | |
3461 | } | |
3462 | ||
3463 | /* Merge the hostmasks. */ | |
3464 | for (ii=0; ii<hi_from->masks->used; ii++) { | |
3465 | char *mask = hi_from->masks->list[ii]; | |
3466 | for (jj=0; jj<hi_to->masks->used; jj++) | |
3467 | if (match_ircglobs(hi_to->masks->list[jj], mask)) | |
3468 | break; | |
3469 | if (jj==hi_to->masks->used) /* Nothing from the "to" handle covered this mask, so add it. */ | |
3470 | string_list_append(hi_to->masks, strdup(mask)); | |
3471 | } | |
3472 | ||
3473 | /* Merge the ignores. */ | |
3474 | for (ii=0; ii<hi_from->ignores->used; ii++) { | |
3475 | char *ignore = hi_from->ignores->list[ii]; | |
3476 | for (jj=0; jj<hi_to->ignores->used; jj++) | |
3477 | if (match_ircglobs(hi_to->ignores->list[jj], ignore)) | |
3478 | break; | |
3479 | if (jj==hi_to->ignores->used) /* Nothing from the "to" handle covered this mask, so add it. */ | |
3480 | string_list_append(hi_to->ignores, strdup(ignore)); | |
3481 | } | |
3482 | ||
3483 | /* Merge the lists of authed users. */ | |
3484 | if (hi_to->users) { | |
3485 | for (last_user=hi_to->users; last_user->next_authed; last_user=last_user->next_authed) ; | |
3486 | last_user->next_authed = hi_from->users; | |
3487 | } else { | |
3488 | hi_to->users = hi_from->users; | |
3489 | } | |
3490 | /* Repoint the old "from" handle's users. */ | |
3491 | for (last_user=hi_from->users; last_user; last_user=last_user->next_authed) { | |
3492 | last_user->handle_info = hi_to; | |
3493 | } | |
3494 | hi_from->users = NULL; | |
3495 | ||
3496 | /* Merge channel userlists. */ | |
3497 | for (cList=hi_from->channels; cList; cList=cListNext) { | |
3498 | struct userData *cList2; | |
3499 | cListNext = cList->u_next; | |
3500 | for (cList2=hi_to->channels; cList2; cList2=cList2->u_next) | |
3501 | if (cList->channel == cList2->channel) | |
3502 | break; | |
3503 | if (cList2 && (cList2->access >= cList->access)) { | |
3504 | 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); | |
3505 | /* keep cList2 in hi_to; remove cList from hi_from */ | |
3506 | del_channel_user(cList, 1); | |
3507 | } else { | |
3508 | if (cList2) { | |
3509 | 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); | |
3510 | /* remove the lower-ranking cList2 from hi_to */ | |
3511 | del_channel_user(cList2, 1); | |
3512 | } else { | |
3513 | log_module(NS_LOG, LOG_INFO, "Merge: %s had no access in %s", hi_to->handle, cList->channel->channel->name); | |
3514 | } | |
3515 | /* cList needs to be moved from hi_from to hi_to */ | |
3516 | cList->handle = hi_to; | |
3517 | /* Remove from linked list for hi_from */ | |
3518 | assert(!cList->u_prev); | |
3519 | hi_from->channels = cList->u_next; | |
3520 | if (cList->u_next) | |
3521 | cList->u_next->u_prev = cList->u_prev; | |
3522 | /* Add to linked list for hi_to */ | |
3523 | cList->u_prev = NULL; | |
3524 | cList->u_next = hi_to->channels; | |
3525 | if (hi_to->channels) | |
3526 | hi_to->channels->u_prev = cList; | |
3527 | hi_to->channels = cList; | |
3528 | } | |
3529 | } | |
3530 | ||
3531 | /* Do they get an OpServ level promotion? */ | |
3532 | if (hi_from->opserv_level > hi_to->opserv_level) | |
3533 | hi_to->opserv_level = hi_from->opserv_level; | |
3534 | ||
3535 | /* What about last seen time? */ | |
3536 | if (hi_from->lastseen > hi_to->lastseen) | |
3537 | hi_to->lastseen = hi_from->lastseen; | |
3538 | ||
3539 | /* Does a fakehost carry over? (This intentionally doesn't set it | |
3540 | * for users previously attached to hi_to. They'll just have to | |
3541 | * reconnect.) | |
3542 | */ | |
3543 | if (hi_from->fakehost && !hi_to->fakehost) | |
3544 | hi_to->fakehost = strdup(hi_from->fakehost); | |
3545 | ||
3546 | /* Notify of success. */ | |
3547 | sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle); | |
3548 | reply("NSMSG_HANDLES_MERGED", hi_from->handle, hi_to->handle); | |
3549 | global_message(MESSAGE_RECIPIENT_STAFF, buffer); | |
3550 | ||
3551 | /* Unregister the "from" handle. */ | |
3552 | nickserv_unregister_handle(hi_from, NULL, cmd->parent->bot); | |
3553 | ||
3554 | return 1; | |
3555 | } | |
3556 | ||
3557 | struct nickserv_discrim { | |
3558 | unsigned int limit, min_level, max_level; | |
3559 | unsigned long flags_on, flags_off; | |
3560 | time_t min_registered, max_registered; | |
3561 | time_t lastseen; | |
3562 | enum { SUBSET, EXACT, SUPERSET, LASTQUIT } hostmask_type; | |
3563 | const char *nickmask; | |
3564 | const char *hostmask; | |
3565 | const char *handlemask; | |
3566 | const char *emailmask; | |
3567 | }; | |
3568 | ||
3569 | typedef void (*discrim_search_func)(struct userNode *source, struct handle_info *hi); | |
3570 | ||
3571 | struct discrim_apply_info { | |
3572 | struct nickserv_discrim *discrim; | |
3573 | discrim_search_func func; | |
3574 | struct userNode *source; | |
3575 | unsigned int matched; | |
3576 | }; | |
3577 | ||
3578 | static struct nickserv_discrim * | |
3579 | nickserv_discrim_create(struct svccmd *cmd, struct userNode *user, unsigned int argc, char *argv[]) | |
3580 | { | |
3581 | unsigned int i; | |
3582 | struct nickserv_discrim *discrim; | |
3583 | ||
3584 | discrim = malloc(sizeof(*discrim)); | |
3585 | memset(discrim, 0, sizeof(*discrim)); | |
3586 | discrim->min_level = 0; | |
3587 | discrim->max_level = ~0; | |
3588 | discrim->limit = 50; | |
3589 | discrim->min_registered = 0; | |
3590 | discrim->max_registered = INT_MAX; | |
3591 | discrim->lastseen = now; | |
3592 | ||
3593 | for (i=0; i<argc; i++) { | |
3594 | if (i == argc - 1) { | |
3595 | reply("MSG_MISSING_PARAMS", argv[i]); | |
3596 | goto fail; | |
3597 | } | |
3598 | if (!irccasecmp(argv[i], "limit")) { | |
3599 | discrim->limit = strtoul(argv[++i], NULL, 0); | |
3600 | } else if (!irccasecmp(argv[i], "flags")) { | |
3601 | nickserv_modify_handle_flags(user, nickserv, argv[++i], &discrim->flags_on, &discrim->flags_off); | |
3602 | } else if (!irccasecmp(argv[i], "registered")) { | |
3603 | const char *cmp = argv[++i]; | |
3604 | if (cmp[0] == '<') { | |
3605 | if (cmp[1] == '=') { | |
3606 | discrim->min_registered = now - ParseInterval(cmp+2); | |
3607 | } else { | |
3608 | discrim->min_registered = now - ParseInterval(cmp+1) + 1; | |
3609 | } | |
3610 | } else if (cmp[0] == '=') { | |
3611 | discrim->min_registered = discrim->max_registered = now - ParseInterval(cmp+1); | |
3612 | } else if (cmp[0] == '>') { | |
3613 | if (cmp[1] == '=') { | |
3614 | discrim->max_registered = now - ParseInterval(cmp+2); | |
3615 | } else { | |
3616 | discrim->max_registered = now - ParseInterval(cmp+1) - 1; | |
3617 | } | |
3618 | } else { | |
3619 | reply("MSG_INVALID_CRITERIA", cmp); | |
3620 | } | |
3621 | } else if (!irccasecmp(argv[i], "seen")) { | |
3622 | discrim->lastseen = now - ParseInterval(argv[++i]); | |
3623 | } else if (!nickserv_conf.disable_nicks && !irccasecmp(argv[i], "nickmask")) { | |
3624 | discrim->nickmask = argv[++i]; | |
3625 | } else if (!irccasecmp(argv[i], "hostmask")) { | |
3626 | i++; | |
3627 | if (!irccasecmp(argv[i], "exact")) { | |
3628 | if (i == argc - 1) { | |
3629 | reply("MSG_MISSING_PARAMS", argv[i]); | |
3630 | goto fail; | |
3631 | } | |
3632 | discrim->hostmask_type = EXACT; | |
3633 | } else if (!irccasecmp(argv[i], "subset")) { | |
3634 | if (i == argc - 1) { | |
3635 | reply("MSG_MISSING_PARAMS", argv[i]); | |
3636 | goto fail; | |
3637 | } | |
3638 | discrim->hostmask_type = SUBSET; | |
3639 | } else if (!irccasecmp(argv[i], "superset")) { | |
3640 | if (i == argc - 1) { | |
3641 | reply("MSG_MISSING_PARAMS", argv[i]); | |
3642 | goto fail; | |
3643 | } | |
3644 | discrim->hostmask_type = SUPERSET; | |
3645 | } else if (!irccasecmp(argv[i], "lastquit") || !irccasecmp(argv[i], "lastauth")) { | |
3646 | if (i == argc - 1) { | |
3647 | reply("MSG_MISSING_PARAMS", argv[i]); | |
3648 | goto fail; | |
3649 | } | |
3650 | discrim->hostmask_type = LASTQUIT; | |
3651 | } else { | |
3652 | i--; | |
3653 | discrim->hostmask_type = SUPERSET; | |
3654 | } | |
3655 | discrim->hostmask = argv[++i]; | |
3656 | } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) { | |
3657 | if (!irccasecmp(argv[++i], "*")) { | |
3658 | discrim->handlemask = 0; | |
3659 | } else { | |
3660 | discrim->handlemask = argv[i]; | |
3661 | } | |
3662 | } else if (!irccasecmp(argv[i], "email")) { | |
3663 | if (user->handle_info->opserv_level < nickserv_conf.email_search_level) { | |
3664 | reply("MSG_NO_SEARCH_ACCESS", "email"); | |
3665 | goto fail; | |
3666 | } else if (!irccasecmp(argv[++i], "*")) { | |
3667 | discrim->emailmask = 0; | |
3668 | } else { | |
3669 | discrim->emailmask = argv[i]; | |
3670 | } | |
3671 | } else if (!irccasecmp(argv[i], "access")) { | |
3672 | const char *cmp = argv[++i]; | |
3673 | if (cmp[0] == '<') { | |
3674 | if (discrim->min_level == 0) discrim->min_level = 1; | |
3675 | if (cmp[1] == '=') { | |
3676 | discrim->max_level = strtoul(cmp+2, NULL, 0); | |
3677 | } else { | |
3678 | discrim->max_level = strtoul(cmp+1, NULL, 0) - 1; | |
3679 | } | |
3680 | } else if (cmp[0] == '=') { | |
3681 | discrim->min_level = discrim->max_level = strtoul(cmp+1, NULL, 0); | |
3682 | } else if (cmp[0] == '>') { | |
3683 | if (cmp[1] == '=') { | |
3684 | discrim->min_level = strtoul(cmp+2, NULL, 0); | |
3685 | } else { | |
3686 | discrim->min_level = strtoul(cmp+1, NULL, 0) + 1; | |
3687 | } | |
3688 | } else { | |
3689 | reply("MSG_INVALID_CRITERIA", cmp); | |
3690 | } | |
3691 | } else { | |
3692 | reply("MSG_INVALID_CRITERIA", argv[i]); | |
3693 | goto fail; | |
3694 | } | |
3695 | } | |
3696 | return discrim; | |
3697 | fail: | |
3698 | free(discrim); | |
3699 | return NULL; | |
3700 | } | |
3701 | ||
3702 | static int | |
3703 | nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi) | |
3704 | { | |
3705 | if (((discrim->flags_on & hi->flags) != discrim->flags_on) | |
3706 | || (discrim->flags_off & hi->flags) | |
3707 | || (discrim->min_registered > hi->registered) | |
3708 | || (discrim->max_registered < hi->registered) | |
3709 | || (discrim->lastseen < (hi->users?now:hi->lastseen)) | |
3710 | || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask)) | |
3711 | || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask))) | |
3712 | || (discrim->min_level > hi->opserv_level) | |
3713 | || (discrim->max_level < hi->opserv_level)) { | |
3714 | return 0; | |
3715 | } | |
3716 | if (discrim->hostmask) { | |
3717 | unsigned int i; | |
3718 | for (i=0; i<hi->masks->used; i++) { | |
3719 | const char *mask = hi->masks->list[i]; | |
3720 | if ((discrim->hostmask_type == SUBSET) | |
3721 | && (match_ircglobs(discrim->hostmask, mask))) break; | |
3722 | else if ((discrim->hostmask_type == EXACT) | |
3723 | && !irccasecmp(discrim->hostmask, mask)) break; | |
3724 | else if ((discrim->hostmask_type == SUPERSET) | |
3725 | && (match_ircglobs(mask, discrim->hostmask))) break; | |
3726 | else if ((discrim->hostmask_type == LASTQUIT) | |
3727 | && (match_ircglobs(discrim->hostmask, hi->last_quit_host))) break; | |
3728 | } | |
3729 | if (i==hi->masks->used) return 0; | |
3730 | } | |
3731 | if (discrim->nickmask) { | |
3732 | struct nick_info *nick = hi->nicks; | |
3733 | while (nick) { | |
3734 | if (match_ircglob(nick->nick, discrim->nickmask)) break; | |
3735 | nick = nick->next; | |
3736 | } | |
3737 | if (!nick) return 0; | |
3738 | } | |
3739 | return 1; | |
3740 | } | |
3741 | ||
3742 | static unsigned int | |
3743 | nickserv_discrim_search(struct nickserv_discrim *discrim, discrim_search_func dsf, struct userNode *source) | |
3744 | { | |
3745 | dict_iterator_t it, next; | |
3746 | unsigned int matched; | |
3747 | ||
3748 | for (it = dict_first(nickserv_handle_dict), matched = 0; | |
3749 | it && (matched < discrim->limit); | |
3750 | it = next) { | |
3751 | next = iter_next(it); | |
3752 | if (nickserv_discrim_match(discrim, iter_data(it))) { | |
3753 | dsf(source, iter_data(it)); | |
3754 | matched++; | |
3755 | } | |
3756 | } | |
3757 | return matched; | |
3758 | } | |
3759 | ||
3760 | static void | |
3761 | search_print_func(struct userNode *source, struct handle_info *match) | |
3762 | { | |
3763 | send_message(source, nickserv, "NSMSG_SEARCH_MATCH", match->handle); | |
3764 | } | |
3765 | ||
3766 | static void | |
3767 | search_count_func(UNUSED_ARG(struct userNode *source), UNUSED_ARG(struct handle_info *match)) | |
3768 | { | |
3769 | } | |
3770 | ||
3771 | static void | |
3772 | search_unregister_func (struct userNode *source, struct handle_info *match) | |
3773 | { | |
3774 | if (oper_has_access(source, nickserv, match->opserv_level, 0)) | |
3775 | nickserv_unregister_handle(match, source, nickserv); // XXX nickserv hard coded | |
3776 | } | |
3777 | ||
3778 | static int | |
3779 | nickserv_sort_accounts_by_access(const void *a, const void *b) | |
3780 | { | |
3781 | const struct handle_info *hi_a = *(const struct handle_info**)a; | |
3782 | const struct handle_info *hi_b = *(const struct handle_info**)b; | |
3783 | if (hi_a->opserv_level != hi_b->opserv_level) | |
3784 | return hi_b->opserv_level - hi_a->opserv_level; | |
3785 | return irccasecmp(hi_a->handle, hi_b->handle); | |
3786 | } | |
3787 | ||
3788 | void | |
3789 | nickserv_show_oper_accounts(struct userNode *user, struct svccmd *cmd) | |
3790 | { | |
3791 | struct handle_info_list hil; | |
3792 | struct helpfile_table tbl; | |
3793 | unsigned int ii; | |
3794 | dict_iterator_t it; | |
3795 | const char **ary; | |
3796 | ||
3797 | memset(&hil, 0, sizeof(hil)); | |
3798 | for (it = dict_first(nickserv_handle_dict); it; it = iter_next(it)) { | |
3799 | struct handle_info *hi = iter_data(it); | |
3800 | if (hi->opserv_level) | |
3801 | handle_info_list_append(&hil, hi); | |
3802 | } | |
3803 | qsort(hil.list, hil.used, sizeof(hil.list[0]), nickserv_sort_accounts_by_access); | |
3804 | tbl.length = hil.used + 1; | |
3805 | tbl.width = 2; | |
3806 | tbl.flags = TABLE_NO_FREE | TABLE_REPEAT_ROWS | TABLE_REPEAT_HEADERS; | |
3807 | tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0])); | |
3808 | tbl.contents[0] = ary = malloc(tbl.width * sizeof(ary[0])); | |
3809 | ary[0] = "Account"; | |
3810 | ary[1] = "Level"; | |
3811 | for (ii = 0; ii < hil.used; ) { | |
3812 | ary = malloc(tbl.width * sizeof(ary[0])); | |
3813 | ary[0] = hil.list[ii]->handle; | |
3814 | ary[1] = strtab(hil.list[ii]->opserv_level); | |
3815 | tbl.contents[++ii] = ary; | |
3816 | } | |
3817 | table_send(cmd->parent->bot, user->nick, 0, NULL, tbl); | |
3818 | /*reply("MSG_MATCH_COUNT", hil.used); */ | |
3819 | for (ii = 0; ii < hil.used; ii++) | |
3820 | free(tbl.contents[ii]); | |
3821 | free(tbl.contents); | |
3822 | free(hil.list); | |
3823 | } | |
3824 | ||
3825 | static NICKSERV_FUNC(cmd_search) | |
3826 | { | |
3827 | struct nickserv_discrim *discrim; | |
3828 | discrim_search_func action; | |
3829 | struct svccmd *subcmd; | |
3830 | unsigned int matches; | |
3831 | char buf[MAXLEN]; | |
3832 | ||
3833 | NICKSERV_MIN_PARMS(3); | |
3834 | sprintf(buf, "search %s", argv[1]); | |
3835 | subcmd = dict_find(nickserv_service->commands, buf, NULL); | |
3836 | if (!irccasecmp(argv[1], "print")) | |
3837 | action = search_print_func; | |
3838 | else if (!irccasecmp(argv[1], "count")) | |
3839 | action = search_count_func; | |
3840 | else if (!irccasecmp(argv[1], "unregister")) | |
3841 | action = search_unregister_func; | |
3842 | else { | |
3843 | reply("NSMSG_INVALID_ACTION", argv[1]); | |
3844 | return 0; | |
3845 | } | |
3846 | ||
3847 | if (subcmd && !svccmd_can_invoke(user, nickserv, subcmd, NULL, SVCCMD_NOISY)) | |
3848 | return 0; | |
3849 | ||
3850 | discrim = nickserv_discrim_create(cmd, user, argc-2, argv+2); | |
3851 | if (!discrim) | |
3852 | return 0; | |
3853 | ||
3854 | if (action == search_print_func) | |
3855 | reply("NSMSG_ACCOUNT_SEARCH_RESULTS"); | |
3856 | else if (action == search_count_func) | |
3857 | discrim->limit = INT_MAX; | |
3858 | ||
3859 | matches = nickserv_discrim_search(discrim, action, user); | |
3860 | ||
3861 | if (matches) | |
3862 | reply("MSG_MATCH_COUNT", matches); | |
3863 | else | |
3864 | reply("MSG_NO_MATCHES"); | |
3865 | ||
3866 | free(discrim); | |
3867 | return 0; | |
3868 | } | |
3869 | ||
3870 | static MODCMD_FUNC(cmd_checkpass) | |
3871 | { | |
3872 | struct handle_info *hi; | |
3873 | ||
3874 | NICKSERV_MIN_PARMS(3); | |
3875 | if (!(hi = get_handle_info(argv[1]))) { | |
3876 | reply("MSG_HANDLE_UNKNOWN", argv[1]); | |
3877 | return 0; | |
3878 | } | |
3879 | if (checkpass(argv[2], hi->passwd)) | |
3880 | reply("CHECKPASS_YES"); | |
3881 | else | |
3882 | reply("CHECKPASS_NO"); | |
3883 | argv[2] = "****"; | |
3884 | return 1; | |
3885 | } | |
3886 | ||
3887 | static void | |
3888 | nickserv_db_read_handle(const char *handle, dict_t obj) | |
3889 | { | |
3890 | const char *str; | |
3891 | struct string_list *masks, *slist, *ignores; | |
3892 | struct handle_info *hi; | |
3893 | struct userNode *authed_users; | |
3894 | struct userData *channels; | |
3895 | unsigned long int id; | |
3896 | unsigned int ii; | |
3897 | dict_t subdb; | |
3898 | char *setter, *note; | |
3899 | time_t date; | |
3900 | ||
3901 | str = database_get_data(obj, KEY_ID, RECDB_QSTRING); | |
3902 | id = str ? strtoul(str, NULL, 0) : 0; | |
3903 | str = database_get_data(obj, KEY_PASSWD, RECDB_QSTRING); | |
3904 | if (!str) { | |
3905 | log_module(NS_LOG, LOG_WARNING, "did not find a password for %s -- skipping user.", handle); | |
3906 | return; | |
3907 | } | |
3908 | if ((hi = get_handle_info(handle))) { | |
3909 | authed_users = hi->users; | |
3910 | channels = hi->channels; | |
3911 | hi->users = NULL; | |
3912 | hi->channels = NULL; | |
3913 | dict_remove(nickserv_handle_dict, hi->handle); | |
3914 | } else { | |
3915 | authed_users = NULL; | |
3916 | channels = NULL; | |
3917 | } | |
3918 | hi = register_handle(handle, str, id); | |
3919 | if (authed_users) { | |
3920 | hi->users = authed_users; | |
3921 | while (authed_users) { | |
3922 | authed_users->handle_info = hi; | |
3923 | authed_users = authed_users->next_authed; | |
3924 | } | |
3925 | } | |
3926 | hi->channels = channels; | |
3927 | masks = database_get_data(obj, KEY_MASKS, RECDB_STRING_LIST); | |
3928 | hi->masks = masks ? string_list_copy(masks) : alloc_string_list(1); | |
3929 | ignores = database_get_data(obj, KEY_IGNORES, RECDB_STRING_LIST); | |
3930 | hi->ignores = ignores ? string_list_copy(ignores) : alloc_string_list(1); | |
3931 | str = database_get_data(obj, KEY_MAXLOGINS, RECDB_QSTRING); | |
3932 | hi->maxlogins = str ? strtoul(str, NULL, 0) : 0; | |
3933 | str = database_get_data(obj, KEY_LANGUAGE, RECDB_QSTRING); | |
3934 | hi->language = language_find(str ? str : "C"); | |
3935 | str = database_get_data(obj, KEY_OPSERV_LEVEL, RECDB_QSTRING); | |
3936 | hi->opserv_level = str ? strtoul(str, NULL, 0) : 0; | |
3937 | str = database_get_data(obj, KEY_INFO, RECDB_QSTRING); | |
3938 | if (str) | |
3939 | hi->infoline = strdup(str); | |
3940 | str = database_get_data(obj, KEY_REGISTER_ON, RECDB_QSTRING); | |
3941 | hi->registered = str ? (time_t)strtoul(str, NULL, 0) : now; | |
3942 | str = database_get_data(obj, KEY_LAST_SEEN, RECDB_QSTRING); | |
3943 | hi->lastseen = str ? (time_t)strtoul(str, NULL, 0) : hi->registered; | |
3944 | /* We want to read the nicks even if disable_nicks is set. This is so | |
3945 | * that we don't lose the nick data entirely. */ | |
3946 | slist = database_get_data(obj, KEY_NICKS, RECDB_STRING_LIST); | |
3947 | if (slist) { | |
3948 | for (ii=0; ii<slist->used; ii++) | |
3949 | register_nick(slist->list[ii], hi); | |
3950 | } | |
3951 | str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING); | |
3952 | if (str) { | |
3953 | for (ii=0; str[ii]; ii++) | |
3954 | hi->flags |= 1 << (handle_inverse_flags[(unsigned char)str[ii]] - 1); | |
3955 | } | |
3956 | str = database_get_data(obj, KEY_USERLIST_STYLE, RECDB_QSTRING); | |
3957 | hi->userlist_style = str ? str[0] : HI_DEFAULT_STYLE; | |
3958 | str = database_get_data(obj, KEY_ANNOUNCEMENTS, RECDB_QSTRING); | |
3959 | hi->announcements = str ? str[0] : '?'; | |
3960 | str = database_get_data(obj, KEY_SCREEN_WIDTH, RECDB_QSTRING); | |
3961 | hi->screen_width = str ? strtoul(str, NULL, 0) : 0; | |
3962 | str = database_get_data(obj, KEY_TABLE_WIDTH, RECDB_QSTRING); | |
3963 | hi->table_width = str ? strtoul(str, NULL, 0) : 0; | |
3964 | str = database_get_data(obj, KEY_LAST_QUIT_HOST, RECDB_QSTRING); | |
3965 | if (!str) | |
3966 | str = database_get_data(obj, KEY_LAST_AUTHED_HOST, RECDB_QSTRING); | |
3967 | if (str) | |
3968 | safestrncpy(hi->last_quit_host, str, sizeof(hi->last_quit_host)); | |
3969 | str = database_get_data(obj, KEY_EMAIL_ADDR, RECDB_QSTRING); | |
3970 | if (str) | |
3971 | nickserv_set_email_addr(hi, str); | |
3972 | str = database_get_data(obj, KEY_EPITHET, RECDB_QSTRING); | |
3973 | if (str) | |
3974 | hi->epithet = strdup(str); | |
3975 | subdb = database_get_data(obj, KEY_NOTE_NOTE, RECDB_OBJECT); | |
3976 | if (subdb) { | |
3977 | setter = database_get_data(subdb, KEY_NOTE_SETTER, RECDB_QSTRING); | |
3978 | str = database_get_data(subdb, KEY_NOTE_DATE, RECDB_QSTRING); | |
3979 | date = str ? (time_t)strtoul(str, NULL, 0) : now; | |
3980 | note = database_get_data(subdb, KEY_NOTE_NOTE, RECDB_QSTRING); | |
3981 | if (setter && date && note) | |
3982 | { | |
3983 | if (!(hi->note = nickserv_add_note(setter, date, note))) | |
3984 | hi->note = NULL; | |
3985 | } | |
3986 | } | |
3987 | ||
3988 | str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING); | |
3989 | if (str) | |
3990 | hi->fakehost = strdup(str); | |
3991 | ||
3992 | subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT); | |
3993 | if (subdb) { | |
3994 | const char *data, *type, *expires, *cookie_str; | |
3995 | struct handle_cookie *cookie; | |
3996 | ||
3997 | cookie = calloc(1, sizeof(*cookie)); | |
3998 | type = database_get_data(subdb, KEY_COOKIE_TYPE, RECDB_QSTRING); | |
3999 | data = database_get_data(subdb, KEY_COOKIE_DATA, RECDB_QSTRING); | |
4000 | expires = database_get_data(subdb, KEY_COOKIE_EXPIRES, RECDB_QSTRING); | |
4001 | cookie_str = database_get_data(subdb, KEY_COOKIE, RECDB_QSTRING); | |
4002 | if (!type || !expires || !cookie_str) { | |
4003 | log_module(NS_LOG, LOG_ERROR, "Missing field(s) from cookie for account %s; dropping cookie.", hi->handle); | |
4004 | goto cookie_out; | |
4005 | } | |
4006 | if (!irccasecmp(type, KEY_ACTIVATION)) | |
4007 | cookie->type = ACTIVATION; | |
4008 | else if (!irccasecmp(type, KEY_PASSWORD_CHANGE)) | |
4009 | cookie->type = PASSWORD_CHANGE; | |
4010 | else if (!irccasecmp(type, KEY_EMAIL_CHANGE)) | |
4011 | cookie->type = EMAIL_CHANGE; | |
4012 | else if (!irccasecmp(type, KEY_ALLOWAUTH)) | |
4013 | cookie->type = ALLOWAUTH; | |
4014 | else { | |
4015 | log_module(NS_LOG, LOG_ERROR, "Invalid cookie type %s for account %s; dropping cookie.", type, handle); | |
4016 | goto cookie_out; | |
4017 | } | |
4018 | cookie->expires = strtoul(expires, NULL, 0); | |
4019 | if (cookie->expires < now) | |
4020 | goto cookie_out; | |
4021 | if (data) | |
4022 | cookie->data = strdup(data); | |
4023 | safestrncpy(cookie->cookie, cookie_str, sizeof(cookie->cookie)); | |
4024 | cookie->hi = hi; | |
4025 | cookie_out: | |
4026 | if (cookie->hi) | |
4027 | nickserv_bake_cookie(cookie); | |
4028 | else | |
4029 | nickserv_free_cookie(cookie); | |
4030 | } | |
4031 | } | |
4032 | ||
4033 | static int | |
4034 | nickserv_saxdb_read(dict_t db) { | |
4035 | dict_iterator_t it; | |
4036 | struct record_data *rd; | |
4037 | ||
4038 | for (it=dict_first(db); it; it=iter_next(it)) { | |
4039 | rd = iter_data(it); | |
4040 | nickserv_db_read_handle(iter_key(it), rd->d.object); | |
4041 | } | |
4042 | return 0; | |
4043 | } | |
4044 | ||
4045 | static NICKSERV_FUNC(cmd_mergedb) | |
4046 | { | |
4047 | struct timeval start, stop; | |
4048 | dict_t db; | |
4049 | ||
4050 | NICKSERV_MIN_PARMS(2); | |
4051 | gettimeofday(&start, NULL); | |
4052 | if (!(db = parse_database(argv[1]))) { | |
4053 | reply("NSMSG_DB_UNREADABLE", argv[1]); | |
4054 | return 0; | |
4055 | } | |
4056 | nickserv_saxdb_read(db); | |
4057 | free_database(db); | |
4058 | gettimeofday(&stop, NULL); | |
4059 | stop.tv_sec -= start.tv_sec; | |
4060 | stop.tv_usec -= start.tv_usec; | |
4061 | if (stop.tv_usec < 0) { | |
4062 | stop.tv_sec -= 1; | |
4063 | stop.tv_usec += 1000000; | |
4064 | } | |
4065 | reply("NSMSG_DB_MERGED", argv[1], stop.tv_sec, stop.tv_usec/1000); | |
4066 | return 1; | |
4067 | } | |
4068 | ||
4069 | static void | |
4070 | expire_handles(UNUSED_ARG(void *data)) | |
4071 | { | |
4072 | dict_iterator_t it, next; | |
4073 | time_t expiry; | |
4074 | struct handle_info *hi; | |
4075 | ||
4076 | for (it=dict_first(nickserv_handle_dict); it; it=next) { | |
4077 | next = iter_next(it); | |
4078 | hi = iter_data(it); | |
4079 | if ((hi->opserv_level > 0) | |
4080 | || hi->users | |
4081 | || HANDLE_FLAGGED(hi, FROZEN) | |
4082 | || HANDLE_FLAGGED(hi, NODELETE)) { | |
4083 | continue; | |
4084 | } | |
4085 | expiry = hi->channels ? nickserv_conf.handle_expire_delay : nickserv_conf.nochan_handle_expire_delay; | |
4086 | if ((now - hi->lastseen) > expiry) { | |
4087 | log_module(NS_LOG, LOG_INFO, "Expiring account %s for inactivity.", hi->handle); | |
4088 | nickserv_unregister_handle(hi, NULL, NULL); | |
4089 | } | |
4090 | } | |
4091 | ||
4092 | if (nickserv_conf.handle_expire_frequency) | |
4093 | timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL); | |
4094 | } | |
4095 | ||
4096 | static void | |
4097 | nickserv_load_dict(const char *fname) | |
4098 | { | |
4099 | FILE *file; | |
4100 | char line[128]; | |
4101 | if (!(file = fopen(fname, "r"))) { | |
4102 | log_module(NS_LOG, LOG_ERROR, "Unable to open dictionary file %s: %s", fname, strerror(errno)); | |
4103 | return; | |
4104 | } | |
4105 | while (!feof(file)) { | |
4106 | fgets(line, sizeof(line), file); | |
4107 | if (!line[0]) | |
4108 | continue; | |
4109 | if (line[strlen(line)-1] == '\n') | |
4110 | line[strlen(line)-1] = 0; | |
4111 | dict_insert(nickserv_conf.weak_password_dict, strdup(line), NULL); | |
4112 | } | |
4113 | fclose(file); | |
4114 | log_module(NS_LOG, LOG_INFO, "Loaded %d words into weak password dictionary.", dict_size(nickserv_conf.weak_password_dict)); | |
4115 | } | |
4116 | ||
4117 | static enum reclaim_action | |
4118 | reclaim_action_from_string(const char *str) { | |
4119 | if (!str) | |
4120 | return RECLAIM_NONE; | |
4121 | else if (!irccasecmp(str, "warn")) | |
4122 | return RECLAIM_WARN; | |
4123 | else if (!irccasecmp(str, "svsnick")) | |
4124 | return RECLAIM_SVSNICK; | |
4125 | else if (!irccasecmp(str, "kill")) | |
4126 | return RECLAIM_KILL; | |
4127 | else | |
4128 | return RECLAIM_NONE; | |
4129 | } | |
4130 | ||
4131 | static void | |
4132 | nickserv_conf_read(void) | |
4133 | { | |
4134 | dict_t conf_node, child; | |
4135 | const char *str; | |
4136 | dict_iterator_t it; | |
4137 | struct string_list *strlist; | |
4138 | ||
4139 | if (!(conf_node = conf_get_data(NICKSERV_CONF_NAME, RECDB_OBJECT))) { | |
4140 | log_module(NS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", NICKSERV_CONF_NAME); | |
4141 | return; | |
4142 | } | |
4143 | str = database_get_data(conf_node, KEY_VALID_HANDLE_REGEX, RECDB_QSTRING); | |
4144 | if (!str) | |
4145 | str = database_get_data(conf_node, KEY_VALID_ACCOUNT_REGEX, RECDB_QSTRING); | |
4146 | if (nickserv_conf.valid_handle_regex_set) | |
4147 | regfree(&nickserv_conf.valid_handle_regex); | |
4148 | if (str) { | |
4149 | int err = regcomp(&nickserv_conf.valid_handle_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB); | |
4150 | nickserv_conf.valid_handle_regex_set = !err; | |
4151 | if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_account_regex (error %d)", err); | |
4152 | } else { | |
4153 | nickserv_conf.valid_handle_regex_set = 0; | |
4154 | } | |
4155 | str = database_get_data(conf_node, KEY_VALID_NICK_REGEX, RECDB_QSTRING); | |
4156 | if (nickserv_conf.valid_nick_regex_set) | |
4157 | regfree(&nickserv_conf.valid_nick_regex); | |
4158 | if (str) { | |
4159 | int err = regcomp(&nickserv_conf.valid_nick_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB); | |
4160 | nickserv_conf.valid_nick_regex_set = !err; | |
4161 | if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_nick_regex (error %d)", err); | |
4162 | } else { | |
4163 | nickserv_conf.valid_nick_regex_set = 0; | |
4164 | } | |
4165 | str = database_get_data(conf_node, KEY_VALID_FAKEHOST_REGEX, RECDB_QSTRING); | |
4166 | if (nickserv_conf.valid_fakehost_regex_set) | |
4167 | regfree(&nickserv_conf.valid_fakehost_regex); | |
4168 | if (str) { | |
4169 | int err = regcomp(&nickserv_conf.valid_fakehost_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB); | |
4170 | nickserv_conf.valid_fakehost_regex_set = !err; | |
4171 | if (err) log_module(NS_LOG, LOG_ERROR, "Bad valid_fakehost_regex (error %d)", err); | |
4172 | } else { | |
4173 | nickserv_conf.valid_fakehost_regex_set = 0; | |
4174 | } | |
4175 | str = database_get_data(conf_node, KEY_NICKS_PER_HANDLE, RECDB_QSTRING); | |
4176 | if (!str) | |
4177 | str = database_get_data(conf_node, KEY_NICKS_PER_ACCOUNT, RECDB_QSTRING); | |
4178 | nickserv_conf.nicks_per_handle = str ? strtoul(str, NULL, 0) : 4; | |
4179 | str = database_get_data(conf_node, KEY_DISABLE_NICKS, RECDB_QSTRING); | |
4180 | nickserv_conf.disable_nicks = str ? strtoul(str, NULL, 0) : 0; | |
4181 | str = database_get_data(conf_node, KEY_DEFAULT_HOSTMASK, RECDB_QSTRING); | |
4182 | nickserv_conf.default_hostmask = str ? !disabled_string(str) : 0; | |
4183 | str = database_get_data(conf_node, KEY_PASSWORD_MIN_LENGTH, RECDB_QSTRING); | |
4184 | nickserv_conf.password_min_length = str ? strtoul(str, NULL, 0) : 0; | |
4185 | str = database_get_data(conf_node, KEY_PASSWORD_MIN_DIGITS, RECDB_QSTRING); | |
4186 | nickserv_conf.password_min_digits = str ? strtoul(str, NULL, 0) : 0; | |
4187 | str = database_get_data(conf_node, KEY_PASSWORD_MIN_UPPER, RECDB_QSTRING); | |
4188 | nickserv_conf.password_min_upper = str ? strtoul(str, NULL, 0) : 0; | |
4189 | str = database_get_data(conf_node, KEY_PASSWORD_MIN_LOWER, RECDB_QSTRING); | |
4190 | nickserv_conf.password_min_lower = str ? strtoul(str, NULL, 0) : 0; | |
4191 | str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING); | |
4192 | nickserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200; | |
4193 | str = database_get_data(conf_node, KEY_MODOPER_LEVEL, RECDB_QSTRING); | |
4194 | nickserv_conf.modoper_level = str ? strtoul(str, NULL, 0) : 900; | |
4195 | str = database_get_data(conf_node, KEY_SET_EPITHET_LEVEL, RECDB_QSTRING); | |
4196 | nickserv_conf.set_epithet_level = str ? strtoul(str, NULL, 0) : 1; | |
4197 | str = database_get_data(conf_node, KEY_SET_TITLE_LEVEL, RECDB_QSTRING); | |
4198 | nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900; | |
4199 | str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING); | |
4200 | nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000; | |
4201 | str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING); | |
4202 | if (!str) | |
4203 | str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING); | |
4204 | nickserv_conf.handle_expire_frequency = str ? ParseInterval(str) : 86400; | |
4205 | str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_DELAY, RECDB_QSTRING); | |
4206 | if (!str) | |
4207 | str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING); | |
4208 | nickserv_conf.handle_expire_delay = str ? ParseInterval(str) : 86400*30; | |
4209 | str = database_get_data(conf_node, KEY_NOCHAN_HANDLE_EXPIRE_DELAY, RECDB_QSTRING); | |
4210 | if (!str) | |
4211 | str = database_get_data(conf_node, KEY_NOCHAN_ACCOUNT_EXPIRE_DELAY, RECDB_QSTRING); | |
4212 | nickserv_conf.nochan_handle_expire_delay = str ? ParseInterval(str) : 86400*15; | |
4213 | str = database_get_data(conf_node, "warn_clone_auth", RECDB_QSTRING); | |
4214 | nickserv_conf.warn_clone_auth = str ? !disabled_string(str) : 1; | |
4215 | str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING); | |
4216 | nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2; | |
4217 | str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING); | |
4218 | nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10; | |
4219 | if (!nickserv_conf.disable_nicks) { | |
4220 | str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING); | |
4221 | nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE; | |
4222 | str = database_get_data(conf_node, "warn_nick_owned", RECDB_QSTRING); | |
4223 | nickserv_conf.warn_nick_owned = str ? enabled_string(str) : 0; | |
4224 | str = database_get_data(conf_node, "auto_reclaim_action", RECDB_QSTRING); | |
4225 | nickserv_conf.auto_reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE; | |
4226 | str = database_get_data(conf_node, "auto_reclaim_delay", RECDB_QSTRING); | |
4227 | nickserv_conf.auto_reclaim_delay = str ? ParseInterval(str) : 0; | |
4228 | } | |
4229 | child = database_get_data(conf_node, KEY_FLAG_LEVELS, RECDB_OBJECT); | |
4230 | for (it=dict_first(child); it; it=iter_next(it)) { | |
4231 | const char *key = iter_key(it), *value; | |
4232 | unsigned char flag; | |
4233 | int pos; | |
4234 | ||
4235 | if (!strncasecmp(key, "uc_", 3)) | |
4236 | flag = toupper(key[3]); | |
4237 | else if (!strncasecmp(key, "lc_", 3)) | |
4238 | flag = tolower(key[3]); | |
4239 | else | |
4240 | flag = key[0]; | |
4241 | ||
4242 | if ((pos = handle_inverse_flags[flag])) { | |
4243 | value = GET_RECORD_QSTRING((struct record_data*)iter_data(it)); | |
4244 | flag_access_levels[pos - 1] = strtoul(value, NULL, 0); | |
4245 | } | |
4246 | } | |
4247 | if (nickserv_conf.weak_password_dict) | |
4248 | dict_delete(nickserv_conf.weak_password_dict); | |
4249 | nickserv_conf.weak_password_dict = dict_new(); | |
4250 | dict_set_free_keys(nickserv_conf.weak_password_dict, free); | |
4251 | dict_insert(nickserv_conf.weak_password_dict, strdup("password"), NULL); | |
4252 | dict_insert(nickserv_conf.weak_password_dict, strdup("<password>"), NULL); | |
4253 | str = database_get_data(conf_node, KEY_DICT_FILE, RECDB_QSTRING); | |
4254 | if (str) | |
4255 | nickserv_load_dict(str); | |
4256 | str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING); | |
4257 | if (nickserv && str) | |
4258 | NickChange(nickserv, str, 0); | |
4259 | str = database_get_data(conf_node, KEY_AUTOGAG_ENABLED, RECDB_QSTRING); | |
4260 | nickserv_conf.autogag_enabled = str ? strtoul(str, NULL, 0) : 1; | |
4261 | str = database_get_data(conf_node, KEY_AUTOGAG_DURATION, RECDB_QSTRING); | |
4262 | nickserv_conf.autogag_duration = str ? ParseInterval(str) : 1800; | |
4263 | str = database_get_data(conf_node, KEY_EMAIL_VISIBLE_LEVEL, RECDB_QSTRING); | |
4264 | nickserv_conf.email_visible_level = str ? strtoul(str, NULL, 0) : 800; | |
4265 | str = database_get_data(conf_node, KEY_EMAIL_ENABLED, RECDB_QSTRING); | |
4266 | nickserv_conf.email_enabled = str ? enabled_string(str) : 0; | |
4267 | str = database_get_data(conf_node, KEY_SYNC_LOG, RECDB_QSTRING); | |
4268 | nickserv_conf.sync_log = str ? enabled_string(str) : 0; | |
4269 | str = database_get_data(conf_node, KEY_COOKIE_TIMEOUT, RECDB_QSTRING); | |
4270 | nickserv_conf.cookie_timeout = str ? ParseInterval(str) : 24*3600; | |
4271 | str = database_get_data(conf_node, KEY_EMAIL_REQUIRED, RECDB_QSTRING); | |
4272 | nickserv_conf.email_required = (nickserv_conf.email_enabled && str) ? enabled_string(str) : 0; | |
4273 | str = database_get_data(conf_node, KEY_ACCOUNTS_PER_EMAIL, RECDB_QSTRING); | |
4274 | nickserv_conf.handles_per_email = str ? strtoul(str, NULL, 0) : 1; | |
4275 | str = database_get_data(conf_node, KEY_EMAIL_SEARCH_LEVEL, RECDB_QSTRING); | |
4276 | nickserv_conf.email_search_level = str ? strtoul(str, NULL, 0) : 600; | |
4277 | str = database_get_data(conf_node, KEY_TITLEHOST_SUFFIX, RECDB_QSTRING); | |
4278 | nickserv_conf.titlehost_suffix = str ? str : "example.net"; | |
4279 | ||
4280 | free_string_list(nickserv_conf.denied_fakehost_words); | |
4281 | strlist = database_get_data(conf_node, KEY_DENIED_FAKEHOST_WORDS, RECDB_STRING_LIST); | |
4282 | if(strlist) | |
4283 | strlist = string_list_copy(strlist); | |
4284 | else { | |
4285 | strlist = alloc_string_list(4); | |
4286 | string_list_append(strlist, strdup("sex")); | |
4287 | string_list_append(strlist, strdup("fuck")); | |
4288 | } | |
4289 | nickserv_conf.denied_fakehost_words = strlist; | |
4290 | ||
4291 | str = database_get_data(conf_node, KEY_DEFAULT_STYLE, RECDB_QSTRING); | |
4292 | nickserv_conf.default_style = str ? str[0] : HI_DEFAULT_STYLE; | |
4293 | ||
4294 | str = database_get_data(conf_node, KEY_AUTO_OPER, RECDB_QSTRING); | |
4295 | nickserv_conf.auto_oper = str ? str : ""; | |
4296 | ||
4297 | str = database_get_data(conf_node, KEY_AUTO_ADMIN, RECDB_QSTRING); | |
4298 | nickserv_conf.auto_admin = str ? str : ""; | |
4299 | ||
4300 | str = conf_get_data("server/network", RECDB_QSTRING); | |
4301 | nickserv_conf.network_name = str ? str : "some IRC network"; | |
4302 | if (!nickserv_conf.auth_policer_params) { | |
4303 | nickserv_conf.auth_policer_params = policer_params_new(); | |
4304 | policer_params_set(nickserv_conf.auth_policer_params, "size", "5"); | |
4305 | policer_params_set(nickserv_conf.auth_policer_params, "drain-rate", "0.05"); | |
4306 | } | |
4307 | child = database_get_data(conf_node, KEY_AUTH_POLICER, RECDB_OBJECT); | |
4308 | for (it=dict_first(child); it; it=iter_next(it)) | |
4309 | set_policer_param(iter_key(it), iter_data(it), nickserv_conf.auth_policer_params); | |
4310 | } | |
4311 | ||
4312 | static void | |
4313 | nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_action action) { | |
4314 | const char *msg; | |
4315 | char newnick[NICKLEN+1]; | |
4316 | ||
4317 | assert(user); | |
4318 | assert(ni); | |
4319 | switch (action) { | |
4320 | case RECLAIM_NONE: | |
4321 | /* do nothing */ | |
4322 | break; | |
4323 | case RECLAIM_WARN: | |
4324 | send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle); | |
4325 | break; | |
4326 | case RECLAIM_SVSNICK: | |
4327 | do { | |
4328 | snprintf(newnick, sizeof(newnick), "Guest%d", rand()%10000); | |
4329 | } while (GetUserH(newnick)); | |
4330 | irc_svsnick(nickserv, user, newnick); | |
4331 | break; | |
4332 | case RECLAIM_KILL: | |
4333 | msg = user_find_message(user, "NSMSG_RECLAIM_KILL"); | |
4334 | irc_kill(nickserv, user, msg); | |
4335 | break; | |
4336 | } | |
4337 | } | |
4338 | ||
4339 | static void | |
4340 | nickserv_reclaim_p(void *data) { | |
4341 | struct userNode *user = data; | |
4342 | struct nick_info *ni = get_nick_info(user->nick); | |
4343 | if (ni) | |
4344 | nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action); | |
4345 | } | |
4346 | ||
4347 | static int | |
4348 | check_user_nick(struct userNode *user) { | |
4349 | struct nick_info *ni; | |
4350 | user->modes &= ~FLAGS_REGNICK; | |
4351 | if (!(ni = get_nick_info(user->nick))) | |
4352 | return 0; | |
4353 | if (user->handle_info == ni->owner) { | |
4354 | user->modes |= FLAGS_REGNICK; | |
4355 | irc_regnick(user); | |
4356 | return 0; | |
4357 | } | |
4358 | if (nickserv_conf.warn_nick_owned) | |
4359 | send_message(user, nickserv, "NSMSG_RECLAIM_WARN", ni->nick, ni->owner->handle); | |
4360 | if (nickserv_conf.auto_reclaim_action == RECLAIM_NONE) | |
4361 | return 0; | |
4362 | if (nickserv_conf.auto_reclaim_delay) | |
4363 | timeq_add(now + nickserv_conf.auto_reclaim_delay, nickserv_reclaim_p, user); | |
4364 | else | |
4365 | nickserv_reclaim(user, ni, nickserv_conf.auto_reclaim_action); | |
4366 | return 0; | |
4367 | } | |
4368 | ||
4369 | int | |
4370 | handle_new_user(struct userNode *user) | |
4371 | { | |
4372 | return check_user_nick(user); | |
4373 | } | |
4374 | ||
4375 | void | |
4376 | handle_account(struct userNode *user, const char *stamp) | |
4377 | { | |
4378 | struct handle_info *hi; | |
4379 | char *colon; | |
4380 | ||
4381 | #ifdef WITH_PROTOCOL_P10 | |
4382 | time_t timestamp = 0; | |
4383 | ||
4384 | colon = strchr(stamp, ':'); | |
4385 | if(colon && colon[1]) | |
4386 | { | |
4387 | *colon = 0; | |
4388 | timestamp = atoi(colon+1); | |
4389 | } | |
4390 | hi = dict_find(nickserv_handle_dict, stamp, NULL); | |
4391 | if(hi && timestamp && hi->registered != timestamp) | |
4392 | { | |
4393 | log_module(MAIN_LOG, LOG_WARNING, "%s using account %s but timestamp does not match %lu is not %lu.", user->nick, stamp, timestamp, hi->registered); | |
4394 | return; | |
4395 | } | |
4396 | #else | |
4397 | hi = dict_find(nickserv_id_dict, stamp, NULL); | |
4398 | log_module(MAIN_LOG, LOG_WARNING, "Using non-P10 code in accounts, not tested at all!"); | |
4399 | #endif | |
4400 | ||
4401 | if (hi) { | |
4402 | if (HANDLE_FLAGGED(hi, SUSPENDED)) { | |
4403 | return; | |
4404 | } | |
4405 | set_user_handle_info(user, hi, 0); | |
4406 | } else { | |
4407 | log_module(MAIN_LOG, LOG_WARNING, "%s had unknown account stamp %s.", user->nick, stamp); | |
4408 | } | |
4409 | } | |
4410 | ||
4411 | void | |
4412 | handle_nick_change(struct userNode *user, const char *old_nick) | |
4413 | { | |
4414 | struct handle_info *hi; | |
4415 | ||
4416 | if ((hi = dict_find(nickserv_allow_auth_dict, old_nick, 0))) { | |
4417 | dict_remove(nickserv_allow_auth_dict, old_nick); | |
4418 | dict_insert(nickserv_allow_auth_dict, user->nick, hi); | |
4419 | } | |
4420 | timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN); | |
4421 | check_user_nick(user); | |
4422 | } | |
4423 | ||
4424 | void | |
4425 | nickserv_remove_user(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why)) | |
4426 | { | |
4427 | dict_remove(nickserv_allow_auth_dict, user->nick); | |
4428 | timeq_del(0, nickserv_reclaim_p, user, TIMEQ_IGNORE_WHEN); | |
4429 | set_user_handle_info(user, NULL, 0); | |
4430 | } | |
4431 | ||
4432 | static struct modcmd * | |
4433 | nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int must_auth, int must_be_qualified) | |
4434 | { | |
4435 | if (min_level > 0) { | |
4436 | char buf[16]; | |
4437 | sprintf(buf, "%u", min_level); | |
4438 | if (must_be_qualified) { | |
4439 | return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, "flags", "+qualified,+loghostmask", NULL); | |
4440 | } else { | |
4441 | return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "level", buf, NULL); | |
4442 | } | |
4443 | } else if (min_level == 0) { | |
4444 | if (must_be_qualified) { | |
4445 | return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL); | |
4446 | } else { | |
4447 | return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+helping", NULL); | |
4448 | } | |
4449 | } else { | |
4450 | if (must_be_qualified) { | |
4451 | return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), "flags", "+qualified,+loghostmask", NULL); | |
4452 | } else { | |
4453 | return modcmd_register(nickserv_module, name, func, 1, (must_auth ? MODCMD_REQUIRE_AUTHED : 0), NULL); | |
4454 | } | |
4455 | } | |
4456 | } | |
4457 | ||
4458 | static void | |
4459 | nickserv_db_cleanup(void) | |
4460 | { | |
4461 | unreg_del_user_func(nickserv_remove_user); | |
4462 | userList_clean(&curr_helpers); | |
4463 | policer_params_delete(nickserv_conf.auth_policer_params); | |
4464 | dict_delete(nickserv_handle_dict); | |
4465 | dict_delete(nickserv_nick_dict); | |
4466 | dict_delete(nickserv_opt_dict); | |
4467 | dict_delete(nickserv_allow_auth_dict); | |
4468 | dict_delete(nickserv_email_dict); | |
4469 | dict_delete(nickserv_id_dict); | |
4470 | dict_delete(nickserv_conf.weak_password_dict); | |
4471 | free(auth_func_list); | |
4472 | free(unreg_func_list); | |
4473 | free(rf_list); | |
4474 | free(allowauth_func_list); | |
4475 | free(handle_merge_func_list); | |
4476 | free(failpw_func_list); | |
4477 | if (nickserv_conf.valid_handle_regex_set) | |
4478 | regfree(&nickserv_conf.valid_handle_regex); | |
4479 | if (nickserv_conf.valid_nick_regex_set) | |
4480 | regfree(&nickserv_conf.valid_nick_regex); | |
4481 | } | |
4482 | ||
4483 | void | |
4484 | init_nickserv(const char *nick) | |
4485 | { | |
4486 | struct chanNode *chan; | |
4487 | unsigned int i; | |
4488 | NS_LOG = log_register_type("NickServ", "file:nickserv.log"); | |
4489 | reg_new_user_func(handle_new_user); | |
4490 | reg_nick_change_func(handle_nick_change); | |
4491 | reg_del_user_func(nickserv_remove_user); | |
4492 | reg_account_func(handle_account); | |
4493 | ||
4494 | /* set up handle_inverse_flags */ | |
4495 | memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags)); | |
4496 | for (i=0; handle_flags[i]; i++) { | |
4497 | handle_inverse_flags[(unsigned char)handle_flags[i]] = i + 1; | |
4498 | flag_access_levels[i] = 0; | |
4499 | } | |
4500 | ||
4501 | conf_register_reload(nickserv_conf_read); | |
4502 | nickserv_opt_dict = dict_new(); | |
4503 | nickserv_email_dict = dict_new(); | |
4504 | ||
4505 | dict_set_free_keys(nickserv_email_dict, free); | |
4506 | dict_set_free_data(nickserv_email_dict, nickserv_free_email_addr); | |
4507 | ||
4508 | nickserv_module = module_register("NickServ", NS_LOG, "nickserv.help", NULL); | |
4509 | /* Removed qualified_host as default requirement for AUTH, REGISTER, PASS, etc. nets | |
4510 | * can enable it per command using modcmd. (its a shitty default IMO, and now in 1.3 | |
4511 | * a big pain to disable since its nolonger in the config file. ) -Rubin | |
4512 | */ | |
4513 | modcmd_register(nickserv_module, "AUTH", cmd_auth, 2, MODCMD_KEEP_BOUND, "flags", "+loghostmask", NULL); | |
4514 | nickserv_define_func("ALLOWAUTH", cmd_allowauth, 0, 1, 0); | |
4515 | nickserv_define_func("REGISTER", cmd_register, -1, 0, 0); | |
4516 | nickserv_define_func("OREGISTER", cmd_oregister, 0, 1, 0); | |
4517 | nickserv_define_func("UNREGISTER", cmd_unregister, -1, 1, 0); | |
4518 | nickserv_define_func("OUNREGISTER", cmd_ounregister, 0, 1, 0); | |
4519 | nickserv_define_func("ADDMASK", cmd_addmask, -1, 1, 0); | |
4520 | nickserv_define_func("OADDMASK", cmd_oaddmask, 0, 1, 0); | |
4521 | nickserv_define_func("DELMASK", cmd_delmask, -1, 1, 0); | |
4522 | nickserv_define_func("ODELMASK", cmd_odelmask, 0, 1, 0); | |
4523 | nickserv_define_func("PASS", cmd_pass, -1, 1, 0); | |
4524 | nickserv_define_func("SET", cmd_set, -1, 1, 0); | |
4525 | nickserv_define_func("OSET", cmd_oset, 0, 1, 0); | |
4526 | nickserv_define_func("ACCOUNTINFO", cmd_handleinfo, -1, 0, 0); | |
4527 | nickserv_define_func("USERINFO", cmd_userinfo, -1, 1, 0); | |
4528 | nickserv_define_func("RENAME", cmd_rename_handle, -1, 1, 0); | |
4529 | nickserv_define_func("VACATION", cmd_vacation, -1, 1, 0); | |
4530 | nickserv_define_func("MERGE", cmd_merge, 0, 1, 0); | |
4531 | if (!nickserv_conf.disable_nicks) { | |
4532 | /* nick management commands */ | |
4533 | nickserv_define_func("REGNICK", cmd_regnick, -1, 1, 0); | |
4534 | nickserv_define_func("OREGNICK", cmd_oregnick, 0, 1, 0); | |
4535 | nickserv_define_func("UNREGNICK", cmd_unregnick, -1, 1, 0); | |
4536 | nickserv_define_func("OUNREGNICK", cmd_ounregnick, 0, 1, 0); | |
4537 | nickserv_define_func("NICKINFO", cmd_nickinfo, -1, 1, 0); | |
4538 | nickserv_define_func("RECLAIM", cmd_reclaim, -1, 1, 0); | |
4539 | } | |
4540 | if (nickserv_conf.email_enabled) { | |
4541 | nickserv_define_func("AUTHCOOKIE", cmd_authcookie, -1, 0, 0); | |
4542 | nickserv_define_func("RESETPASS", cmd_resetpass, -1, 0, 0); | |
4543 | nickserv_define_func("COOKIE", cmd_cookie, -1, 0, 0); | |
4544 | nickserv_define_func("DELCOOKIE", cmd_delcookie, -1, 1, 0); | |
4545 | nickserv_define_func("ODELCOOKIE", cmd_odelcookie, 0, 1, 0); | |
4546 | dict_insert(nickserv_opt_dict, "EMAIL", opt_email); | |
4547 | } | |
4548 | nickserv_define_func("GHOST", cmd_ghost, -1, 1, 0); | |
4549 | /* ignore commands */ | |
4550 | nickserv_define_func("ADDIGNORE", cmd_addignore, -1, 1, 0); | |
4551 | nickserv_define_func("OADDIGNORE", cmd_oaddignore, 0, 1, 0); | |
4552 | nickserv_define_func("DELIGNORE", cmd_delignore, -1, 1, 0); | |
4553 | nickserv_define_func("ODELIGNORE", cmd_odelignore, 0, 1, 0); | |
4554 | /* miscellaneous commands */ | |
4555 | nickserv_define_func("STATUS", cmd_status, -1, 0, 0); | |
4556 | nickserv_define_func("SEARCH", cmd_search, 100, 1, 0); | |
4557 | nickserv_define_func("SEARCH UNREGISTER", NULL, 800, 1, 0); | |
4558 | nickserv_define_func("MERGEDB", cmd_mergedb, 999, 1, 0); | |
4559 | nickserv_define_func("CHECKPASS", cmd_checkpass, 601, 1, 0); | |
4560 | /* other options */ | |
4561 | dict_insert(nickserv_opt_dict, "INFO", opt_info); | |
4562 | dict_insert(nickserv_opt_dict, "WIDTH", opt_width); | |
4563 | dict_insert(nickserv_opt_dict, "TABLEWIDTH", opt_tablewidth); | |
4564 | dict_insert(nickserv_opt_dict, "COLOR", opt_color); | |
4565 | dict_insert(nickserv_opt_dict, "PRIVMSG", opt_privmsg); | |
4566 | dict_insert(nickserv_opt_dict, "AUTOHIDE", opt_autohide); | |
4567 | dict_insert(nickserv_opt_dict, "STYLE", opt_style); | |
4568 | dict_insert(nickserv_opt_dict, "PASS", opt_password); | |
4569 | dict_insert(nickserv_opt_dict, "PASSWORD", opt_password); | |
4570 | dict_insert(nickserv_opt_dict, "FLAGS", opt_flags); | |
4571 | dict_insert(nickserv_opt_dict, "ACCESS", opt_level); | |
4572 | dict_insert(nickserv_opt_dict, "LEVEL", opt_level); | |
4573 | dict_insert(nickserv_opt_dict, "EPITHET", opt_epithet); | |
4574 | dict_insert(nickserv_opt_dict, "NOTE", opt_note); | |
4575 | if (nickserv_conf.titlehost_suffix) { | |
4576 | dict_insert(nickserv_opt_dict, "TITLE", opt_title); | |
4577 | dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost); | |
4578 | } | |
4579 | dict_insert(nickserv_opt_dict, "ANNOUNCEMENTS", opt_announcements); | |
4580 | dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins); | |
4581 | dict_insert(nickserv_opt_dict, "ADVANCED", opt_advanced); | |
4582 | dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language); | |
4583 | ||
4584 | nickserv_handle_dict = dict_new(); | |
4585 | dict_set_free_keys(nickserv_handle_dict, free); | |
4586 | dict_set_free_data(nickserv_handle_dict, free_handle_info); | |
4587 | ||
4588 | nickserv_id_dict = dict_new(); | |
4589 | dict_set_free_keys(nickserv_id_dict, free); | |
4590 | ||
4591 | nickserv_nick_dict = dict_new(); | |
4592 | dict_set_free_data(nickserv_nick_dict, free); | |
4593 | ||
4594 | nickserv_allow_auth_dict = dict_new(); | |
4595 | ||
4596 | userList_init(&curr_helpers); | |
4597 | ||
4598 | if (nick) { | |
4599 | const char *modes = conf_get_data("services/nickserv/modes", RECDB_QSTRING); | |
4600 | nickserv = AddService(nick, modes ? modes : NULL, "Nick Services", NULL); | |
4601 | nickserv_service = service_register(nickserv); | |
4602 | } | |
4603 | saxdb_register("NickServ", nickserv_saxdb_read, nickserv_saxdb_write); | |
4604 | reg_exit_func(nickserv_db_cleanup); | |
4605 | if(nickserv_conf.handle_expire_frequency) | |
4606 | timeq_add(now + nickserv_conf.handle_expire_frequency, expire_handles, NULL); | |
4607 | ||
4608 | if(autojoin_channels && nickserv) { | |
4609 | for (i = 0; i < autojoin_channels->used; i++) { | |
4610 | chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL); | |
4611 | AddChannelUser(nickserv, chan)->modes |= MODE_CHANOP; | |
4612 | } | |
4613 | } | |
4614 | ||
4615 | message_register_table(msgtab); | |
4616 | } |